Fix #6874 - Grid column resize
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @singleton
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux",
704                 "Roo.bootstrap",
705                 "Roo.bootstrap.dash");
706 /*
707  * Based on:
708  * Ext JS Library 1.1.1
709  * Copyright(c) 2006-2007, Ext JS, LLC.
710  *
711  * Originally Released Under LGPL - original licence link has changed is not relivant.
712  *
713  * Fork - LGPL
714  * <script type="text/javascript">
715  */
716
717 (function() {    
718     // wrappedn so fnCleanup is not in global scope...
719     if(Roo.isIE) {
720         function fnCleanUp() {
721             var p = Function.prototype;
722             delete p.createSequence;
723             delete p.defer;
724             delete p.createDelegate;
725             delete p.createCallback;
726             delete p.createInterceptor;
727
728             window.detachEvent("onunload", fnCleanUp);
729         }
730         window.attachEvent("onunload", fnCleanUp);
731     }
732 })();
733
734
735 /**
736  * @class Function
737  * These functions are available on every Function object (any JavaScript function).
738  */
739 Roo.apply(Function.prototype, {
740      /**
741      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
742      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
743      * Will create a function that is bound to those 2 args.
744      * @return {Function} The new function
745     */
746     createCallback : function(/*args...*/){
747         // make args available, in function below
748         var args = arguments;
749         var method = this;
750         return function() {
751             return method.apply(window, args);
752         };
753     },
754
755     /**
756      * Creates a delegate (callback) that sets the scope to obj.
757      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
758      * Will create a function that is automatically scoped to this.
759      * @param {Object} obj (optional) The object for which the scope is set
760      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
761      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
762      *                                             if a number the args are inserted at the specified position
763      * @return {Function} The new function
764      */
765     createDelegate : function(obj, args, appendArgs){
766         var method = this;
767         return function() {
768             var callArgs = args || arguments;
769             if(appendArgs === true){
770                 callArgs = Array.prototype.slice.call(arguments, 0);
771                 callArgs = callArgs.concat(args);
772             }else if(typeof appendArgs == "number"){
773                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
774                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
775                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776             }
777             return method.apply(obj || window, callArgs);
778         };
779     },
780
781     /**
782      * Calls this function after the number of millseconds specified.
783      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
784      * @param {Object} obj (optional) The object for which the scope is set
785      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
786      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
787      *                                             if a number the args are inserted at the specified position
788      * @return {Number} The timeout id that can be used with clearTimeout
789      */
790     defer : function(millis, obj, args, appendArgs){
791         var fn = this.createDelegate(obj, args, appendArgs);
792         if(millis){
793             return setTimeout(fn, millis);
794         }
795         fn();
796         return 0;
797     },
798     /**
799      * Create a combined function call sequence of the original function + the passed function.
800      * The resulting function returns the results of the original function.
801      * The passed fcn is called with the parameters of the original function
802      * @param {Function} fcn The function to sequence
803      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
804      * @return {Function} The new function
805      */
806     createSequence : function(fcn, scope){
807         if(typeof fcn != "function"){
808             return this;
809         }
810         var method = this;
811         return function() {
812             var retval = method.apply(this || window, arguments);
813             fcn.apply(scope || this || window, arguments);
814             return retval;
815         };
816     },
817
818     /**
819      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
820      * The resulting function returns the results of the original function.
821      * The passed fcn is called with the parameters of the original function.
822      * @addon
823      * @param {Function} fcn The function to call before the original
824      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
825      * @return {Function} The new function
826      */
827     createInterceptor : function(fcn, scope){
828         if(typeof fcn != "function"){
829             return this;
830         }
831         var method = this;
832         return function() {
833             fcn.target = this;
834             fcn.method = method;
835             if(fcn.apply(scope || this || window, arguments) === false){
836                 return;
837             }
838             return method.apply(this || window, arguments);
839         };
840     }
841 });
842 /*
843  * Based on:
844  * Ext JS Library 1.1.1
845  * Copyright(c) 2006-2007, Ext JS, LLC.
846  *
847  * Originally Released Under LGPL - original licence link has changed is not relivant.
848  *
849  * Fork - LGPL
850  * <script type="text/javascript">
851  */
852
853 Roo.applyIf(String, {
854     
855     /** @scope String */
856     
857     /**
858      * Escapes the passed string for ' and \
859      * @param {String} string The string to escape
860      * @return {String} The escaped string
861      * @static
862      */
863     escape : function(string) {
864         return string.replace(/('|\\)/g, "\\$1");
865     },
866
867     /**
868      * Pads the left side of a string with a specified character.  This is especially useful
869      * for normalizing number and date strings.  Example usage:
870      * <pre><code>
871 var s = String.leftPad('123', 5, '0');
872 // s now contains the string: '00123'
873 </code></pre>
874      * @param {String} string The original string
875      * @param {Number} size The total length of the output string
876      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
877      * @return {String} The padded string
878      * @static
879      */
880     leftPad : function (val, size, ch) {
881         var result = new String(val);
882         if(ch === null || ch === undefined || ch === '') {
883             ch = " ";
884         }
885         while (result.length < size) {
886             result = ch + result;
887         }
888         return result;
889     },
890
891     /**
892      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
893      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
894      * <pre><code>
895 var cls = 'my-class', text = 'Some text';
896 var s = String.format('<div class="{0}">{1}</div>', cls, text);
897 // s now contains the string: '<div class="my-class">Some text</div>'
898 </code></pre>
899      * @param {String} string The tokenized string to be formatted
900      * @param {String} value1 The value to replace token {0}
901      * @param {String} value2 Etc...
902      * @return {String} The formatted string
903      * @static
904      */
905     format : function(format){
906         var args = Array.prototype.slice.call(arguments, 1);
907         return format.replace(/\{(\d+)\}/g, function(m, i){
908             return Roo.util.Format.htmlEncode(args[i]);
909         });
910     }
911   
912     
913 });
914
915 /**
916  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
917  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
918  * they are already different, the first value passed in is returned.  Note that this method returns the new value
919  * but does not change the current string.
920  * <pre><code>
921 // alternate sort directions
922 sort = sort.toggle('ASC', 'DESC');
923
924 // instead of conditional logic:
925 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 </code></pre>
927  * @param {String} value The value to compare to the current string
928  * @param {String} other The new value to use if the string already equals the first value passed in
929  * @return {String} The new value
930  */
931  
932 String.prototype.toggle = function(value, other){
933     return this == value ? other : value;
934 };
935
936
937 /**
938   * Remove invalid unicode characters from a string 
939   *
940   * @return {String} The clean string
941   */
942 String.prototype.unicodeClean = function () {
943     return this.replace(/[\s\S]/g,
944         function(character) {
945             if (character.charCodeAt()< 256) {
946               return character;
947            }
948            try {
949                 encodeURIComponent(character);
950            } catch(e) { 
951               return '';
952            }
953            return character;
954         }
955     );
956 };
957   
958 /*
959  * Based on:
960  * Ext JS Library 1.1.1
961  * Copyright(c) 2006-2007, Ext JS, LLC.
962  *
963  * Originally Released Under LGPL - original licence link has changed is not relivant.
964  *
965  * Fork - LGPL
966  * <script type="text/javascript">
967  */
968
969  /**
970  * @class Number
971  */
972 Roo.applyIf(Number.prototype, {
973     /**
974      * Checks whether or not the current number is within a desired range.  If the number is already within the
975      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
976      * exceeded.  Note that this method returns the constrained value but does not change the current number.
977      * @param {Number} min The minimum number in the range
978      * @param {Number} max The maximum number in the range
979      * @return {Number} The constrained value if outside the range, otherwise the current value
980      */
981     constrain : function(min, max){
982         return Math.min(Math.max(this, min), max);
983     }
984 });/*
985  * Based on:
986  * Ext JS Library 1.1.1
987  * Copyright(c) 2006-2007, Ext JS, LLC.
988  *
989  * Originally Released Under LGPL - original licence link has changed is not relivant.
990  *
991  * Fork - LGPL
992  * <script type="text/javascript">
993  */
994  /**
995  * @class Array
996  */
997 Roo.applyIf(Array.prototype, {
998     /**
999      * 
1000      * Checks whether or not the specified object exists in the array.
1001      * @param {Object} o The object to check for
1002      * @return {Number} The index of o in the array (or -1 if it is not found)
1003      */
1004     indexOf : function(o){
1005        for (var i = 0, len = this.length; i < len; i++){
1006               if(this[i] == o) { return i; }
1007        }
1008            return -1;
1009     },
1010
1011     /**
1012      * Removes the specified object from the array.  If the object is not found nothing happens.
1013      * @param {Object} o The object to remove
1014      */
1015     remove : function(o){
1016        var index = this.indexOf(o);
1017        if(index != -1){
1018            this.splice(index, 1);
1019        }
1020     },
1021     /**
1022      * Map (JS 1.6 compatibility)
1023      * @param {Function} function  to call
1024      */
1025     map : function(fun )
1026     {
1027         var len = this.length >>> 0;
1028         if (typeof fun != "function") {
1029             throw new TypeError();
1030         }
1031         var res = new Array(len);
1032         var thisp = arguments[1];
1033         for (var i = 0; i < len; i++)
1034         {
1035             if (i in this) {
1036                 res[i] = fun.call(thisp, this[i], i, this);
1037             }
1038         }
1039
1040         return res;
1041     },
1042     /**
1043      * equals
1044      * @param {Array} o The array to compare to
1045      * @returns {Boolean} true if the same
1046      */
1047     equals : function(b)
1048     {
1049         // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1050         if (this === b) {
1051             return true;
1052          }
1053         if (b == null) {
1054             return false;
1055         }
1056         if (this.length !== b.length) {
1057             return false;
1058         }
1059       
1060         // sort?? a.sort().equals(b.sort());
1061       
1062         for (var i = 0; i < this.length; ++i) {
1063             if (this[i] !== b[i]) {
1064                 return false;
1065             }
1066         }
1067         return true;
1068     }
1069 });
1070
1071
1072  
1073 /*
1074  * Based on:
1075  * Ext JS Library 1.1.1
1076  * Copyright(c) 2006-2007, Ext JS, LLC.
1077  *
1078  * Originally Released Under LGPL - original licence link has changed is not relivant.
1079  *
1080  * Fork - LGPL
1081  * <script type="text/javascript">
1082  */
1083
1084 /**
1085  * @class Date
1086  *
1087  * The date parsing and format syntax is a subset of
1088  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1089  * supported will provide results equivalent to their PHP versions.
1090  *
1091  * Following is the list of all currently supported formats:
1092  *<pre>
1093 Sample date:
1094 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1095
1096 Format  Output      Description
1097 ------  ----------  --------------------------------------------------------------
1098   d      10         Day of the month, 2 digits with leading zeros
1099   D      Wed        A textual representation of a day, three letters
1100   j      10         Day of the month without leading zeros
1101   l      Wednesday  A full textual representation of the day of the week
1102   S      th         English ordinal day of month suffix, 2 chars (use with j)
1103   w      3          Numeric representation of the day of the week
1104   z      9          The julian date, or day of the year (0-365)
1105   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1106   F      January    A full textual representation of the month
1107   m      01         Numeric representation of a month, with leading zeros
1108   M      Jan        Month name abbreviation, three letters
1109   n      1          Numeric representation of a month, without leading zeros
1110   t      31         Number of days in the given month
1111   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1112   Y      2007       A full numeric representation of a year, 4 digits
1113   y      07         A two digit representation of a year
1114   a      pm         Lowercase Ante meridiem and Post meridiem
1115   A      PM         Uppercase Ante meridiem and Post meridiem
1116   g      3          12-hour format of an hour without leading zeros
1117   G      15         24-hour format of an hour without leading zeros
1118   h      03         12-hour format of an hour with leading zeros
1119   H      15         24-hour format of an hour with leading zeros
1120   i      05         Minutes with leading zeros
1121   s      01         Seconds, with leading zeros
1122   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1123   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1124   T      CST        Timezone setting of the machine running the code
1125   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1126 </pre>
1127  *
1128  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1129  * <pre><code>
1130 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1131 document.write(dt.format('Y-m-d'));                         //2007-01-10
1132 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1133 document.write(dt.format('l, \\t\\he dS of F Y h:i:s A'));  //Wednesday, the 10th of January 2007 03:05:01 PM
1134  </code></pre>
1135  *
1136  * Here are some standard date/time patterns that you might find helpful.  They
1137  * are not part of the source of Date.js, but to use them you can simply copy this
1138  * block of code into any script that is included after Date.js and they will also become
1139  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1140  * <pre><code>
1141 Date.patterns = {
1142     ISO8601Long:"Y-m-d H:i:s",
1143     ISO8601Short:"Y-m-d",
1144     ShortDate: "n/j/Y",
1145     LongDate: "l, F d, Y",
1146     FullDateTime: "l, F d, Y g:i:s A",
1147     MonthDay: "F d",
1148     ShortTime: "g:i A",
1149     LongTime: "g:i:s A",
1150     SortableDateTime: "Y-m-d\\TH:i:s",
1151     UniversalSortableDateTime: "Y-m-d H:i:sO",
1152     YearMonth: "F, Y"
1153 };
1154 </code></pre>
1155  *
1156  * Example usage:
1157  * <pre><code>
1158 var dt = new Date();
1159 document.write(dt.format(Date.patterns.ShortDate));
1160  </code></pre>
1161  */
1162
1163 /*
1164  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1165  * They generate precompiled functions from date formats instead of parsing and
1166  * processing the pattern every time you format a date.  These functions are available
1167  * on every Date object (any javascript function).
1168  *
1169  * The original article and download are here:
1170  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1171  *
1172  */
1173  
1174  
1175  // was in core
1176 /**
1177  Returns the number of milliseconds between this date and date
1178  @param {Date} date (optional) Defaults to now
1179  @return {Number} The diff in milliseconds
1180  @member Date getElapsed
1181  */
1182 Date.prototype.getElapsed = function(date) {
1183         return Math.abs((date || new Date()).getTime()-this.getTime());
1184 };
1185 // was in date file..
1186
1187
1188 // private
1189 Date.parseFunctions = {count:0};
1190 // private
1191 Date.parseRegexes = [];
1192 // private
1193 Date.formatFunctions = {count:0};
1194
1195 // private
1196 Date.prototype.dateFormat = function(format) {
1197     if (Date.formatFunctions[format] == null) {
1198         Date.createNewFormat(format);
1199     }
1200     var func = Date.formatFunctions[format];
1201     return this[func]();
1202 };
1203
1204
1205 /**
1206  * Formats a date given the supplied format string
1207  * @param {String} format The format string
1208  * @return {String} The formatted date
1209  * @method
1210  */
1211 Date.prototype.format = Date.prototype.dateFormat;
1212
1213 // private
1214 Date.createNewFormat = function(format) {
1215     var funcName = "format" + Date.formatFunctions.count++;
1216     Date.formatFunctions[format] = funcName;
1217     var code = "Date.prototype." + funcName + " = function(){return ";
1218     var special = false;
1219     var ch = '';
1220     for (var i = 0; i < format.length; ++i) {
1221         ch = format.charAt(i);
1222         if (!special && ch == "\\") {
1223             special = true;
1224         }
1225         else if (special) {
1226             special = false;
1227             code += "'" + String.escape(ch) + "' + ";
1228         }
1229         else {
1230             code += Date.getFormatCode(ch);
1231         }
1232     }
1233     /** eval:var:zzzzzzzzzzzzz */
1234     eval(code.substring(0, code.length - 3) + ";}");
1235 };
1236
1237 // private
1238 Date.getFormatCode = function(character) {
1239     switch (character) {
1240     case "d":
1241         return "String.leftPad(this.getDate(), 2, '0') + ";
1242     case "D":
1243         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1244     case "j":
1245         return "this.getDate() + ";
1246     case "l":
1247         return "Date.dayNames[this.getDay()] + ";
1248     case "S":
1249         return "this.getSuffix() + ";
1250     case "w":
1251         return "this.getDay() + ";
1252     case "z":
1253         return "this.getDayOfYear() + ";
1254     case "W":
1255         return "this.getWeekOfYear() + ";
1256     case "F":
1257         return "Date.monthNames[this.getMonth()] + ";
1258     case "m":
1259         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1260     case "M":
1261         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1262     case "n":
1263         return "(this.getMonth() + 1) + ";
1264     case "t":
1265         return "this.getDaysInMonth() + ";
1266     case "L":
1267         return "(this.isLeapYear() ? 1 : 0) + ";
1268     case "Y":
1269         return "this.getFullYear() + ";
1270     case "y":
1271         return "('' + this.getFullYear()).substring(2, 4) + ";
1272     case "a":
1273         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1274     case "A":
1275         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1276     case "g":
1277         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1278     case "G":
1279         return "this.getHours() + ";
1280     case "h":
1281         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1282     case "H":
1283         return "String.leftPad(this.getHours(), 2, '0') + ";
1284     case "i":
1285         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1286     case "s":
1287         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1288     case "O":
1289         return "this.getGMTOffset() + ";
1290     case "P":
1291         return "this.getGMTColonOffset() + ";
1292     case "T":
1293         return "this.getTimezone() + ";
1294     case "Z":
1295         return "(this.getTimezoneOffset() * -60) + ";
1296     default:
1297         return "'" + String.escape(character) + "' + ";
1298     }
1299 };
1300
1301 /**
1302  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1303  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1304  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1305  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1306  * string or the parse operation will fail.
1307  * Example Usage:
1308 <pre><code>
1309 //dt = Fri May 25 2007 (current date)
1310 var dt = new Date();
1311
1312 //dt = Thu May 25 2006 (today's month/day in 2006)
1313 dt = Date.parseDate("2006", "Y");
1314
1315 //dt = Sun Jan 15 2006 (all date parts specified)
1316 dt = Date.parseDate("2006-1-15", "Y-m-d");
1317
1318 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1319 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1320 </code></pre>
1321  * @param {String} input The unparsed date as a string
1322  * @param {String} format The format the date is in
1323  * @return {Date} The parsed date
1324  * @static
1325  */
1326 Date.parseDate = function(input, format) {
1327     if (Date.parseFunctions[format] == null) {
1328         Date.createParser(format);
1329     }
1330     var func = Date.parseFunctions[format];
1331     return Date[func](input);
1332 };
1333 /**
1334  * @private
1335  */
1336
1337 Date.createParser = function(format) {
1338     var funcName = "parse" + Date.parseFunctions.count++;
1339     var regexNum = Date.parseRegexes.length;
1340     var currentGroup = 1;
1341     Date.parseFunctions[format] = funcName;
1342
1343     var code = "Date." + funcName + " = function(input){\n"
1344         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1345         + "var d = new Date();\n"
1346         + "y = d.getFullYear();\n"
1347         + "m = d.getMonth();\n"
1348         + "d = d.getDate();\n"
1349         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1350         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1351         + "if (results && results.length > 0) {";
1352     var regex = "";
1353
1354     var special = false;
1355     var ch = '';
1356     for (var i = 0; i < format.length; ++i) {
1357         ch = format.charAt(i);
1358         if (!special && ch == "\\") {
1359             special = true;
1360         }
1361         else if (special) {
1362             special = false;
1363             regex += String.escape(ch);
1364         }
1365         else {
1366             var obj = Date.formatCodeToRegex(ch, currentGroup);
1367             currentGroup += obj.g;
1368             regex += obj.s;
1369             if (obj.g && obj.c) {
1370                 code += obj.c;
1371             }
1372         }
1373     }
1374
1375     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1376         + "{v = new Date(y, m, d, h, i, s);}\n"
1377         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1378         + "{v = new Date(y, m, d, h, i);}\n"
1379         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1380         + "{v = new Date(y, m, d, h);}\n"
1381         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1382         + "{v = new Date(y, m, d);}\n"
1383         + "else if (y >= 0 && m >= 0)\n"
1384         + "{v = new Date(y, m);}\n"
1385         + "else if (y >= 0)\n"
1386         + "{v = new Date(y);}\n"
1387         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1388         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1389         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1390         + ";}";
1391
1392     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1393     /** eval:var:zzzzzzzzzzzzz */
1394     eval(code);
1395 };
1396
1397 // private
1398 Date.formatCodeToRegex = function(character, currentGroup) {
1399     switch (character) {
1400     case "D":
1401         return {g:0,
1402         c:null,
1403         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1404     case "j":
1405         return {g:1,
1406             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1407             s:"(\\d{1,2})"}; // day of month without leading zeroes
1408     case "d":
1409         return {g:1,
1410             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1411             s:"(\\d{2})"}; // day of month with leading zeroes
1412     case "l":
1413         return {g:0,
1414             c:null,
1415             s:"(?:" + Date.dayNames.join("|") + ")"};
1416     case "S":
1417         return {g:0,
1418             c:null,
1419             s:"(?:st|nd|rd|th)"};
1420     case "w":
1421         return {g:0,
1422             c:null,
1423             s:"\\d"};
1424     case "z":
1425         return {g:0,
1426             c:null,
1427             s:"(?:\\d{1,3})"};
1428     case "W":
1429         return {g:0,
1430             c:null,
1431             s:"(?:\\d{2})"};
1432     case "F":
1433         return {g:1,
1434             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1435             s:"(" + Date.monthNames.join("|") + ")"};
1436     case "M":
1437         return {g:1,
1438             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1439             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1440     case "n":
1441         return {g:1,
1442             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1443             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1444     case "m":
1445         return {g:1,
1446             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1447             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1448     case "t":
1449         return {g:0,
1450             c:null,
1451             s:"\\d{1,2}"};
1452     case "L":
1453         return {g:0,
1454             c:null,
1455             s:"(?:1|0)"};
1456     case "Y":
1457         return {g:1,
1458             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1459             s:"(\\d{4})"};
1460     case "y":
1461         return {g:1,
1462             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1463                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1464             s:"(\\d{1,2})"};
1465     case "a":
1466         return {g:1,
1467             c:"if (results[" + currentGroup + "] == 'am') {\n"
1468                 + "if (h == 12) { h = 0; }\n"
1469                 + "} else { if (h < 12) { h += 12; }}",
1470             s:"(am|pm)"};
1471     case "A":
1472         return {g:1,
1473             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1474                 + "if (h == 12) { h = 0; }\n"
1475                 + "} else { if (h < 12) { h += 12; }}",
1476             s:"(AM|PM)"};
1477     case "g":
1478     case "G":
1479         return {g:1,
1480             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1481             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1482     case "h":
1483     case "H":
1484         return {g:1,
1485             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1486             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1487     case "i":
1488         return {g:1,
1489             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1490             s:"(\\d{2})"};
1491     case "s":
1492         return {g:1,
1493             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1494             s:"(\\d{2})"};
1495     case "O":
1496         return {g:1,
1497             c:[
1498                 "o = results[", currentGroup, "];\n",
1499                 "var sn = o.substring(0,1);\n", // get + / - sign
1500                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1501                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1502                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1503                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1504             ].join(""),
1505             s:"([+\-]\\d{2,4})"};
1506     
1507     
1508     case "P":
1509         return {g:1,
1510                 c:[
1511                    "o = results[", currentGroup, "];\n",
1512                    "var sn = o.substring(0,1);\n",
1513                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1514                    "var mn = o.substring(4,6) % 60;\n",
1515                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1516                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1517             ].join(""),
1518             s:"([+\-]\\d{4})"};
1519     case "T":
1520         return {g:0,
1521             c:null,
1522             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1523     case "Z":
1524         return {g:1,
1525             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1526                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1527             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1528     default:
1529         return {g:0,
1530             c:null,
1531             s:String.escape(character)};
1532     }
1533 };
1534
1535 /**
1536  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1537  * @return {String} The abbreviated timezone name (e.g. 'CST')
1538  */
1539 Date.prototype.getTimezone = function() {
1540     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1541 };
1542
1543 /**
1544  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1545  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1546  */
1547 Date.prototype.getGMTOffset = function() {
1548     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1549         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1550         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1551 };
1552
1553 /**
1554  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1555  * @return {String} 2-characters representing hours and 2-characters representing minutes
1556  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1557  */
1558 Date.prototype.getGMTColonOffset = function() {
1559         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1560                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1561                 + ":"
1562                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1563 }
1564
1565 /**
1566  * Get the numeric day number of the year, adjusted for leap year.
1567  * @return {Number} 0 through 364 (365 in leap years)
1568  */
1569 Date.prototype.getDayOfYear = function() {
1570     var num = 0;
1571     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1572     for (var i = 0; i < this.getMonth(); ++i) {
1573         num += Date.daysInMonth[i];
1574     }
1575     return num + this.getDate() - 1;
1576 };
1577
1578 /**
1579  * Get the string representation of the numeric week number of the year
1580  * (equivalent to the format specifier 'W').
1581  * @return {String} '00' through '52'
1582  */
1583 Date.prototype.getWeekOfYear = function() {
1584     // Skip to Thursday of this week
1585     var now = this.getDayOfYear() + (4 - this.getDay());
1586     // Find the first Thursday of the year
1587     var jan1 = new Date(this.getFullYear(), 0, 1);
1588     var then = (7 - jan1.getDay() + 4);
1589     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1590 };
1591
1592 /**
1593  * Whether or not the current date is in a leap year.
1594  * @return {Boolean} True if the current date is in a leap year, else false
1595  */
1596 Date.prototype.isLeapYear = function() {
1597     var year = this.getFullYear();
1598     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1599 };
1600
1601 /**
1602  * Get the first day of the current month, adjusted for leap year.  The returned value
1603  * is the numeric day index within the week (0-6) which can be used in conjunction with
1604  * the {@link #monthNames} array to retrieve the textual day name.
1605  * Example:
1606  *<pre><code>
1607 var dt = new Date('1/10/2007');
1608 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1609 </code></pre>
1610  * @return {Number} The day number (0-6)
1611  */
1612 Date.prototype.getFirstDayOfMonth = function() {
1613     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1614     return (day < 0) ? (day + 7) : day;
1615 };
1616
1617 /**
1618  * Get the last day of the current month, adjusted for leap year.  The returned value
1619  * is the numeric day index within the week (0-6) which can be used in conjunction with
1620  * the {@link #monthNames} array to retrieve the textual day name.
1621  * Example:
1622  *<pre><code>
1623 var dt = new Date('1/10/2007');
1624 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1625 </code></pre>
1626  * @return {Number} The day number (0-6)
1627  */
1628 Date.prototype.getLastDayOfMonth = function() {
1629     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1630     return (day < 0) ? (day + 7) : day;
1631 };
1632
1633
1634 /**
1635  * Get the first date of this date's month
1636  * @return {Date}
1637  */
1638 Date.prototype.getFirstDateOfMonth = function() {
1639     return new Date(this.getFullYear(), this.getMonth(), 1);
1640 };
1641
1642 /**
1643  * Get the last date of this date's month
1644  * @return {Date}
1645  */
1646 Date.prototype.getLastDateOfMonth = function() {
1647     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1648 };
1649 /**
1650  * Get the number of days in the current month, adjusted for leap year.
1651  * @return {Number} The number of days in the month
1652  */
1653 Date.prototype.getDaysInMonth = function() {
1654     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1655     return Date.daysInMonth[this.getMonth()];
1656 };
1657
1658 /**
1659  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1660  * @return {String} 'st, 'nd', 'rd' or 'th'
1661  */
1662 Date.prototype.getSuffix = function() {
1663     switch (this.getDate()) {
1664         case 1:
1665         case 21:
1666         case 31:
1667             return "st";
1668         case 2:
1669         case 22:
1670             return "nd";
1671         case 3:
1672         case 23:
1673             return "rd";
1674         default:
1675             return "th";
1676     }
1677 };
1678
1679 // private
1680 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1681
1682 /**
1683  * An array of textual month names.
1684  * Override these values for international dates, for example...
1685  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1686  * @type Array
1687  * @static
1688  */
1689 Date.monthNames =
1690    ["January",
1691     "February",
1692     "March",
1693     "April",
1694     "May",
1695     "June",
1696     "July",
1697     "August",
1698     "September",
1699     "October",
1700     "November",
1701     "December"];
1702
1703 /**
1704  * An array of textual day names.
1705  * Override these values for international dates, for example...
1706  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1707  * @type Array
1708  * @static
1709  */
1710 Date.dayNames =
1711    ["Sunday",
1712     "Monday",
1713     "Tuesday",
1714     "Wednesday",
1715     "Thursday",
1716     "Friday",
1717     "Saturday"];
1718
1719 // private
1720 Date.y2kYear = 50;
1721 // private
1722 Date.monthNumbers = {
1723     Jan:0,
1724     Feb:1,
1725     Mar:2,
1726     Apr:3,
1727     May:4,
1728     Jun:5,
1729     Jul:6,
1730     Aug:7,
1731     Sep:8,
1732     Oct:9,
1733     Nov:10,
1734     Dec:11};
1735
1736 /**
1737  * Creates and returns a new Date instance with the exact same date value as the called instance.
1738  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1739  * variable will also be changed.  When the intention is to create a new variable that will not
1740  * modify the original instance, you should create a clone.
1741  *
1742  * Example of correctly cloning a date:
1743  * <pre><code>
1744 //wrong way:
1745 var orig = new Date('10/1/2006');
1746 var copy = orig;
1747 copy.setDate(5);
1748 document.write(orig);  //returns 'Thu Oct 05 2006'!
1749
1750 //correct way:
1751 var orig = new Date('10/1/2006');
1752 var copy = orig.clone();
1753 copy.setDate(5);
1754 document.write(orig);  //returns 'Thu Oct 01 2006'
1755 </code></pre>
1756  * @return {Date} The new Date instance
1757  */
1758 Date.prototype.clone = function() {
1759         return new Date(this.getTime());
1760 };
1761
1762 /**
1763  * Clears any time information from this date
1764  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1765  @return {Date} this or the clone
1766  */
1767 Date.prototype.clearTime = function(clone){
1768     if(clone){
1769         return this.clone().clearTime();
1770     }
1771     this.setHours(0);
1772     this.setMinutes(0);
1773     this.setSeconds(0);
1774     this.setMilliseconds(0);
1775     return this;
1776 };
1777
1778 // private
1779 // safari setMonth is broken -- check that this is only donw once...
1780 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1781     Date.brokenSetMonth = Date.prototype.setMonth;
1782         Date.prototype.setMonth = function(num){
1783                 if(num <= -1){
1784                         var n = Math.ceil(-num);
1785                         var back_year = Math.ceil(n/12);
1786                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1787                         this.setFullYear(this.getFullYear() - back_year);
1788                         return Date.brokenSetMonth.call(this, month);
1789                 } else {
1790                         return Date.brokenSetMonth.apply(this, arguments);
1791                 }
1792         };
1793 }
1794
1795 /** Date interval constant 
1796 * @static 
1797 * @type String */
1798 Date.MILLI = "ms";
1799 /** Date interval constant 
1800 * @static 
1801 * @type String */
1802 Date.SECOND = "s";
1803 /** Date interval constant 
1804 * @static 
1805 * @type String */
1806 Date.MINUTE = "mi";
1807 /** Date interval constant 
1808 * @static 
1809 * @type String */
1810 Date.HOUR = "h";
1811 /** Date interval constant 
1812 * @static 
1813 * @type String */
1814 Date.DAY = "d";
1815 /** Date interval constant 
1816 * @static 
1817 * @type String */
1818 Date.MONTH = "mo";
1819 /** Date interval constant 
1820 * @static 
1821 * @type String */
1822 Date.YEAR = "y";
1823
1824 /**
1825  * Provides a convenient method of performing basic date arithmetic.  This method
1826  * does not modify the Date instance being called - it creates and returns
1827  * a new Date instance containing the resulting date value.
1828  *
1829  * Examples:
1830  * <pre><code>
1831 //Basic usage:
1832 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1833 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1834
1835 //Negative values will subtract correctly:
1836 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1837 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1838
1839 //You can even chain several calls together in one line!
1840 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1841 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1842  </code></pre>
1843  *
1844  * @param {String} interval   A valid date interval enum value
1845  * @param {Number} value      The amount to add to the current date
1846  * @return {Date} The new Date instance
1847  */
1848 Date.prototype.add = function(interval, value){
1849   var d = this.clone();
1850   if (!interval || value === 0) { return d; }
1851   switch(interval.toLowerCase()){
1852     case Date.MILLI:
1853       d.setMilliseconds(this.getMilliseconds() + value);
1854       break;
1855     case Date.SECOND:
1856       d.setSeconds(this.getSeconds() + value);
1857       break;
1858     case Date.MINUTE:
1859       d.setMinutes(this.getMinutes() + value);
1860       break;
1861     case Date.HOUR:
1862       d.setHours(this.getHours() + value);
1863       break;
1864     case Date.DAY:
1865       d.setDate(this.getDate() + value);
1866       break;
1867     case Date.MONTH:
1868       var day = this.getDate();
1869       if(day > 28){
1870           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1871       }
1872       d.setDate(day);
1873       d.setMonth(this.getMonth() + value);
1874       break;
1875     case Date.YEAR:
1876       d.setFullYear(this.getFullYear() + value);
1877       break;
1878   }
1879   return d;
1880 };
1881 /*
1882  * Based on:
1883  * Ext JS Library 1.1.1
1884  * Copyright(c) 2006-2007, Ext JS, LLC.
1885  *
1886  * Originally Released Under LGPL - original licence link has changed is not relivant.
1887  *
1888  * Fork - LGPL
1889  * <script type="text/javascript">
1890  */
1891
1892 /**
1893  * @class Roo.lib.Dom
1894  * @static
1895  * 
1896  * Dom utils (from YIU afaik)
1897  * 
1898  **/
1899 Roo.lib.Dom = {
1900     /**
1901      * Get the view width
1902      * @param {Boolean} full True will get the full document, otherwise it's the view width
1903      * @return {Number} The width
1904      */
1905      
1906     getViewWidth : function(full) {
1907         return full ? this.getDocumentWidth() : this.getViewportWidth();
1908     },
1909     /**
1910      * Get the view height
1911      * @param {Boolean} full True will get the full document, otherwise it's the view height
1912      * @return {Number} The height
1913      */
1914     getViewHeight : function(full) {
1915         return full ? this.getDocumentHeight() : this.getViewportHeight();
1916     },
1917
1918     getDocumentHeight: function() {
1919         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1920         return Math.max(scrollHeight, this.getViewportHeight());
1921     },
1922
1923     getDocumentWidth: function() {
1924         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1925         return Math.max(scrollWidth, this.getViewportWidth());
1926     },
1927
1928     getViewportHeight: function() {
1929         var height = self.innerHeight;
1930         var mode = document.compatMode;
1931
1932         if ((mode || Roo.isIE) && !Roo.isOpera) {
1933             height = (mode == "CSS1Compat") ?
1934                      document.documentElement.clientHeight :
1935                      document.body.clientHeight;
1936         }
1937
1938         return height;
1939     },
1940
1941     getViewportWidth: function() {
1942         var width = self.innerWidth;
1943         var mode = document.compatMode;
1944
1945         if (mode || Roo.isIE) {
1946             width = (mode == "CSS1Compat") ?
1947                     document.documentElement.clientWidth :
1948                     document.body.clientWidth;
1949         }
1950         return width;
1951     },
1952
1953     isAncestor : function(p, c) {
1954         p = Roo.getDom(p);
1955         c = Roo.getDom(c);
1956         if (!p || !c) {
1957             return false;
1958         }
1959
1960         if (p.contains && !Roo.isSafari) {
1961             return p.contains(c);
1962         } else if (p.compareDocumentPosition) {
1963             return !!(p.compareDocumentPosition(c) & 16);
1964         } else {
1965             var parent = c.parentNode;
1966             while (parent) {
1967                 if (parent == p) {
1968                     return true;
1969                 }
1970                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1971                     return false;
1972                 }
1973                 parent = parent.parentNode;
1974             }
1975             return false;
1976         }
1977     },
1978
1979     getRegion : function(el) {
1980         return Roo.lib.Region.getRegion(el);
1981     },
1982
1983     getY : function(el) {
1984         return this.getXY(el)[1];
1985     },
1986
1987     getX : function(el) {
1988         return this.getXY(el)[0];
1989     },
1990
1991     getXY : function(el) {
1992         var p, pe, b, scroll, bd = document.body;
1993         el = Roo.getDom(el);
1994         var fly = Roo.lib.AnimBase.fly;
1995         if (el.getBoundingClientRect) {
1996             b = el.getBoundingClientRect();
1997             scroll = fly(document).getScroll();
1998             return [b.left + scroll.left, b.top + scroll.top];
1999         }
2000         var x = 0, y = 0;
2001
2002         p = el;
2003
2004         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2005
2006         while (p) {
2007
2008             x += p.offsetLeft;
2009             y += p.offsetTop;
2010
2011             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2012                 hasAbsolute = true;
2013             }
2014
2015             if (Roo.isGecko) {
2016                 pe = fly(p);
2017
2018                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2019                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2020
2021
2022                 x += bl;
2023                 y += bt;
2024
2025
2026                 if (p != el && pe.getStyle('overflow') != 'visible') {
2027                     x += bl;
2028                     y += bt;
2029                 }
2030             }
2031             p = p.offsetParent;
2032         }
2033
2034         if (Roo.isSafari && hasAbsolute) {
2035             x -= bd.offsetLeft;
2036             y -= bd.offsetTop;
2037         }
2038
2039         if (Roo.isGecko && !hasAbsolute) {
2040             var dbd = fly(bd);
2041             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2042             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2043         }
2044
2045         p = el.parentNode;
2046         while (p && p != bd) {
2047             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2048                 x -= p.scrollLeft;
2049                 y -= p.scrollTop;
2050             }
2051             p = p.parentNode;
2052         }
2053         return [x, y];
2054     },
2055  
2056   
2057
2058
2059     setXY : function(el, xy) {
2060         el = Roo.fly(el, '_setXY');
2061         el.position();
2062         var pts = el.translatePoints(xy);
2063         if (xy[0] !== false) {
2064             el.dom.style.left = pts.left + "px";
2065         }
2066         if (xy[1] !== false) {
2067             el.dom.style.top = pts.top + "px";
2068         }
2069     },
2070
2071     setX : function(el, x) {
2072         this.setXY(el, [x, false]);
2073     },
2074
2075     setY : function(el, y) {
2076         this.setXY(el, [false, y]);
2077     }
2078 };
2079 /*
2080  * Portions of this file are based on pieces of Yahoo User Interface Library
2081  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2082  * YUI licensed under the BSD License:
2083  * http://developer.yahoo.net/yui/license.txt
2084  * <script type="text/javascript">
2085  *
2086  */
2087
2088 Roo.lib.Event = function() {
2089     var loadComplete = false;
2090     var listeners = [];
2091     var unloadListeners = [];
2092     var retryCount = 0;
2093     var onAvailStack = [];
2094     var counter = 0;
2095     var lastError = null;
2096
2097     return {
2098         POLL_RETRYS: 200,
2099         POLL_INTERVAL: 20,
2100         EL: 0,
2101         TYPE: 1,
2102         FN: 2,
2103         WFN: 3,
2104         OBJ: 3,
2105         ADJ_SCOPE: 4,
2106         _interval: null,
2107
2108         startInterval: function() {
2109             if (!this._interval) {
2110                 var self = this;
2111                 var callback = function() {
2112                     self._tryPreloadAttach();
2113                 };
2114                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2115
2116             }
2117         },
2118
2119         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2120             onAvailStack.push({ id:         p_id,
2121                 fn:         p_fn,
2122                 obj:        p_obj,
2123                 override:   p_override,
2124                 checkReady: false    });
2125
2126             retryCount = this.POLL_RETRYS;
2127             this.startInterval();
2128         },
2129
2130
2131         addListener: function(el, eventName, fn) {
2132             el = Roo.getDom(el);
2133             if (!el || !fn) {
2134                 return false;
2135             }
2136
2137             if ("unload" == eventName) {
2138                 unloadListeners[unloadListeners.length] =
2139                 [el, eventName, fn];
2140                 return true;
2141             }
2142
2143             var wrappedFn = function(e) {
2144                 return fn(Roo.lib.Event.getEvent(e));
2145             };
2146
2147             var li = [el, eventName, fn, wrappedFn];
2148
2149             var index = listeners.length;
2150             listeners[index] = li;
2151
2152             this.doAdd(el, eventName, wrappedFn, false);
2153             return true;
2154
2155         },
2156
2157
2158         removeListener: function(el, eventName, fn) {
2159             var i, len;
2160
2161             el = Roo.getDom(el);
2162
2163             if(!fn) {
2164                 return this.purgeElement(el, false, eventName);
2165             }
2166
2167
2168             if ("unload" == eventName) {
2169
2170                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2171                     var li = unloadListeners[i];
2172                     if (li &&
2173                         li[0] == el &&
2174                         li[1] == eventName &&
2175                         li[2] == fn) {
2176                         unloadListeners.splice(i, 1);
2177                         return true;
2178                     }
2179                 }
2180
2181                 return false;
2182             }
2183
2184             var cacheItem = null;
2185
2186
2187             var index = arguments[3];
2188
2189             if ("undefined" == typeof index) {
2190                 index = this._getCacheIndex(el, eventName, fn);
2191             }
2192
2193             if (index >= 0) {
2194                 cacheItem = listeners[index];
2195             }
2196
2197             if (!el || !cacheItem) {
2198                 return false;
2199             }
2200
2201             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2202
2203             delete listeners[index][this.WFN];
2204             delete listeners[index][this.FN];
2205             listeners.splice(index, 1);
2206
2207             return true;
2208
2209         },
2210
2211
2212         getTarget: function(ev, resolveTextNode) {
2213             ev = ev.browserEvent || ev;
2214             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2215             var t = ev.target || ev.srcElement;
2216             return this.resolveTextNode(t);
2217         },
2218
2219
2220         resolveTextNode: function(node) {
2221             if (Roo.isSafari && node && 3 == node.nodeType) {
2222                 return node.parentNode;
2223             } else {
2224                 return node;
2225             }
2226         },
2227
2228
2229         getPageX: function(ev) {
2230             ev = ev.browserEvent || ev;
2231             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2232             var x = ev.pageX;
2233             if (!x && 0 !== x) {
2234                 x = ev.clientX || 0;
2235
2236                 if (Roo.isIE) {
2237                     x += this.getScroll()[1];
2238                 }
2239             }
2240
2241             return x;
2242         },
2243
2244
2245         getPageY: function(ev) {
2246             ev = ev.browserEvent || ev;
2247             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2248             var y = ev.pageY;
2249             if (!y && 0 !== y) {
2250                 y = ev.clientY || 0;
2251
2252                 if (Roo.isIE) {
2253                     y += this.getScroll()[0];
2254                 }
2255             }
2256
2257
2258             return y;
2259         },
2260
2261
2262         getXY: function(ev) {
2263             ev = ev.browserEvent || ev;
2264             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2265             return [this.getPageX(ev), this.getPageY(ev)];
2266         },
2267
2268
2269         getRelatedTarget: function(ev) {
2270             ev = ev.browserEvent || ev;
2271             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2272             var t = ev.relatedTarget;
2273             if (!t) {
2274                 if (ev.type == "mouseout") {
2275                     t = ev.toElement;
2276                 } else if (ev.type == "mouseover") {
2277                     t = ev.fromElement;
2278                 }
2279             }
2280
2281             return this.resolveTextNode(t);
2282         },
2283
2284
2285         getTime: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2288             if (!ev.time) {
2289                 var t = new Date().getTime();
2290                 try {
2291                     ev.time = t;
2292                 } catch(ex) {
2293                     this.lastError = ex;
2294                     return t;
2295                 }
2296             }
2297
2298             return ev.time;
2299         },
2300
2301
2302         stopEvent: function(ev) {
2303             this.stopPropagation(ev);
2304             this.preventDefault(ev);
2305         },
2306
2307
2308         stopPropagation: function(ev) {
2309             ev = ev.browserEvent || ev;
2310             if (ev.stopPropagation) {
2311                 ev.stopPropagation();
2312             } else {
2313                 ev.cancelBubble = true;
2314             }
2315         },
2316
2317
2318         preventDefault: function(ev) {
2319             ev = ev.browserEvent || ev;
2320             if(ev.preventDefault) {
2321                 ev.preventDefault();
2322             } else {
2323                 ev.returnValue = false;
2324             }
2325         },
2326
2327
2328         getEvent: function(e) {
2329             var ev = e || window.event;
2330             if (!ev) {
2331                 var c = this.getEvent.caller;
2332                 while (c) {
2333                     ev = c.arguments[0];
2334                     if (ev && Event == ev.constructor) {
2335                         break;
2336                     }
2337                     c = c.caller;
2338                 }
2339             }
2340             return ev;
2341         },
2342
2343
2344         getCharCode: function(ev) {
2345             ev = ev.browserEvent || ev;
2346             return ev.charCode || ev.keyCode || 0;
2347         },
2348
2349
2350         _getCacheIndex: function(el, eventName, fn) {
2351             for (var i = 0,len = listeners.length; i < len; ++i) {
2352                 var li = listeners[i];
2353                 if (li &&
2354                     li[this.FN] == fn &&
2355                     li[this.EL] == el &&
2356                     li[this.TYPE] == eventName) {
2357                     return i;
2358                 }
2359             }
2360
2361             return -1;
2362         },
2363
2364
2365         elCache: {},
2366
2367
2368         getEl: function(id) {
2369             return document.getElementById(id);
2370         },
2371
2372
2373         clearCache: function() {
2374         },
2375
2376
2377         _load: function(e) {
2378             loadComplete = true;
2379             var EU = Roo.lib.Event;
2380
2381
2382             if (Roo.isIE) {
2383                 EU.doRemove(window, "load", EU._load);
2384             }
2385         },
2386
2387
2388         _tryPreloadAttach: function() {
2389
2390             if (this.locked) {
2391                 return false;
2392             }
2393
2394             this.locked = true;
2395
2396
2397             var tryAgain = !loadComplete;
2398             if (!tryAgain) {
2399                 tryAgain = (retryCount > 0);
2400             }
2401
2402
2403             var notAvail = [];
2404             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2405                 var item = onAvailStack[i];
2406                 if (item) {
2407                     var el = this.getEl(item.id);
2408
2409                     if (el) {
2410                         if (!item.checkReady ||
2411                             loadComplete ||
2412                             el.nextSibling ||
2413                             (document && document.body)) {
2414
2415                             var scope = el;
2416                             if (item.override) {
2417                                 if (item.override === true) {
2418                                     scope = item.obj;
2419                                 } else {
2420                                     scope = item.override;
2421                                 }
2422                             }
2423                             item.fn.call(scope, item.obj);
2424                             onAvailStack[i] = null;
2425                         }
2426                     } else {
2427                         notAvail.push(item);
2428                     }
2429                 }
2430             }
2431
2432             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2433
2434             if (tryAgain) {
2435
2436                 this.startInterval();
2437             } else {
2438                 clearInterval(this._interval);
2439                 this._interval = null;
2440             }
2441
2442             this.locked = false;
2443
2444             return true;
2445
2446         },
2447
2448
2449         purgeElement: function(el, recurse, eventName) {
2450             var elListeners = this.getListeners(el, eventName);
2451             if (elListeners) {
2452                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2453                     var l = elListeners[i];
2454                     this.removeListener(el, l.type, l.fn);
2455                 }
2456             }
2457
2458             if (recurse && el && el.childNodes) {
2459                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2460                     this.purgeElement(el.childNodes[i], recurse, eventName);
2461                 }
2462             }
2463         },
2464
2465
2466         getListeners: function(el, eventName) {
2467             var results = [], searchLists;
2468             if (!eventName) {
2469                 searchLists = [listeners, unloadListeners];
2470             } else if (eventName == "unload") {
2471                 searchLists = [unloadListeners];
2472             } else {
2473                 searchLists = [listeners];
2474             }
2475
2476             for (var j = 0; j < searchLists.length; ++j) {
2477                 var searchList = searchLists[j];
2478                 if (searchList && searchList.length > 0) {
2479                     for (var i = 0,len = searchList.length; i < len; ++i) {
2480                         var l = searchList[i];
2481                         if (l && l[this.EL] === el &&
2482                             (!eventName || eventName === l[this.TYPE])) {
2483                             results.push({
2484                                 type:   l[this.TYPE],
2485                                 fn:     l[this.FN],
2486                                 obj:    l[this.OBJ],
2487                                 adjust: l[this.ADJ_SCOPE],
2488                                 index:  i
2489                             });
2490                         }
2491                     }
2492                 }
2493             }
2494
2495             return (results.length) ? results : null;
2496         },
2497
2498
2499         _unload: function(e) {
2500
2501             var EU = Roo.lib.Event, i, j, l, len, index;
2502
2503             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2504                 l = unloadListeners[i];
2505                 if (l) {
2506                     var scope = window;
2507                     if (l[EU.ADJ_SCOPE]) {
2508                         if (l[EU.ADJ_SCOPE] === true) {
2509                             scope = l[EU.OBJ];
2510                         } else {
2511                             scope = l[EU.ADJ_SCOPE];
2512                         }
2513                     }
2514                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2515                     unloadListeners[i] = null;
2516                     l = null;
2517                     scope = null;
2518                 }
2519             }
2520
2521             unloadListeners = null;
2522
2523             if (listeners && listeners.length > 0) {
2524                 j = listeners.length;
2525                 while (j) {
2526                     index = j - 1;
2527                     l = listeners[index];
2528                     if (l) {
2529                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2530                                 l[EU.FN], index);
2531                     }
2532                     j = j - 1;
2533                 }
2534                 l = null;
2535
2536                 EU.clearCache();
2537             }
2538
2539             EU.doRemove(window, "unload", EU._unload);
2540
2541         },
2542
2543
2544         getScroll: function() {
2545             var dd = document.documentElement, db = document.body;
2546             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2547                 return [dd.scrollTop, dd.scrollLeft];
2548             } else if (db) {
2549                 return [db.scrollTop, db.scrollLeft];
2550             } else {
2551                 return [0, 0];
2552             }
2553         },
2554
2555
2556         doAdd: function () {
2557             if (window.addEventListener) {
2558                 return function(el, eventName, fn, capture) {
2559                     el.addEventListener(eventName, fn, (capture));
2560                 };
2561             } else if (window.attachEvent) {
2562                 return function(el, eventName, fn, capture) {
2563                     el.attachEvent("on" + eventName, fn);
2564                 };
2565             } else {
2566                 return function() {
2567                 };
2568             }
2569         }(),
2570
2571
2572         doRemove: function() {
2573             if (window.removeEventListener) {
2574                 return function (el, eventName, fn, capture) {
2575                     el.removeEventListener(eventName, fn, (capture));
2576                 };
2577             } else if (window.detachEvent) {
2578                 return function (el, eventName, fn) {
2579                     el.detachEvent("on" + eventName, fn);
2580                 };
2581             } else {
2582                 return function() {
2583                 };
2584             }
2585         }()
2586     };
2587     
2588 }();
2589 (function() {     
2590    
2591     var E = Roo.lib.Event;
2592     E.on = E.addListener;
2593     E.un = E.removeListener;
2594
2595     if (document && document.body) {
2596         E._load();
2597     } else {
2598         E.doAdd(window, "load", E._load);
2599     }
2600     E.doAdd(window, "unload", E._unload);
2601     E._tryPreloadAttach();
2602 })();
2603
2604 /*
2605  * Portions of this file are based on pieces of Yahoo User Interface Library
2606  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2607  * YUI licensed under the BSD License:
2608  * http://developer.yahoo.net/yui/license.txt
2609  * <script type="text/javascript">
2610  *
2611  */
2612
2613 (function() {
2614     /**
2615      * @class Roo.lib.Ajax
2616      *
2617      */
2618     Roo.lib.Ajax = {
2619         /**
2620          * @static 
2621          */
2622         request : function(method, uri, cb, data, options) {
2623             if(options){
2624                 var hs = options.headers;
2625                 if(hs){
2626                     for(var h in hs){
2627                         if(hs.hasOwnProperty(h)){
2628                             this.initHeader(h, hs[h], false);
2629                         }
2630                     }
2631                 }
2632                 if(options.xmlData){
2633                     this.initHeader('Content-Type', 'text/xml', false);
2634                     method = 'POST';
2635                     data = options.xmlData;
2636                 }
2637             }
2638
2639             return this.asyncRequest(method, uri, cb, data);
2640         },
2641
2642         serializeForm : function(form) {
2643             if(typeof form == 'string') {
2644                 form = (document.getElementById(form) || document.forms[form]);
2645             }
2646
2647             var el, name, val, disabled, data = '', hasSubmit = false;
2648             for (var i = 0; i < form.elements.length; i++) {
2649                 el = form.elements[i];
2650                 disabled = form.elements[i].disabled;
2651                 name = form.elements[i].name;
2652                 val = form.elements[i].value;
2653
2654                 if (!disabled && name){
2655                     switch (el.type)
2656                             {
2657                         case 'select-one':
2658                         case 'select-multiple':
2659                             for (var j = 0; j < el.options.length; j++) {
2660                                 if (el.options[j].selected) {
2661                                     if (Roo.isIE) {
2662                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2663                                     }
2664                                     else {
2665                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2666                                     }
2667                                 }
2668                             }
2669                             break;
2670                         case 'radio':
2671                         case 'checkbox':
2672                             if (el.checked) {
2673                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2674                             }
2675                             break;
2676                         case 'file':
2677
2678                         case undefined:
2679
2680                         case 'reset':
2681
2682                         case 'button':
2683
2684                             break;
2685                         case 'submit':
2686                             if(hasSubmit == false) {
2687                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2688                                 hasSubmit = true;
2689                             }
2690                             break;
2691                         default:
2692                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2693                             break;
2694                     }
2695                 }
2696             }
2697             data = data.substr(0, data.length - 1);
2698             return data;
2699         },
2700
2701         headers:{},
2702
2703         hasHeaders:false,
2704
2705         useDefaultHeader:true,
2706
2707         defaultPostHeader:'application/x-www-form-urlencoded',
2708
2709         useDefaultXhrHeader:true,
2710
2711         defaultXhrHeader:'XMLHttpRequest',
2712
2713         hasDefaultHeaders:true,
2714
2715         defaultHeaders:{},
2716
2717         poll:{},
2718
2719         timeout:{},
2720
2721         pollInterval:50,
2722
2723         transactionId:0,
2724
2725         setProgId:function(id)
2726         {
2727             this.activeX.unshift(id);
2728         },
2729
2730         setDefaultPostHeader:function(b)
2731         {
2732             this.useDefaultHeader = b;
2733         },
2734
2735         setDefaultXhrHeader:function(b)
2736         {
2737             this.useDefaultXhrHeader = b;
2738         },
2739
2740         setPollingInterval:function(i)
2741         {
2742             if (typeof i == 'number' && isFinite(i)) {
2743                 this.pollInterval = i;
2744             }
2745         },
2746
2747         createXhrObject:function(transactionId)
2748         {
2749             var obj,http;
2750             try
2751             {
2752
2753                 http = new XMLHttpRequest();
2754
2755                 obj = { conn:http, tId:transactionId };
2756             }
2757             catch(e)
2758             {
2759                 for (var i = 0; i < this.activeX.length; ++i) {
2760                     try
2761                     {
2762
2763                         http = new ActiveXObject(this.activeX[i]);
2764
2765                         obj = { conn:http, tId:transactionId };
2766                         break;
2767                     }
2768                     catch(e) {
2769                     }
2770                 }
2771             }
2772             finally
2773             {
2774                 return obj;
2775             }
2776         },
2777
2778         getConnectionObject:function()
2779         {
2780             var o;
2781             var tId = this.transactionId;
2782
2783             try
2784             {
2785                 o = this.createXhrObject(tId);
2786                 if (o) {
2787                     this.transactionId++;
2788                 }
2789             }
2790             catch(e) {
2791             }
2792             finally
2793             {
2794                 return o;
2795             }
2796         },
2797
2798         asyncRequest:function(method, uri, callback, postData)
2799         {
2800             var o = this.getConnectionObject();
2801
2802             if (!o) {
2803                 return null;
2804             }
2805             else {
2806                 o.conn.open(method, uri, true);
2807
2808                 if (this.useDefaultXhrHeader) {
2809                     if (!this.defaultHeaders['X-Requested-With']) {
2810                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2811                     }
2812                 }
2813
2814                 if(postData && this.useDefaultHeader){
2815                     this.initHeader('Content-Type', this.defaultPostHeader);
2816                 }
2817
2818                  if (this.hasDefaultHeaders || this.hasHeaders) {
2819                     this.setHeader(o);
2820                 }
2821
2822                 this.handleReadyState(o, callback);
2823                 o.conn.send(postData || null);
2824
2825                 return o;
2826             }
2827         },
2828
2829         handleReadyState:function(o, callback)
2830         {
2831             var oConn = this;
2832
2833             if (callback && callback.timeout) {
2834                 
2835                 this.timeout[o.tId] = window.setTimeout(function() {
2836                     oConn.abort(o, callback, true);
2837                 }, callback.timeout);
2838             }
2839
2840             this.poll[o.tId] = window.setInterval(
2841                     function() {
2842                         if (o.conn && o.conn.readyState == 4) {
2843                             window.clearInterval(oConn.poll[o.tId]);
2844                             delete oConn.poll[o.tId];
2845
2846                             if(callback && callback.timeout) {
2847                                 window.clearTimeout(oConn.timeout[o.tId]);
2848                                 delete oConn.timeout[o.tId];
2849                             }
2850
2851                             oConn.handleTransactionResponse(o, callback);
2852                         }
2853                     }
2854                     , this.pollInterval);
2855         },
2856
2857         handleTransactionResponse:function(o, callback, isAbort)
2858         {
2859
2860             if (!callback) {
2861                 this.releaseObject(o);
2862                 return;
2863             }
2864
2865             var httpStatus, responseObject;
2866
2867             try
2868             {
2869                 if (o.conn.status !== undefined && o.conn.status != 0) {
2870                     httpStatus = o.conn.status;
2871                 }
2872                 else {
2873                     httpStatus = 13030;
2874                 }
2875             }
2876             catch(e) {
2877
2878
2879                 httpStatus = 13030;
2880             }
2881
2882             if (httpStatus >= 200 && httpStatus < 300) {
2883                 responseObject = this.createResponseObject(o, callback.argument);
2884                 if (callback.success) {
2885                     if (!callback.scope) {
2886                         callback.success(responseObject);
2887                     }
2888                     else {
2889
2890
2891                         callback.success.apply(callback.scope, [responseObject]);
2892                     }
2893                 }
2894             }
2895             else {
2896                 switch (httpStatus) {
2897
2898                     case 12002:
2899                     case 12029:
2900                     case 12030:
2901                     case 12031:
2902                     case 12152:
2903                     case 13030:
2904                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2905                         if (callback.failure) {
2906                             if (!callback.scope) {
2907                                 callback.failure(responseObject);
2908                             }
2909                             else {
2910                                 callback.failure.apply(callback.scope, [responseObject]);
2911                             }
2912                         }
2913                         break;
2914                     default:
2915                         responseObject = this.createResponseObject(o, callback.argument);
2916                         if (callback.failure) {
2917                             if (!callback.scope) {
2918                                 callback.failure(responseObject);
2919                             }
2920                             else {
2921                                 callback.failure.apply(callback.scope, [responseObject]);
2922                             }
2923                         }
2924                 }
2925             }
2926
2927             this.releaseObject(o);
2928             responseObject = null;
2929         },
2930
2931         createResponseObject:function(o, callbackArg)
2932         {
2933             var obj = {};
2934             var headerObj = {};
2935
2936             try
2937             {
2938                 var headerStr = o.conn.getAllResponseHeaders();
2939                 var header = headerStr.split('\n');
2940                 for (var i = 0; i < header.length; i++) {
2941                     var delimitPos = header[i].indexOf(':');
2942                     if (delimitPos != -1) {
2943                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2944                     }
2945                 }
2946             }
2947             catch(e) {
2948             }
2949
2950             obj.tId = o.tId;
2951             obj.status = o.conn.status;
2952             obj.statusText = o.conn.statusText;
2953             obj.getResponseHeader = headerObj;
2954             obj.getAllResponseHeaders = headerStr;
2955             obj.responseText = o.conn.responseText;
2956             obj.responseXML = o.conn.responseXML;
2957
2958             if (typeof callbackArg !== undefined) {
2959                 obj.argument = callbackArg;
2960             }
2961
2962             return obj;
2963         },
2964
2965         createExceptionObject:function(tId, callbackArg, isAbort)
2966         {
2967             var COMM_CODE = 0;
2968             var COMM_ERROR = 'communication failure';
2969             var ABORT_CODE = -1;
2970             var ABORT_ERROR = 'transaction aborted';
2971
2972             var obj = {};
2973
2974             obj.tId = tId;
2975             if (isAbort) {
2976                 obj.status = ABORT_CODE;
2977                 obj.statusText = ABORT_ERROR;
2978             }
2979             else {
2980                 obj.status = COMM_CODE;
2981                 obj.statusText = COMM_ERROR;
2982             }
2983
2984             if (callbackArg) {
2985                 obj.argument = callbackArg;
2986             }
2987
2988             return obj;
2989         },
2990
2991         initHeader:function(label, value, isDefault)
2992         {
2993             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
2994
2995             if (headerObj[label] === undefined) {
2996                 headerObj[label] = value;
2997             }
2998             else {
2999
3000
3001                 headerObj[label] = value + "," + headerObj[label];
3002             }
3003
3004             if (isDefault) {
3005                 this.hasDefaultHeaders = true;
3006             }
3007             else {
3008                 this.hasHeaders = true;
3009             }
3010         },
3011
3012
3013         setHeader:function(o)
3014         {
3015             if (this.hasDefaultHeaders) {
3016                 for (var prop in this.defaultHeaders) {
3017                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3018                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3019                     }
3020                 }
3021             }
3022
3023             if (this.hasHeaders) {
3024                 for (var prop in this.headers) {
3025                     if (this.headers.hasOwnProperty(prop)) {
3026                         o.conn.setRequestHeader(prop, this.headers[prop]);
3027                     }
3028                 }
3029                 this.headers = {};
3030                 this.hasHeaders = false;
3031             }
3032         },
3033
3034         resetDefaultHeaders:function() {
3035             delete this.defaultHeaders;
3036             this.defaultHeaders = {};
3037             this.hasDefaultHeaders = false;
3038         },
3039
3040         abort:function(o, callback, isTimeout)
3041         {
3042             if(this.isCallInProgress(o)) {
3043                 o.conn.abort();
3044                 window.clearInterval(this.poll[o.tId]);
3045                 delete this.poll[o.tId];
3046                 if (isTimeout) {
3047                     delete this.timeout[o.tId];
3048                 }
3049
3050                 this.handleTransactionResponse(o, callback, true);
3051
3052                 return true;
3053             }
3054             else {
3055                 return false;
3056             }
3057         },
3058
3059
3060         isCallInProgress:function(o)
3061         {
3062             if (o && o.conn) {
3063                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3064             }
3065             else {
3066
3067                 return false;
3068             }
3069         },
3070
3071
3072         releaseObject:function(o)
3073         {
3074
3075             o.conn = null;
3076
3077             o = null;
3078         },
3079
3080         activeX:[
3081         'MSXML2.XMLHTTP.3.0',
3082         'MSXML2.XMLHTTP',
3083         'Microsoft.XMLHTTP'
3084         ]
3085
3086
3087     };
3088 })();/*
3089  * Portions of this file are based on pieces of Yahoo User Interface Library
3090  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3091  * YUI licensed under the BSD License:
3092  * http://developer.yahoo.net/yui/license.txt
3093  * <script type="text/javascript">
3094  *
3095  */
3096
3097 Roo.lib.Region = function(t, r, b, l) {
3098     this.top = t;
3099     this[1] = t;
3100     this.right = r;
3101     this.bottom = b;
3102     this.left = l;
3103     this[0] = l;
3104 };
3105
3106
3107 Roo.lib.Region.prototype = {
3108     contains : function(region) {
3109         return ( region.left >= this.left &&
3110                  region.right <= this.right &&
3111                  region.top >= this.top &&
3112                  region.bottom <= this.bottom    );
3113
3114     },
3115
3116     getArea : function() {
3117         return ( (this.bottom - this.top) * (this.right - this.left) );
3118     },
3119
3120     intersect : function(region) {
3121         var t = Math.max(this.top, region.top);
3122         var r = Math.min(this.right, region.right);
3123         var b = Math.min(this.bottom, region.bottom);
3124         var l = Math.max(this.left, region.left);
3125
3126         if (b >= t && r >= l) {
3127             return new Roo.lib.Region(t, r, b, l);
3128         } else {
3129             return null;
3130         }
3131     },
3132     union : function(region) {
3133         var t = Math.min(this.top, region.top);
3134         var r = Math.max(this.right, region.right);
3135         var b = Math.max(this.bottom, region.bottom);
3136         var l = Math.min(this.left, region.left);
3137
3138         return new Roo.lib.Region(t, r, b, l);
3139     },
3140
3141     adjust : function(t, l, b, r) {
3142         this.top += t;
3143         this.left += l;
3144         this.right += r;
3145         this.bottom += b;
3146         return this;
3147     }
3148 };
3149
3150 Roo.lib.Region.getRegion = function(el) {
3151     var p = Roo.lib.Dom.getXY(el);
3152
3153     var t = p[1];
3154     var r = p[0] + el.offsetWidth;
3155     var b = p[1] + el.offsetHeight;
3156     var l = p[0];
3157
3158     return new Roo.lib.Region(t, r, b, l);
3159 };
3160 /*
3161  * Portions of this file are based on pieces of Yahoo User Interface Library
3162  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3163  * YUI licensed under the BSD License:
3164  * http://developer.yahoo.net/yui/license.txt
3165  * <script type="text/javascript">
3166  *
3167  */
3168 //@@dep Roo.lib.Region
3169
3170
3171 Roo.lib.Point = function(x, y) {
3172     if (x instanceof Array) {
3173         y = x[1];
3174         x = x[0];
3175     }
3176     this.x = this.right = this.left = this[0] = x;
3177     this.y = this.top = this.bottom = this[1] = y;
3178 };
3179
3180 Roo.lib.Point.prototype = new Roo.lib.Region();
3181 /*
3182  * Portions of this file are based on pieces of Yahoo User Interface Library
3183  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3184  * YUI licensed under the BSD License:
3185  * http://developer.yahoo.net/yui/license.txt
3186  * <script type="text/javascript">
3187  *
3188  */
3189  
3190 (function() {   
3191
3192     Roo.lib.Anim = {
3193         scroll : function(el, args, duration, easing, cb, scope) {
3194             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3195         },
3196
3197         motion : function(el, args, duration, easing, cb, scope) {
3198             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3199         },
3200
3201         color : function(el, args, duration, easing, cb, scope) {
3202             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3203         },
3204
3205         run : function(el, args, duration, easing, cb, scope, type) {
3206             type = type || Roo.lib.AnimBase;
3207             if (typeof easing == "string") {
3208                 easing = Roo.lib.Easing[easing];
3209             }
3210             var anim = new type(el, args, duration, easing);
3211             anim.animateX(function() {
3212                 Roo.callback(cb, scope);
3213             });
3214             return anim;
3215         }
3216     };
3217 })();/*
3218  * Portions of this file are based on pieces of Yahoo User Interface Library
3219  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3220  * YUI licensed under the BSD License:
3221  * http://developer.yahoo.net/yui/license.txt
3222  * <script type="text/javascript">
3223  *
3224  */
3225
3226 (function() {    
3227     var libFlyweight;
3228     
3229     function fly(el) {
3230         if (!libFlyweight) {
3231             libFlyweight = new Roo.Element.Flyweight();
3232         }
3233         libFlyweight.dom = el;
3234         return libFlyweight;
3235     }
3236
3237     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3238     
3239    
3240     
3241     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3242         if (el) {
3243             this.init(el, attributes, duration, method);
3244         }
3245     };
3246
3247     Roo.lib.AnimBase.fly = fly;
3248     
3249     
3250     
3251     Roo.lib.AnimBase.prototype = {
3252
3253         toString: function() {
3254             var el = this.getEl();
3255             var id = el.id || el.tagName;
3256             return ("Anim " + id);
3257         },
3258
3259         patterns: {
3260             noNegatives:        /width|height|opacity|padding/i,
3261             offsetAttribute:  /^((width|height)|(top|left))$/,
3262             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3263             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3264         },
3265
3266
3267         doMethod: function(attr, start, end) {
3268             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3269         },
3270
3271
3272         setAttribute: function(attr, val, unit) {
3273             if (this.patterns.noNegatives.test(attr)) {
3274                 val = (val > 0) ? val : 0;
3275             }
3276
3277             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3278         },
3279
3280
3281         getAttribute: function(attr) {
3282             var el = this.getEl();
3283             var val = fly(el).getStyle(attr);
3284
3285             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3286                 return parseFloat(val);
3287             }
3288
3289             var a = this.patterns.offsetAttribute.exec(attr) || [];
3290             var pos = !!( a[3] );
3291             var box = !!( a[2] );
3292
3293
3294             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3295                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3296             } else {
3297                 val = 0;
3298             }
3299
3300             return val;
3301         },
3302
3303
3304         getDefaultUnit: function(attr) {
3305             if (this.patterns.defaultUnit.test(attr)) {
3306                 return 'px';
3307             }
3308
3309             return '';
3310         },
3311
3312         animateX : function(callback, scope) {
3313             var f = function() {
3314                 this.onComplete.removeListener(f);
3315                 if (typeof callback == "function") {
3316                     callback.call(scope || this, this);
3317                 }
3318             };
3319             this.onComplete.addListener(f, this);
3320             this.animate();
3321         },
3322
3323
3324         setRuntimeAttribute: function(attr) {
3325             var start;
3326             var end;
3327             var attributes = this.attributes;
3328
3329             this.runtimeAttributes[attr] = {};
3330
3331             var isset = function(prop) {
3332                 return (typeof prop !== 'undefined');
3333             };
3334
3335             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3336                 return false;
3337             }
3338
3339             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3340
3341
3342             if (isset(attributes[attr]['to'])) {
3343                 end = attributes[attr]['to'];
3344             } else if (isset(attributes[attr]['by'])) {
3345                 if (start.constructor == Array) {
3346                     end = [];
3347                     for (var i = 0, len = start.length; i < len; ++i) {
3348                         end[i] = start[i] + attributes[attr]['by'][i];
3349                     }
3350                 } else {
3351                     end = start + attributes[attr]['by'];
3352                 }
3353             }
3354
3355             this.runtimeAttributes[attr].start = start;
3356             this.runtimeAttributes[attr].end = end;
3357
3358
3359             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3360         },
3361
3362
3363         init: function(el, attributes, duration, method) {
3364
3365             var isAnimated = false;
3366
3367
3368             var startTime = null;
3369
3370
3371             var actualFrames = 0;
3372
3373
3374             el = Roo.getDom(el);
3375
3376
3377             this.attributes = attributes || {};
3378
3379
3380             this.duration = duration || 1;
3381
3382
3383             this.method = method || Roo.lib.Easing.easeNone;
3384
3385
3386             this.useSeconds = true;
3387
3388
3389             this.currentFrame = 0;
3390
3391
3392             this.totalFrames = Roo.lib.AnimMgr.fps;
3393
3394
3395             this.getEl = function() {
3396                 return el;
3397             };
3398
3399
3400             this.isAnimated = function() {
3401                 return isAnimated;
3402             };
3403
3404
3405             this.getStartTime = function() {
3406                 return startTime;
3407             };
3408
3409             this.runtimeAttributes = {};
3410
3411
3412             this.animate = function() {
3413                 if (this.isAnimated()) {
3414                     return false;
3415                 }
3416
3417                 this.currentFrame = 0;
3418
3419                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3420
3421                 Roo.lib.AnimMgr.registerElement(this);
3422             };
3423
3424
3425             this.stop = function(finish) {
3426                 if (finish) {
3427                     this.currentFrame = this.totalFrames;
3428                     this._onTween.fire();
3429                 }
3430                 Roo.lib.AnimMgr.stop(this);
3431             };
3432
3433             var onStart = function() {
3434                 this.onStart.fire();
3435
3436                 this.runtimeAttributes = {};
3437                 for (var attr in this.attributes) {
3438                     this.setRuntimeAttribute(attr);
3439                 }
3440
3441                 isAnimated = true;
3442                 actualFrames = 0;
3443                 startTime = new Date();
3444             };
3445
3446
3447             var onTween = function() {
3448                 var data = {
3449                     duration: new Date() - this.getStartTime(),
3450                     currentFrame: this.currentFrame
3451                 };
3452
3453                 data.toString = function() {
3454                     return (
3455                             'duration: ' + data.duration +
3456                             ', currentFrame: ' + data.currentFrame
3457                             );
3458                 };
3459
3460                 this.onTween.fire(data);
3461
3462                 var runtimeAttributes = this.runtimeAttributes;
3463
3464                 for (var attr in runtimeAttributes) {
3465                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3466                 }
3467
3468                 actualFrames += 1;
3469             };
3470
3471             var onComplete = function() {
3472                 var actual_duration = (new Date() - startTime) / 1000 ;
3473
3474                 var data = {
3475                     duration: actual_duration,
3476                     frames: actualFrames,
3477                     fps: actualFrames / actual_duration
3478                 };
3479
3480                 data.toString = function() {
3481                     return (
3482                             'duration: ' + data.duration +
3483                             ', frames: ' + data.frames +
3484                             ', fps: ' + data.fps
3485                             );
3486                 };
3487
3488                 isAnimated = false;
3489                 actualFrames = 0;
3490                 this.onComplete.fire(data);
3491             };
3492
3493
3494             this._onStart = new Roo.util.Event(this);
3495             this.onStart = new Roo.util.Event(this);
3496             this.onTween = new Roo.util.Event(this);
3497             this._onTween = new Roo.util.Event(this);
3498             this.onComplete = new Roo.util.Event(this);
3499             this._onComplete = new Roo.util.Event(this);
3500             this._onStart.addListener(onStart);
3501             this._onTween.addListener(onTween);
3502             this._onComplete.addListener(onComplete);
3503         }
3504     };
3505 })();
3506 /*
3507  * Portions of this file are based on pieces of Yahoo User Interface Library
3508  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3509  * YUI licensed under the BSD License:
3510  * http://developer.yahoo.net/yui/license.txt
3511  * <script type="text/javascript">
3512  *
3513  */
3514
3515 Roo.lib.AnimMgr = new function() {
3516
3517     var thread = null;
3518
3519
3520     var queue = [];
3521
3522
3523     var tweenCount = 0;
3524
3525
3526     this.fps = 1000;
3527
3528
3529     this.delay = 1;
3530
3531
3532     this.registerElement = function(tween) {
3533         queue[queue.length] = tween;
3534         tweenCount += 1;
3535         tween._onStart.fire();
3536         this.start();
3537     };
3538
3539
3540     this.unRegister = function(tween, index) {
3541         tween._onComplete.fire();
3542         index = index || getIndex(tween);
3543         if (index != -1) {
3544             queue.splice(index, 1);
3545         }
3546
3547         tweenCount -= 1;
3548         if (tweenCount <= 0) {
3549             this.stop();
3550         }
3551     };
3552
3553
3554     this.start = function() {
3555         if (thread === null) {
3556             thread = setInterval(this.run, this.delay);
3557         }
3558     };
3559
3560
3561     this.stop = function(tween) {
3562         if (!tween) {
3563             clearInterval(thread);
3564
3565             for (var i = 0, len = queue.length; i < len; ++i) {
3566                 if (queue[0].isAnimated()) {
3567                     this.unRegister(queue[0], 0);
3568                 }
3569             }
3570
3571             queue = [];
3572             thread = null;
3573             tweenCount = 0;
3574         }
3575         else {
3576             this.unRegister(tween);
3577         }
3578     };
3579
3580
3581     this.run = function() {
3582         for (var i = 0, len = queue.length; i < len; ++i) {
3583             var tween = queue[i];
3584             if (!tween || !tween.isAnimated()) {
3585                 continue;
3586             }
3587
3588             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3589             {
3590                 tween.currentFrame += 1;
3591
3592                 if (tween.useSeconds) {
3593                     correctFrame(tween);
3594                 }
3595                 tween._onTween.fire();
3596             }
3597             else {
3598                 Roo.lib.AnimMgr.stop(tween, i);
3599             }
3600         }
3601     };
3602
3603     var getIndex = function(anim) {
3604         for (var i = 0, len = queue.length; i < len; ++i) {
3605             if (queue[i] == anim) {
3606                 return i;
3607             }
3608         }
3609         return -1;
3610     };
3611
3612
3613     var correctFrame = function(tween) {
3614         var frames = tween.totalFrames;
3615         var frame = tween.currentFrame;
3616         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3617         var elapsed = (new Date() - tween.getStartTime());
3618         var tweak = 0;
3619
3620         if (elapsed < tween.duration * 1000) {
3621             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3622         } else {
3623             tweak = frames - (frame + 1);
3624         }
3625         if (tweak > 0 && isFinite(tweak)) {
3626             if (tween.currentFrame + tweak >= frames) {
3627                 tweak = frames - (frame + 1);
3628             }
3629
3630             tween.currentFrame += tweak;
3631         }
3632     };
3633 };
3634
3635     /*
3636  * Portions of this file are based on pieces of Yahoo User Interface Library
3637  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3638  * YUI licensed under the BSD License:
3639  * http://developer.yahoo.net/yui/license.txt
3640  * <script type="text/javascript">
3641  *
3642  */
3643 Roo.lib.Bezier = new function() {
3644
3645         this.getPosition = function(points, t) {
3646             var n = points.length;
3647             var tmp = [];
3648
3649             for (var i = 0; i < n; ++i) {
3650                 tmp[i] = [points[i][0], points[i][1]];
3651             }
3652
3653             for (var j = 1; j < n; ++j) {
3654                 for (i = 0; i < n - j; ++i) {
3655                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3656                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3657                 }
3658             }
3659
3660             return [ tmp[0][0], tmp[0][1] ];
3661
3662         };
3663     };/*
3664  * Portions of this file are based on pieces of Yahoo User Interface Library
3665  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3666  * YUI licensed under the BSD License:
3667  * http://developer.yahoo.net/yui/license.txt
3668  * <script type="text/javascript">
3669  *
3670  */
3671 (function() {
3672
3673     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
3674         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
3675     };
3676
3677     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
3678
3679     var fly = Roo.lib.AnimBase.fly;
3680     var Y = Roo.lib;
3681     var superclass = Y.ColorAnim.superclass;
3682     var proto = Y.ColorAnim.prototype;
3683
3684     proto.toString = function() {
3685         var el = this.getEl();
3686         var id = el.id || el.tagName;
3687         return ("ColorAnim " + id);
3688     };
3689
3690     proto.patterns.color = /color$/i;
3691     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
3692     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
3693     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
3694     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
3695
3696
3697     proto.parseColor = function(s) {
3698         if (s.length == 3) {
3699             return s;
3700         }
3701
3702         var c = this.patterns.hex.exec(s);
3703         if (c && c.length == 4) {
3704             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
3705         }
3706
3707         c = this.patterns.rgb.exec(s);
3708         if (c && c.length == 4) {
3709             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
3710         }
3711
3712         c = this.patterns.hex3.exec(s);
3713         if (c && c.length == 4) {
3714             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
3715         }
3716
3717         return null;
3718     };
3719     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
3720     proto.getAttribute = function(attr) {
3721         var el = this.getEl();
3722         if (this.patterns.color.test(attr)) {
3723             var val = fly(el).getStyle(attr);
3724
3725             if (this.patterns.transparent.test(val)) {
3726                 var parent = el.parentNode;
3727                 val = fly(parent).getStyle(attr);
3728
3729                 while (parent && this.patterns.transparent.test(val)) {
3730                     parent = parent.parentNode;
3731                     val = fly(parent).getStyle(attr);
3732                     if (parent.tagName.toUpperCase() == 'HTML') {
3733                         val = '#fff';
3734                     }
3735                 }
3736             }
3737         } else {
3738             val = superclass.getAttribute.call(this, attr);
3739         }
3740
3741         return val;
3742     };
3743     proto.getAttribute = function(attr) {
3744         var el = this.getEl();
3745         if (this.patterns.color.test(attr)) {
3746             var val = fly(el).getStyle(attr);
3747
3748             if (this.patterns.transparent.test(val)) {
3749                 var parent = el.parentNode;
3750                 val = fly(parent).getStyle(attr);
3751
3752                 while (parent && this.patterns.transparent.test(val)) {
3753                     parent = parent.parentNode;
3754                     val = fly(parent).getStyle(attr);
3755                     if (parent.tagName.toUpperCase() == 'HTML') {
3756                         val = '#fff';
3757                     }
3758                 }
3759             }
3760         } else {
3761             val = superclass.getAttribute.call(this, attr);
3762         }
3763
3764         return val;
3765     };
3766
3767     proto.doMethod = function(attr, start, end) {
3768         var val;
3769
3770         if (this.patterns.color.test(attr)) {
3771             val = [];
3772             for (var i = 0, len = start.length; i < len; ++i) {
3773                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
3774             }
3775
3776             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
3777         }
3778         else {
3779             val = superclass.doMethod.call(this, attr, start, end);
3780         }
3781
3782         return val;
3783     };
3784
3785     proto.setRuntimeAttribute = function(attr) {
3786         superclass.setRuntimeAttribute.call(this, attr);
3787
3788         if (this.patterns.color.test(attr)) {
3789             var attributes = this.attributes;
3790             var start = this.parseColor(this.runtimeAttributes[attr].start);
3791             var end = this.parseColor(this.runtimeAttributes[attr].end);
3792
3793             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
3794                 end = this.parseColor(attributes[attr].by);
3795
3796                 for (var i = 0, len = start.length; i < len; ++i) {
3797                     end[i] = start[i] + end[i];
3798                 }
3799             }
3800
3801             this.runtimeAttributes[attr].start = start;
3802             this.runtimeAttributes[attr].end = end;
3803         }
3804     };
3805 })();
3806
3807 /*
3808  * Portions of this file are based on pieces of Yahoo User Interface Library
3809  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3810  * YUI licensed under the BSD License:
3811  * http://developer.yahoo.net/yui/license.txt
3812  * <script type="text/javascript">
3813  *
3814  */
3815 Roo.lib.Easing = {
3816
3817
3818     easeNone: function (t, b, c, d) {
3819         return c * t / d + b;
3820     },
3821
3822
3823     easeIn: function (t, b, c, d) {
3824         return c * (t /= d) * t + b;
3825     },
3826
3827
3828     easeOut: function (t, b, c, d) {
3829         return -c * (t /= d) * (t - 2) + b;
3830     },
3831
3832
3833     easeBoth: function (t, b, c, d) {
3834         if ((t /= d / 2) < 1) {
3835             return c / 2 * t * t + b;
3836         }
3837
3838         return -c / 2 * ((--t) * (t - 2) - 1) + b;
3839     },
3840
3841
3842     easeInStrong: function (t, b, c, d) {
3843         return c * (t /= d) * t * t * t + b;
3844     },
3845
3846
3847     easeOutStrong: function (t, b, c, d) {
3848         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
3849     },
3850
3851
3852     easeBothStrong: function (t, b, c, d) {
3853         if ((t /= d / 2) < 1) {
3854             return c / 2 * t * t * t * t + b;
3855         }
3856
3857         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
3858     },
3859
3860
3861
3862     elasticIn: function (t, b, c, d, a, p) {
3863         if (t == 0) {
3864             return b;
3865         }
3866         if ((t /= d) == 1) {
3867             return b + c;
3868         }
3869         if (!p) {
3870             p = d * .3;
3871         }
3872
3873         if (!a || a < Math.abs(c)) {
3874             a = c;
3875             var s = p / 4;
3876         }
3877         else {
3878             var s = p / (2 * Math.PI) * Math.asin(c / a);
3879         }
3880
3881         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3882     },
3883
3884
3885     elasticOut: function (t, b, c, d, a, p) {
3886         if (t == 0) {
3887             return b;
3888         }
3889         if ((t /= d) == 1) {
3890             return b + c;
3891         }
3892         if (!p) {
3893             p = d * .3;
3894         }
3895
3896         if (!a || a < Math.abs(c)) {
3897             a = c;
3898             var s = p / 4;
3899         }
3900         else {
3901             var s = p / (2 * Math.PI) * Math.asin(c / a);
3902         }
3903
3904         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
3905     },
3906
3907
3908     elasticBoth: function (t, b, c, d, a, p) {
3909         if (t == 0) {
3910             return b;
3911         }
3912
3913         if ((t /= d / 2) == 2) {
3914             return b + c;
3915         }
3916
3917         if (!p) {
3918             p = d * (.3 * 1.5);
3919         }
3920
3921         if (!a || a < Math.abs(c)) {
3922             a = c;
3923             var s = p / 4;
3924         }
3925         else {
3926             var s = p / (2 * Math.PI) * Math.asin(c / a);
3927         }
3928
3929         if (t < 1) {
3930             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
3931                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
3932         }
3933         return a * Math.pow(2, -10 * (t -= 1)) *
3934                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
3935     },
3936
3937
3938
3939     backIn: function (t, b, c, d, s) {
3940         if (typeof s == 'undefined') {
3941             s = 1.70158;
3942         }
3943         return c * (t /= d) * t * ((s + 1) * t - s) + b;
3944     },
3945
3946
3947     backOut: function (t, b, c, d, s) {
3948         if (typeof s == 'undefined') {
3949             s = 1.70158;
3950         }
3951         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
3952     },
3953
3954
3955     backBoth: function (t, b, c, d, s) {
3956         if (typeof s == 'undefined') {
3957             s = 1.70158;
3958         }
3959
3960         if ((t /= d / 2 ) < 1) {
3961             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
3962         }
3963         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
3964     },
3965
3966
3967     bounceIn: function (t, b, c, d) {
3968         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
3969     },
3970
3971
3972     bounceOut: function (t, b, c, d) {
3973         if ((t /= d) < (1 / 2.75)) {
3974             return c * (7.5625 * t * t) + b;
3975         } else if (t < (2 / 2.75)) {
3976             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
3977         } else if (t < (2.5 / 2.75)) {
3978             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
3979         }
3980         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
3981     },
3982
3983
3984     bounceBoth: function (t, b, c, d) {
3985         if (t < d / 2) {
3986             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
3987         }
3988         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
3989     }
3990 };/*
3991  * Portions of this file are based on pieces of Yahoo User Interface Library
3992  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3993  * YUI licensed under the BSD License:
3994  * http://developer.yahoo.net/yui/license.txt
3995  * <script type="text/javascript">
3996  *
3997  */
3998     (function() {
3999         Roo.lib.Motion = function(el, attributes, duration, method) {
4000             if (el) {
4001                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4002             }
4003         };
4004
4005         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4006
4007
4008         var Y = Roo.lib;
4009         var superclass = Y.Motion.superclass;
4010         var proto = Y.Motion.prototype;
4011
4012         proto.toString = function() {
4013             var el = this.getEl();
4014             var id = el.id || el.tagName;
4015             return ("Motion " + id);
4016         };
4017
4018         proto.patterns.points = /^points$/i;
4019
4020         proto.setAttribute = function(attr, val, unit) {
4021             if (this.patterns.points.test(attr)) {
4022                 unit = unit || 'px';
4023                 superclass.setAttribute.call(this, 'left', val[0], unit);
4024                 superclass.setAttribute.call(this, 'top', val[1], unit);
4025             } else {
4026                 superclass.setAttribute.call(this, attr, val, unit);
4027             }
4028         };
4029
4030         proto.getAttribute = function(attr) {
4031             if (this.patterns.points.test(attr)) {
4032                 var val = [
4033                         superclass.getAttribute.call(this, 'left'),
4034                         superclass.getAttribute.call(this, 'top')
4035                         ];
4036             } else {
4037                 val = superclass.getAttribute.call(this, attr);
4038             }
4039
4040             return val;
4041         };
4042
4043         proto.doMethod = function(attr, start, end) {
4044             var val = null;
4045
4046             if (this.patterns.points.test(attr)) {
4047                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4048                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4049             } else {
4050                 val = superclass.doMethod.call(this, attr, start, end);
4051             }
4052             return val;
4053         };
4054
4055         proto.setRuntimeAttribute = function(attr) {
4056             if (this.patterns.points.test(attr)) {
4057                 var el = this.getEl();
4058                 var attributes = this.attributes;
4059                 var start;
4060                 var control = attributes['points']['control'] || [];
4061                 var end;
4062                 var i, len;
4063
4064                 if (control.length > 0 && !(control[0] instanceof Array)) {
4065                     control = [control];
4066                 } else {
4067                     var tmp = [];
4068                     for (i = 0,len = control.length; i < len; ++i) {
4069                         tmp[i] = control[i];
4070                     }
4071                     control = tmp;
4072                 }
4073
4074                 Roo.fly(el).position();
4075
4076                 if (isset(attributes['points']['from'])) {
4077                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4078                 }
4079                 else {
4080                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4081                 }
4082
4083                 start = this.getAttribute('points');
4084
4085
4086                 if (isset(attributes['points']['to'])) {
4087                     end = translateValues.call(this, attributes['points']['to'], start);
4088
4089                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4090                     for (i = 0,len = control.length; i < len; ++i) {
4091                         control[i] = translateValues.call(this, control[i], start);
4092                     }
4093
4094
4095                 } else if (isset(attributes['points']['by'])) {
4096                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4097
4098                     for (i = 0,len = control.length; i < len; ++i) {
4099                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4100                     }
4101                 }
4102
4103                 this.runtimeAttributes[attr] = [start];
4104
4105                 if (control.length > 0) {
4106                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4107                 }
4108
4109                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4110             }
4111             else {
4112                 superclass.setRuntimeAttribute.call(this, attr);
4113             }
4114         };
4115
4116         var translateValues = function(val, start) {
4117             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4118             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4119
4120             return val;
4121         };
4122
4123         var isset = function(prop) {
4124             return (typeof prop !== 'undefined');
4125         };
4126     })();
4127 /*
4128  * Portions of this file are based on pieces of Yahoo User Interface Library
4129  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4130  * YUI licensed under the BSD License:
4131  * http://developer.yahoo.net/yui/license.txt
4132  * <script type="text/javascript">
4133  *
4134  */
4135     (function() {
4136         Roo.lib.Scroll = function(el, attributes, duration, method) {
4137             if (el) {
4138                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4139             }
4140         };
4141
4142         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4143
4144
4145         var Y = Roo.lib;
4146         var superclass = Y.Scroll.superclass;
4147         var proto = Y.Scroll.prototype;
4148
4149         proto.toString = function() {
4150             var el = this.getEl();
4151             var id = el.id || el.tagName;
4152             return ("Scroll " + id);
4153         };
4154
4155         proto.doMethod = function(attr, start, end) {
4156             var val = null;
4157
4158             if (attr == 'scroll') {
4159                 val = [
4160                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4161                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4162                         ];
4163
4164             } else {
4165                 val = superclass.doMethod.call(this, attr, start, end);
4166             }
4167             return val;
4168         };
4169
4170         proto.getAttribute = function(attr) {
4171             var val = null;
4172             var el = this.getEl();
4173
4174             if (attr == 'scroll') {
4175                 val = [ el.scrollLeft, el.scrollTop ];
4176             } else {
4177                 val = superclass.getAttribute.call(this, attr);
4178             }
4179
4180             return val;
4181         };
4182
4183         proto.setAttribute = function(attr, val, unit) {
4184             var el = this.getEl();
4185
4186             if (attr == 'scroll') {
4187                 el.scrollLeft = val[0];
4188                 el.scrollTop = val[1];
4189             } else {
4190                 superclass.setAttribute.call(this, attr, val, unit);
4191             }
4192         };
4193     })();
4194 /*
4195  * Based on:
4196  * Ext JS Library 1.1.1
4197  * Copyright(c) 2006-2007, Ext JS, LLC.
4198  *
4199  * Originally Released Under LGPL - original licence link has changed is not relivant.
4200  *
4201  * Fork - LGPL
4202  * <script type="text/javascript">
4203  */
4204
4205
4206 // nasty IE9 hack - what a pile of crap that is..
4207
4208  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4209     Range.prototype.createContextualFragment = function (html) {
4210         var doc = window.document;
4211         var container = doc.createElement("div");
4212         container.innerHTML = html;
4213         var frag = doc.createDocumentFragment(), n;
4214         while ((n = container.firstChild)) {
4215             frag.appendChild(n);
4216         }
4217         return frag;
4218     };
4219 }
4220
4221 /**
4222  * @class Roo.DomHelper
4223  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4224  * For more information see <a href="http://web.archive.org/web/20071221063734/http://www.jackslocum.com/blog/2006/10/06/domhelper-create-elements-using-dom-html-fragments-or-templates/">this blog post with examples</a>.
4225  * @singleton
4226  */
4227 Roo.DomHelper = function(){
4228     var tempTableEl = null;
4229     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4230     var tableRe = /^table|tbody|tr|td$/i;
4231     var xmlns = {};
4232     // build as innerHTML where available
4233     /** @ignore */
4234     var createHtml = function(o){
4235         if(typeof o == 'string'){
4236             return o;
4237         }
4238         var b = "";
4239         if(!o.tag){
4240             o.tag = "div";
4241         }
4242         b += "<" + o.tag;
4243         for(var attr in o){
4244             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4245             if(attr == "style"){
4246                 var s = o["style"];
4247                 if(typeof s == "function"){
4248                     s = s.call();
4249                 }
4250                 if(typeof s == "string"){
4251                     b += ' style="' + s + '"';
4252                 }else if(typeof s == "object"){
4253                     b += ' style="';
4254                     for(var key in s){
4255                         if(typeof s[key] != "function"){
4256                             b += key + ":" + s[key] + ";";
4257                         }
4258                     }
4259                     b += '"';
4260                 }
4261             }else{
4262                 if(attr == "cls"){
4263                     b += ' class="' + o["cls"] + '"';
4264                 }else if(attr == "htmlFor"){
4265                     b += ' for="' + o["htmlFor"] + '"';
4266                 }else{
4267                     b += " " + attr + '="' + o[attr] + '"';
4268                 }
4269             }
4270         }
4271         if(emptyTags.test(o.tag)){
4272             b += "/>";
4273         }else{
4274             b += ">";
4275             var cn = o.children || o.cn;
4276             if(cn){
4277                 //http://bugs.kde.org/show_bug.cgi?id=71506
4278                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4279                     for(var i = 0, len = cn.length; i < len; i++) {
4280                         b += createHtml(cn[i], b);
4281                     }
4282                 }else{
4283                     b += createHtml(cn, b);
4284                 }
4285             }
4286             if(o.html){
4287                 b += o.html;
4288             }
4289             b += "</" + o.tag + ">";
4290         }
4291         return b;
4292     };
4293
4294     // build as dom
4295     /** @ignore */
4296     var createDom = function(o, parentNode){
4297          
4298         // defininition craeted..
4299         var ns = false;
4300         if (o.ns && o.ns != 'html') {
4301                
4302             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
4303                 xmlns[o.ns] = o.xmlns;
4304                 ns = o.xmlns;
4305             }
4306             if (typeof(xmlns[o.ns]) == 'undefined') {
4307                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
4308             }
4309             ns = xmlns[o.ns];
4310         }
4311         
4312         
4313         if (typeof(o) == 'string') {
4314             return parentNode.appendChild(document.createTextNode(o));
4315         }
4316         o.tag = o.tag || div;
4317         if (o.ns && Roo.isIE) {
4318             ns = false;
4319             o.tag = o.ns + ':' + o.tag;
4320             
4321         }
4322         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
4323         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
4324         for(var attr in o){
4325             
4326             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
4327                     attr == "style" || typeof o[attr] == "function") { continue; }
4328                     
4329             if(attr=="cls" && Roo.isIE){
4330                 el.className = o["cls"];
4331             }else{
4332                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
4333                 else { 
4334                     el[attr] = o[attr];
4335                 }
4336             }
4337         }
4338         Roo.DomHelper.applyStyles(el, o.style);
4339         var cn = o.children || o.cn;
4340         if(cn){
4341             //http://bugs.kde.org/show_bug.cgi?id=71506
4342              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4343                 for(var i = 0, len = cn.length; i < len; i++) {
4344                     createDom(cn[i], el);
4345                 }
4346             }else{
4347                 createDom(cn, el);
4348             }
4349         }
4350         if(o.html){
4351             el.innerHTML = o.html;
4352         }
4353         if(parentNode){
4354            parentNode.appendChild(el);
4355         }
4356         return el;
4357     };
4358
4359     var ieTable = function(depth, s, h, e){
4360         tempTableEl.innerHTML = [s, h, e].join('');
4361         var i = -1, el = tempTableEl;
4362         while(++i < depth){
4363             el = el.firstChild;
4364         }
4365         return el;
4366     };
4367
4368     // kill repeat to save bytes
4369     var ts = '<table>',
4370         te = '</table>',
4371         tbs = ts+'<tbody>',
4372         tbe = '</tbody>'+te,
4373         trs = tbs + '<tr>',
4374         tre = '</tr>'+tbe;
4375
4376     /**
4377      * @ignore
4378      * Nasty code for IE's broken table implementation
4379      */
4380     var insertIntoTable = function(tag, where, el, html){
4381         if(!tempTableEl){
4382             tempTableEl = document.createElement('div');
4383         }
4384         var node;
4385         var before = null;
4386         if(tag == 'td'){
4387             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
4388                 return;
4389             }
4390             if(where == 'beforebegin'){
4391                 before = el;
4392                 el = el.parentNode;
4393             } else{
4394                 before = el.nextSibling;
4395                 el = el.parentNode;
4396             }
4397             node = ieTable(4, trs, html, tre);
4398         }
4399         else if(tag == 'tr'){
4400             if(where == 'beforebegin'){
4401                 before = el;
4402                 el = el.parentNode;
4403                 node = ieTable(3, tbs, html, tbe);
4404             } else if(where == 'afterend'){
4405                 before = el.nextSibling;
4406                 el = el.parentNode;
4407                 node = ieTable(3, tbs, html, tbe);
4408             } else{ // INTO a TR
4409                 if(where == 'afterbegin'){
4410                     before = el.firstChild;
4411                 }
4412                 node = ieTable(4, trs, html, tre);
4413             }
4414         } else if(tag == 'tbody'){
4415             if(where == 'beforebegin'){
4416                 before = el;
4417                 el = el.parentNode;
4418                 node = ieTable(2, ts, html, te);
4419             } else if(where == 'afterend'){
4420                 before = el.nextSibling;
4421                 el = el.parentNode;
4422                 node = ieTable(2, ts, html, te);
4423             } else{
4424                 if(where == 'afterbegin'){
4425                     before = el.firstChild;
4426                 }
4427                 node = ieTable(3, tbs, html, tbe);
4428             }
4429         } else{ // TABLE
4430             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
4431                 return;
4432             }
4433             if(where == 'afterbegin'){
4434                 before = el.firstChild;
4435             }
4436             node = ieTable(2, ts, html, te);
4437         }
4438         el.insertBefore(node, before);
4439         return node;
4440     };
4441
4442     return {
4443     /** True to force the use of DOM instead of html fragments @type Boolean */
4444     useDom : false,
4445
4446     /**
4447      * Returns the markup for the passed Element(s) config
4448      * @param {Object} o The Dom object spec (and children)
4449      * @return {String}
4450      */
4451     markup : function(o){
4452         return createHtml(o);
4453     },
4454
4455     /**
4456      * Applies a style specification to an element
4457      * @param {String/HTMLElement} el The element to apply styles to
4458      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
4459      * a function which returns such a specification.
4460      */
4461     applyStyles : function(el, styles){
4462         if(styles){
4463            el = Roo.fly(el);
4464            if(typeof styles == "string"){
4465                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
4466                var matches;
4467                while ((matches = re.exec(styles)) != null){
4468                    el.setStyle(matches[1], matches[2]);
4469                }
4470            }else if (typeof styles == "object"){
4471                for (var style in styles){
4472                   el.setStyle(style, styles[style]);
4473                }
4474            }else if (typeof styles == "function"){
4475                 Roo.DomHelper.applyStyles(el, styles.call());
4476            }
4477         }
4478     },
4479
4480     /**
4481      * Inserts an HTML fragment into the Dom
4482      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
4483      * @param {HTMLElement} el The context element
4484      * @param {String} html The HTML fragmenet
4485      * @return {HTMLElement} The new node
4486      */
4487     insertHtml : function(where, el, html){
4488         where = where.toLowerCase();
4489         if(el.insertAdjacentHTML){
4490             if(tableRe.test(el.tagName)){
4491                 var rs;
4492                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
4493                     return rs;
4494                 }
4495             }
4496             switch(where){
4497                 case "beforebegin":
4498                     el.insertAdjacentHTML('BeforeBegin', html);
4499                     return el.previousSibling;
4500                 case "afterbegin":
4501                     el.insertAdjacentHTML('AfterBegin', html);
4502                     return el.firstChild;
4503                 case "beforeend":
4504                     el.insertAdjacentHTML('BeforeEnd', html);
4505                     return el.lastChild;
4506                 case "afterend":
4507                     el.insertAdjacentHTML('AfterEnd', html);
4508                     return el.nextSibling;
4509             }
4510             throw 'Illegal insertion point -> "' + where + '"';
4511         }
4512         var range = el.ownerDocument.createRange();
4513         var frag;
4514         switch(where){
4515              case "beforebegin":
4516                 range.setStartBefore(el);
4517                 frag = range.createContextualFragment(html);
4518                 el.parentNode.insertBefore(frag, el);
4519                 return el.previousSibling;
4520              case "afterbegin":
4521                 if(el.firstChild){
4522                     range.setStartBefore(el.firstChild);
4523                     frag = range.createContextualFragment(html);
4524                     el.insertBefore(frag, el.firstChild);
4525                     return el.firstChild;
4526                 }else{
4527                     el.innerHTML = html;
4528                     return el.firstChild;
4529                 }
4530             case "beforeend":
4531                 if(el.lastChild){
4532                     range.setStartAfter(el.lastChild);
4533                     frag = range.createContextualFragment(html);
4534                     el.appendChild(frag);
4535                     return el.lastChild;
4536                 }else{
4537                     el.innerHTML = html;
4538                     return el.lastChild;
4539                 }
4540             case "afterend":
4541                 range.setStartAfter(el);
4542                 frag = range.createContextualFragment(html);
4543                 el.parentNode.insertBefore(frag, el.nextSibling);
4544                 return el.nextSibling;
4545             }
4546             throw 'Illegal insertion point -> "' + where + '"';
4547     },
4548
4549     /**
4550      * Creates new Dom element(s) and inserts them before el
4551      * @param {String/HTMLElement/Element} el The context element
4552      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4553      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4554      * @return {HTMLElement/Roo.Element} The new node
4555      */
4556     insertBefore : function(el, o, returnElement){
4557         return this.doInsert(el, o, returnElement, "beforeBegin");
4558     },
4559
4560     /**
4561      * Creates new Dom element(s) and inserts them after el
4562      * @param {String/HTMLElement/Element} el The context element
4563      * @param {Object} o The Dom object spec (and children)
4564      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4565      * @return {HTMLElement/Roo.Element} The new node
4566      */
4567     insertAfter : function(el, o, returnElement){
4568         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
4569     },
4570
4571     /**
4572      * Creates new Dom element(s) and inserts them as the first child of el
4573      * @param {String/HTMLElement/Element} el The context element
4574      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4575      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4576      * @return {HTMLElement/Roo.Element} The new node
4577      */
4578     insertFirst : function(el, o, returnElement){
4579         return this.doInsert(el, o, returnElement, "afterBegin");
4580     },
4581
4582     // private
4583     doInsert : function(el, o, returnElement, pos, sibling){
4584         el = Roo.getDom(el);
4585         var newNode;
4586         if(this.useDom || o.ns){
4587             newNode = createDom(o, null);
4588             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
4589         }else{
4590             var html = createHtml(o);
4591             newNode = this.insertHtml(pos, el, html);
4592         }
4593         return returnElement ? Roo.get(newNode, true) : newNode;
4594     },
4595
4596     /**
4597      * Creates new Dom element(s) and appends them to el
4598      * @param {String/HTMLElement/Element} el The context element
4599      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4600      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4601      * @return {HTMLElement/Roo.Element} The new node
4602      */
4603     append : function(el, o, returnElement){
4604         el = Roo.getDom(el);
4605         var newNode;
4606         if(this.useDom || o.ns){
4607             newNode = createDom(o, null);
4608             el.appendChild(newNode);
4609         }else{
4610             var html = createHtml(o);
4611             newNode = this.insertHtml("beforeEnd", el, html);
4612         }
4613         return returnElement ? Roo.get(newNode, true) : newNode;
4614     },
4615
4616     /**
4617      * Creates new Dom element(s) and overwrites the contents of el with them
4618      * @param {String/HTMLElement/Element} el The context element
4619      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
4620      * @param {Boolean} returnElement (optional) true to return a Roo.Element
4621      * @return {HTMLElement/Roo.Element} The new node
4622      */
4623     overwrite : function(el, o, returnElement){
4624         el = Roo.getDom(el);
4625         if (o.ns) {
4626           
4627             while (el.childNodes.length) {
4628                 el.removeChild(el.firstChild);
4629             }
4630             createDom(o, el);
4631         } else {
4632             el.innerHTML = createHtml(o);   
4633         }
4634         
4635         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4636     },
4637
4638     /**
4639      * Creates a new Roo.DomHelper.Template from the Dom object spec
4640      * @param {Object} o The Dom object spec (and children)
4641      * @return {Roo.DomHelper.Template} The new template
4642      */
4643     createTemplate : function(o){
4644         var html = createHtml(o);
4645         return new Roo.Template(html);
4646     }
4647     };
4648 }();
4649 /*
4650  * Based on:
4651  * Ext JS Library 1.1.1
4652  * Copyright(c) 2006-2007, Ext JS, LLC.
4653  *
4654  * Originally Released Under LGPL - original licence link has changed is not relivant.
4655  *
4656  * Fork - LGPL
4657  * <script type="text/javascript">
4658  */
4659  
4660 /**
4661 * @class Roo.Template
4662 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
4663 * For a list of available format functions, see {@link Roo.util.Format}.<br />
4664 * Usage:
4665 <pre><code>
4666 var t = new Roo.Template({
4667     html :  '&lt;div name="{id}"&gt;' + 
4668         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
4669         '&lt;/div&gt;',
4670     myformat: function (value, allValues) {
4671         return 'XX' + value;
4672     }
4673 });
4674 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
4675 </code></pre>
4676 * For more information see this blog post with examples:
4677 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
4678      - Create Elements using DOM, HTML fragments and Templates</a>. 
4679 * @constructor
4680 * @param {Object} cfg - Configuration object.
4681 */
4682 Roo.Template = function(cfg){
4683     // BC!
4684     if(cfg instanceof Array){
4685         cfg = cfg.join("");
4686     }else if(arguments.length > 1){
4687         cfg = Array.prototype.join.call(arguments, "");
4688     }
4689     
4690     
4691     if (typeof(cfg) == 'object') {
4692         Roo.apply(this,cfg)
4693     } else {
4694         // bc
4695         this.html = cfg;
4696     }
4697     if (this.url) {
4698         this.load();
4699     }
4700     
4701 };
4702 Roo.Template.prototype = {
4703     
4704     /**
4705      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
4706      */
4707     onLoad : false,
4708     
4709     
4710     /**
4711      * @cfg {String} url  The Url to load the template from. beware if you are loading from a url, the data may not be ready if you use it instantly..
4712      *                    it should be fixed so that template is observable...
4713      */
4714     url : false,
4715     /**
4716      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
4717      */
4718     html : '',
4719     
4720     
4721     compiled : false,
4722     loaded : false,
4723     /**
4724      * Returns an HTML fragment of this template with the specified values applied.
4725      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4726      * @return {String} The HTML fragment
4727      */
4728     
4729    
4730     
4731     applyTemplate : function(values){
4732         //Roo.log(["applyTemplate", values]);
4733         try {
4734            
4735             if(this.compiled){
4736                 return this.compiled(values);
4737             }
4738             var useF = this.disableFormats !== true;
4739             var fm = Roo.util.Format, tpl = this;
4740             var fn = function(m, name, format, args){
4741                 if(format && useF){
4742                     if(format.substr(0, 5) == "this."){
4743                         return tpl.call(format.substr(5), values[name], values);
4744                     }else{
4745                         if(args){
4746                             // quoted values are required for strings in compiled templates, 
4747                             // but for non compiled we need to strip them
4748                             // quoted reversed for jsmin
4749                             var re = /^\s*['"](.*)["']\s*$/;
4750                             args = args.split(',');
4751                             for(var i = 0, len = args.length; i < len; i++){
4752                                 args[i] = args[i].replace(re, "$1");
4753                             }
4754                             args = [values[name]].concat(args);
4755                         }else{
4756                             args = [values[name]];
4757                         }
4758                         return fm[format].apply(fm, args);
4759                     }
4760                 }else{
4761                     return values[name] !== undefined ? values[name] : "";
4762                 }
4763             };
4764             return this.html.replace(this.re, fn);
4765         } catch (e) {
4766             Roo.log(e);
4767             throw e;
4768         }
4769          
4770     },
4771     
4772     loading : false,
4773       
4774     load : function ()
4775     {
4776          
4777         if (this.loading) {
4778             return;
4779         }
4780         var _t = this;
4781         
4782         this.loading = true;
4783         this.compiled = false;
4784         
4785         var cx = new Roo.data.Connection();
4786         cx.request({
4787             url : this.url,
4788             method : 'GET',
4789             success : function (response) {
4790                 _t.loading = false;
4791                 _t.url = false;
4792                 
4793                 _t.set(response.responseText,true);
4794                 _t.loaded = true;
4795                 if (_t.onLoad) {
4796                     _t.onLoad();
4797                 }
4798              },
4799             failure : function(response) {
4800                 Roo.log("Template failed to load from " + _t.url);
4801                 _t.loading = false;
4802             }
4803         });
4804     },
4805
4806     /**
4807      * Sets the HTML used as the template and optionally compiles it.
4808      * @param {String} html
4809      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
4810      * @return {Roo.Template} this
4811      */
4812     set : function(html, compile){
4813         this.html = html;
4814         this.compiled = false;
4815         if(compile){
4816             this.compile();
4817         }
4818         return this;
4819     },
4820     
4821     /**
4822      * True to disable format functions (defaults to false)
4823      * @type Boolean
4824      */
4825     disableFormats : false,
4826     
4827     /**
4828     * The regular expression used to match template variables 
4829     * @type RegExp
4830     * @property 
4831     */
4832     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
4833     
4834     /**
4835      * Compiles the template into an internal function, eliminating the RegEx overhead.
4836      * @return {Roo.Template} this
4837      */
4838     compile : function(){
4839         var fm = Roo.util.Format;
4840         var useF = this.disableFormats !== true;
4841         var sep = Roo.isGecko ? "+" : ",";
4842         var fn = function(m, name, format, args){
4843             if(format && useF){
4844                 args = args ? ',' + args : "";
4845                 if(format.substr(0, 5) != "this."){
4846                     format = "fm." + format + '(';
4847                 }else{
4848                     format = 'this.call("'+ format.substr(5) + '", ';
4849                     args = ", values";
4850                 }
4851             }else{
4852                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
4853             }
4854             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
4855         };
4856         var body;
4857         // branched to use + in gecko and [].join() in others
4858         if(Roo.isGecko){
4859             body = "this.compiled = function(values){ return '" +
4860                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
4861                     "';};";
4862         }else{
4863             body = ["this.compiled = function(values){ return ['"];
4864             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
4865             body.push("'].join('');};");
4866             body = body.join('');
4867         }
4868         /**
4869          * eval:var:values
4870          * eval:var:fm
4871          */
4872         eval(body);
4873         return this;
4874     },
4875     
4876     // private function used to call members
4877     call : function(fnName, value, allValues){
4878         return this[fnName](value, allValues);
4879     },
4880     
4881     /**
4882      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
4883      * @param {String/HTMLElement/Roo.Element} el The context element
4884      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4885      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4886      * @return {HTMLElement/Roo.Element} The new node or Element
4887      */
4888     insertFirst: function(el, values, returnElement){
4889         return this.doInsert('afterBegin', el, values, returnElement);
4890     },
4891
4892     /**
4893      * Applies the supplied values to the template and inserts the new node(s) before el.
4894      * @param {String/HTMLElement/Roo.Element} el The context element
4895      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4896      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4897      * @return {HTMLElement/Roo.Element} The new node or Element
4898      */
4899     insertBefore: function(el, values, returnElement){
4900         return this.doInsert('beforeBegin', el, values, returnElement);
4901     },
4902
4903     /**
4904      * Applies the supplied values to the template and inserts the new node(s) after el.
4905      * @param {String/HTMLElement/Roo.Element} el The context element
4906      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4907      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4908      * @return {HTMLElement/Roo.Element} The new node or Element
4909      */
4910     insertAfter : function(el, values, returnElement){
4911         return this.doInsert('afterEnd', el, values, returnElement);
4912     },
4913     
4914     /**
4915      * Applies the supplied values to the template and appends the new node(s) to el.
4916      * @param {String/HTMLElement/Roo.Element} el The context element
4917      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4918      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4919      * @return {HTMLElement/Roo.Element} The new node or Element
4920      */
4921     append : function(el, values, returnElement){
4922         return this.doInsert('beforeEnd', el, values, returnElement);
4923     },
4924
4925     doInsert : function(where, el, values, returnEl){
4926         el = Roo.getDom(el);
4927         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
4928         return returnEl ? Roo.get(newNode, true) : newNode;
4929     },
4930
4931     /**
4932      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
4933      * @param {String/HTMLElement/Roo.Element} el The context element
4934      * @param {Object} values The template values. Can be an array if your params are numeric (i.e. {0}) or an object (i.e. {foo: 'bar'})
4935      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
4936      * @return {HTMLElement/Roo.Element} The new node or Element
4937      */
4938     overwrite : function(el, values, returnElement){
4939         el = Roo.getDom(el);
4940         el.innerHTML = this.applyTemplate(values);
4941         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
4942     }
4943 };
4944 /**
4945  * Alias for {@link #applyTemplate}
4946  * @method
4947  */
4948 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
4949
4950 // backwards compat
4951 Roo.DomHelper.Template = Roo.Template;
4952
4953 /**
4954  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
4955  * @param {String/HTMLElement} el A DOM element or its id
4956  * @returns {Roo.Template} The created template
4957  * @static
4958  */
4959 Roo.Template.from = function(el){
4960     el = Roo.getDom(el);
4961     return new Roo.Template(el.value || el.innerHTML);
4962 };/*
4963  * Based on:
4964  * Ext JS Library 1.1.1
4965  * Copyright(c) 2006-2007, Ext JS, LLC.
4966  *
4967  * Originally Released Under LGPL - original licence link has changed is not relivant.
4968  *
4969  * Fork - LGPL
4970  * <script type="text/javascript">
4971  */
4972  
4973
4974 /*
4975  * This is code is also distributed under MIT license for use
4976  * with jQuery and prototype JavaScript libraries.
4977  */
4978 /**
4979  * @class Roo.DomQuery
4980 Provides high performance selector/xpath processing by compiling queries into reusable functions. New pseudo classes and matchers can be plugged. It works on HTML and XML documents (if a content node is passed in).
4981 <p>
4982 DomQuery supports most of the <a href="http://www.w3.org/TR/2005/WD-css3-selectors-20051215/">CSS3 selectors spec</a>, along with some custom selectors and basic XPath.</p>
4983
4984 <p>
4985 All selectors, attribute filters and pseudos below can be combined infinitely in any order. For example "div.foo:nth-child(odd)[@foo=bar].bar:first" would be a perfectly valid selector. Node filters are processed in the order in which they appear, which allows you to optimize your queries for your document structure.
4986 </p>
4987 <h4>Element Selectors:</h4>
4988 <ul class="list">
4989     <li> <b>*</b> any element</li>
4990     <li> <b>E</b> an element with the tag E</li>
4991     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
4992     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
4993     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
4994     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
4995 </ul>
4996 <h4>Attribute Selectors:</h4>
4997 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
4998 <ul class="list">
4999     <li> <b>E[foo]</b> has an attribute "foo"</li>
5000     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5001     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5002     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5003     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5004     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5005     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5006 </ul>
5007 <h4>Pseudo Classes:</h4>
5008 <ul class="list">
5009     <li> <b>E:first-child</b> E is the first child of its parent</li>
5010     <li> <b>E:last-child</b> E is the last child of its parent</li>
5011     <li> <b>E:nth-child(<i>n</i>)</b> E is the <i>n</i>th child of its parent (1 based as per the spec)</li>
5012     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5013     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5014     <li> <b>E:only-child</b> E is the only child of its parent</li>
5015     <li> <b>E:checked</b> E is an element that is has a checked attribute that is true (e.g. a radio or checkbox) </li>
5016     <li> <b>E:first</b> the first E in the resultset</li>
5017     <li> <b>E:last</b> the last E in the resultset</li>
5018     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5019     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5020     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5021     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5022     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5023     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5024     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5025     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5026     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5027 </ul>
5028 <h4>CSS Value Selectors:</h4>
5029 <ul class="list">
5030     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5031     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5032     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5033     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5034     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5035     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5036 </ul>
5037  * @singleton
5038  */
5039 Roo.DomQuery = function(){
5040     var cache = {}, simpleCache = {}, valueCache = {};
5041     var nonSpace = /\S/;
5042     var trimRe = /^\s+|\s+$/g;
5043     var tplRe = /\{(\d+)\}/g;
5044     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5045     var tagTokenRe = /^(#)?([\w-\*]+)/;
5046     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5047
5048     function child(p, index){
5049         var i = 0;
5050         var n = p.firstChild;
5051         while(n){
5052             if(n.nodeType == 1){
5053                if(++i == index){
5054                    return n;
5055                }
5056             }
5057             n = n.nextSibling;
5058         }
5059         return null;
5060     };
5061
5062     function next(n){
5063         while((n = n.nextSibling) && n.nodeType != 1);
5064         return n;
5065     };
5066
5067     function prev(n){
5068         while((n = n.previousSibling) && n.nodeType != 1);
5069         return n;
5070     };
5071
5072     function children(d){
5073         var n = d.firstChild, ni = -1;
5074             while(n){
5075                 var nx = n.nextSibling;
5076                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5077                     d.removeChild(n);
5078                 }else{
5079                     n.nodeIndex = ++ni;
5080                 }
5081                 n = nx;
5082             }
5083             return this;
5084         };
5085
5086     function byClassName(c, a, v){
5087         if(!v){
5088             return c;
5089         }
5090         var r = [], ri = -1, cn;
5091         for(var i = 0, ci; ci = c[i]; i++){
5092             
5093             
5094             if((' '+
5095                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5096                  +' ').indexOf(v) != -1){
5097                 r[++ri] = ci;
5098             }
5099         }
5100         return r;
5101     };
5102
5103     function attrValue(n, attr){
5104         if(!n.tagName && typeof n.length != "undefined"){
5105             n = n[0];
5106         }
5107         if(!n){
5108             return null;
5109         }
5110         if(attr == "for"){
5111             return n.htmlFor;
5112         }
5113         if(attr == "class" || attr == "className"){
5114             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5115         }
5116         return n.getAttribute(attr) || n[attr];
5117
5118     };
5119
5120     function getNodes(ns, mode, tagName){
5121         var result = [], ri = -1, cs;
5122         if(!ns){
5123             return result;
5124         }
5125         tagName = tagName || "*";
5126         if(typeof ns.getElementsByTagName != "undefined"){
5127             ns = [ns];
5128         }
5129         if(!mode){
5130             for(var i = 0, ni; ni = ns[i]; i++){
5131                 cs = ni.getElementsByTagName(tagName);
5132                 for(var j = 0, ci; ci = cs[j]; j++){
5133                     result[++ri] = ci;
5134                 }
5135             }
5136         }else if(mode == "/" || mode == ">"){
5137             var utag = tagName.toUpperCase();
5138             for(var i = 0, ni, cn; ni = ns[i]; i++){
5139                 cn = ni.children || ni.childNodes;
5140                 for(var j = 0, cj; cj = cn[j]; j++){
5141                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5142                         result[++ri] = cj;
5143                     }
5144                 }
5145             }
5146         }else if(mode == "+"){
5147             var utag = tagName.toUpperCase();
5148             for(var i = 0, n; n = ns[i]; i++){
5149                 while((n = n.nextSibling) && n.nodeType != 1);
5150                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5151                     result[++ri] = n;
5152                 }
5153             }
5154         }else if(mode == "~"){
5155             for(var i = 0, n; n = ns[i]; i++){
5156                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5157                 if(n){
5158                     result[++ri] = n;
5159                 }
5160             }
5161         }
5162         return result;
5163     };
5164
5165     function concat(a, b){
5166         if(b.slice){
5167             return a.concat(b);
5168         }
5169         for(var i = 0, l = b.length; i < l; i++){
5170             a[a.length] = b[i];
5171         }
5172         return a;
5173     }
5174
5175     function byTag(cs, tagName){
5176         if(cs.tagName || cs == document){
5177             cs = [cs];
5178         }
5179         if(!tagName){
5180             return cs;
5181         }
5182         var r = [], ri = -1;
5183         tagName = tagName.toLowerCase();
5184         for(var i = 0, ci; ci = cs[i]; i++){
5185             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5186                 r[++ri] = ci;
5187             }
5188         }
5189         return r;
5190     };
5191
5192     function byId(cs, attr, id){
5193         if(cs.tagName || cs == document){
5194             cs = [cs];
5195         }
5196         if(!id){
5197             return cs;
5198         }
5199         var r = [], ri = -1;
5200         for(var i = 0,ci; ci = cs[i]; i++){
5201             if(ci && ci.id == id){
5202                 r[++ri] = ci;
5203                 return r;
5204             }
5205         }
5206         return r;
5207     };
5208
5209     function byAttribute(cs, attr, value, op, custom){
5210         var r = [], ri = -1, st = custom=="{";
5211         var f = Roo.DomQuery.operators[op];
5212         for(var i = 0, ci; ci = cs[i]; i++){
5213             var a;
5214             if(st){
5215                 a = Roo.DomQuery.getStyle(ci, attr);
5216             }
5217             else if(attr == "class" || attr == "className"){
5218                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5219             }else if(attr == "for"){
5220                 a = ci.htmlFor;
5221             }else if(attr == "href"){
5222                 a = ci.getAttribute("href", 2);
5223             }else{
5224                 a = ci.getAttribute(attr);
5225             }
5226             if((f && f(a, value)) || (!f && a)){
5227                 r[++ri] = ci;
5228             }
5229         }
5230         return r;
5231     };
5232
5233     function byPseudo(cs, name, value){
5234         return Roo.DomQuery.pseudos[name](cs, value);
5235     };
5236
5237     // This is for IE MSXML which does not support expandos.
5238     // IE runs the same speed using setAttribute, however FF slows way down
5239     // and Safari completely fails so they need to continue to use expandos.
5240     var isIE = window.ActiveXObject ? true : false;
5241
5242     // this eval is stop the compressor from
5243     // renaming the variable to something shorter
5244     
5245     /** eval:var:batch */
5246     var batch = 30803; 
5247
5248     var key = 30803;
5249
5250     function nodupIEXml(cs){
5251         var d = ++key;
5252         cs[0].setAttribute("_nodup", d);
5253         var r = [cs[0]];
5254         for(var i = 1, len = cs.length; i < len; i++){
5255             var c = cs[i];
5256             if(!c.getAttribute("_nodup") != d){
5257                 c.setAttribute("_nodup", d);
5258                 r[r.length] = c;
5259             }
5260         }
5261         for(var i = 0, len = cs.length; i < len; i++){
5262             cs[i].removeAttribute("_nodup");
5263         }
5264         return r;
5265     }
5266
5267     function nodup(cs){
5268         if(!cs){
5269             return [];
5270         }
5271         var len = cs.length, c, i, r = cs, cj, ri = -1;
5272         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5273             return cs;
5274         }
5275         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5276             return nodupIEXml(cs);
5277         }
5278         var d = ++key;
5279         cs[0]._nodup = d;
5280         for(i = 1; c = cs[i]; i++){
5281             if(c._nodup != d){
5282                 c._nodup = d;
5283             }else{
5284                 r = [];
5285                 for(var j = 0; j < i; j++){
5286                     r[++ri] = cs[j];
5287                 }
5288                 for(j = i+1; cj = cs[j]; j++){
5289                     if(cj._nodup != d){
5290                         cj._nodup = d;
5291                         r[++ri] = cj;
5292                     }
5293                 }
5294                 return r;
5295             }
5296         }
5297         return r;
5298     }
5299
5300     function quickDiffIEXml(c1, c2){
5301         var d = ++key;
5302         for(var i = 0, len = c1.length; i < len; i++){
5303             c1[i].setAttribute("_qdiff", d);
5304         }
5305         var r = [];
5306         for(var i = 0, len = c2.length; i < len; i++){
5307             if(c2[i].getAttribute("_qdiff") != d){
5308                 r[r.length] = c2[i];
5309             }
5310         }
5311         for(var i = 0, len = c1.length; i < len; i++){
5312            c1[i].removeAttribute("_qdiff");
5313         }
5314         return r;
5315     }
5316
5317     function quickDiff(c1, c2){
5318         var len1 = c1.length;
5319         if(!len1){
5320             return c2;
5321         }
5322         if(isIE && c1[0].selectSingleNode){
5323             return quickDiffIEXml(c1, c2);
5324         }
5325         var d = ++key;
5326         for(var i = 0; i < len1; i++){
5327             c1[i]._qdiff = d;
5328         }
5329         var r = [];
5330         for(var i = 0, len = c2.length; i < len; i++){
5331             if(c2[i]._qdiff != d){
5332                 r[r.length] = c2[i];
5333             }
5334         }
5335         return r;
5336     }
5337
5338     function quickId(ns, mode, root, id){
5339         if(ns == root){
5340            var d = root.ownerDocument || root;
5341            return d.getElementById(id);
5342         }
5343         ns = getNodes(ns, mode, "*");
5344         return byId(ns, null, id);
5345     }
5346
5347     return {
5348         getStyle : function(el, name){
5349             return Roo.fly(el).getStyle(name);
5350         },
5351         /**
5352          * Compiles a selector/xpath query into a reusable function. The returned function
5353          * takes one parameter "root" (optional), which is the context node from where the query should start.
5354          * @param {String} selector The selector/xpath query
5355          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
5356          * @return {Function}
5357          */
5358         compile : function(path, type){
5359             type = type || "select";
5360             
5361             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
5362             var q = path, mode, lq;
5363             var tk = Roo.DomQuery.matchers;
5364             var tklen = tk.length;
5365             var mm;
5366
5367             // accept leading mode switch
5368             var lmode = q.match(modeRe);
5369             if(lmode && lmode[1]){
5370                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
5371                 q = q.replace(lmode[1], "");
5372             }
5373             // strip leading slashes
5374             while(path.substr(0, 1)=="/"){
5375                 path = path.substr(1);
5376             }
5377
5378             while(q && lq != q){
5379                 lq = q;
5380                 var tm = q.match(tagTokenRe);
5381                 if(type == "select"){
5382                     if(tm){
5383                         if(tm[1] == "#"){
5384                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
5385                         }else{
5386                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
5387                         }
5388                         q = q.replace(tm[0], "");
5389                     }else if(q.substr(0, 1) != '@'){
5390                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
5391                     }
5392                 }else{
5393                     if(tm){
5394                         if(tm[1] == "#"){
5395                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
5396                         }else{
5397                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
5398                         }
5399                         q = q.replace(tm[0], "");
5400                     }
5401                 }
5402                 while(!(mm = q.match(modeRe))){
5403                     var matched = false;
5404                     for(var j = 0; j < tklen; j++){
5405                         var t = tk[j];
5406                         var m = q.match(t.re);
5407                         if(m){
5408                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
5409                                                     return m[i];
5410                                                 });
5411                             q = q.replace(m[0], "");
5412                             matched = true;
5413                             break;
5414                         }
5415                     }
5416                     // prevent infinite loop on bad selector
5417                     if(!matched){
5418                         throw 'Error parsing selector, parsing failed at "' + q + '"';
5419                     }
5420                 }
5421                 if(mm[1]){
5422                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
5423                     q = q.replace(mm[1], "");
5424                 }
5425             }
5426             fn[fn.length] = "return nodup(n);\n}";
5427             
5428              /** 
5429               * list of variables that need from compression as they are used by eval.
5430              *  eval:var:batch 
5431              *  eval:var:nodup
5432              *  eval:var:byTag
5433              *  eval:var:ById
5434              *  eval:var:getNodes
5435              *  eval:var:quickId
5436              *  eval:var:mode
5437              *  eval:var:root
5438              *  eval:var:n
5439              *  eval:var:byClassName
5440              *  eval:var:byPseudo
5441              *  eval:var:byAttribute
5442              *  eval:var:attrValue
5443              * 
5444              **/ 
5445             eval(fn.join(""));
5446             return f;
5447         },
5448
5449         /**
5450          * Selects a group of elements.
5451          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
5452          * @param {Node} root (optional) The start of the query (defaults to document).
5453          * @return {Array}
5454          */
5455         select : function(path, root, type){
5456             if(!root || root == document){
5457                 root = document;
5458             }
5459             if(typeof root == "string"){
5460                 root = document.getElementById(root);
5461             }
5462             var paths = path.split(",");
5463             var results = [];
5464             for(var i = 0, len = paths.length; i < len; i++){
5465                 var p = paths[i].replace(trimRe, "");
5466                 if(!cache[p]){
5467                     cache[p] = Roo.DomQuery.compile(p);
5468                     if(!cache[p]){
5469                         throw p + " is not a valid selector";
5470                     }
5471                 }
5472                 var result = cache[p](root);
5473                 if(result && result != document){
5474                     results = results.concat(result);
5475                 }
5476             }
5477             if(paths.length > 1){
5478                 return nodup(results);
5479             }
5480             return results;
5481         },
5482
5483         /**
5484          * Selects a single element.
5485          * @param {String} selector The selector/xpath query
5486          * @param {Node} root (optional) The start of the query (defaults to document).
5487          * @return {Element}
5488          */
5489         selectNode : function(path, root){
5490             return Roo.DomQuery.select(path, root)[0];
5491         },
5492
5493         /**
5494          * Selects the value of a node, optionally replacing null with the defaultValue.
5495          * @param {String} selector The selector/xpath query
5496          * @param {Node} root (optional) The start of the query (defaults to document).
5497          * @param {String} defaultValue
5498          */
5499         selectValue : function(path, root, defaultValue){
5500             path = path.replace(trimRe, "");
5501             if(!valueCache[path]){
5502                 valueCache[path] = Roo.DomQuery.compile(path, "select");
5503             }
5504             var n = valueCache[path](root);
5505             n = n[0] ? n[0] : n;
5506             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
5507             return ((v === null||v === undefined||v==='') ? defaultValue : v);
5508         },
5509
5510         /**
5511          * Selects the value of a node, parsing integers and floats.
5512          * @param {String} selector The selector/xpath query
5513          * @param {Node} root (optional) The start of the query (defaults to document).
5514          * @param {Number} defaultValue
5515          * @return {Number}
5516          */
5517         selectNumber : function(path, root, defaultValue){
5518             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
5519             return parseFloat(v);
5520         },
5521
5522         /**
5523          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
5524          * @param {String/HTMLElement/Array} el An element id, element or array of elements
5525          * @param {String} selector The simple selector to test
5526          * @return {Boolean}
5527          */
5528         is : function(el, ss){
5529             if(typeof el == "string"){
5530                 el = document.getElementById(el);
5531             }
5532             var isArray = (el instanceof Array);
5533             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
5534             return isArray ? (result.length == el.length) : (result.length > 0);
5535         },
5536
5537         /**
5538          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
5539          * @param {Array} el An array of elements to filter
5540          * @param {String} selector The simple selector to test
5541          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
5542          * the selector instead of the ones that match
5543          * @return {Array}
5544          */
5545         filter : function(els, ss, nonMatches){
5546             ss = ss.replace(trimRe, "");
5547             if(!simpleCache[ss]){
5548                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
5549             }
5550             var result = simpleCache[ss](els);
5551             return nonMatches ? quickDiff(result, els) : result;
5552         },
5553
5554         /**
5555          * Collection of matching regular expressions and code snippets.
5556          */
5557         matchers : [{
5558                 re: /^\.([\w-]+)/,
5559                 select: 'n = byClassName(n, null, " {1} ");'
5560             }, {
5561                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
5562                 select: 'n = byPseudo(n, "{1}", "{2}");'
5563             },{
5564                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
5565                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
5566             }, {
5567                 re: /^#([\w-]+)/,
5568                 select: 'n = byId(n, null, "{1}");'
5569             },{
5570                 re: /^@([\w-]+)/,
5571                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
5572             }
5573         ],
5574
5575         /**
5576          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
5577          * New operators can be added as long as the match the format <i>c</i>= where <i>c</i> is any character other than space, &gt; &lt;.
5578          */
5579         operators : {
5580             "=" : function(a, v){
5581                 return a == v;
5582             },
5583             "!=" : function(a, v){
5584                 return a != v;
5585             },
5586             "^=" : function(a, v){
5587                 return a && a.substr(0, v.length) == v;
5588             },
5589             "$=" : function(a, v){
5590                 return a && a.substr(a.length-v.length) == v;
5591             },
5592             "*=" : function(a, v){
5593                 return a && a.indexOf(v) !== -1;
5594             },
5595             "%=" : function(a, v){
5596                 return (a % v) == 0;
5597             },
5598             "|=" : function(a, v){
5599                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
5600             },
5601             "~=" : function(a, v){
5602                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
5603             }
5604         },
5605
5606         /**
5607          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
5608          * and the argument (if any) supplied in the selector.
5609          */
5610         pseudos : {
5611             "first-child" : function(c){
5612                 var r = [], ri = -1, n;
5613                 for(var i = 0, ci; ci = n = c[i]; i++){
5614                     while((n = n.previousSibling) && n.nodeType != 1);
5615                     if(!n){
5616                         r[++ri] = ci;
5617                     }
5618                 }
5619                 return r;
5620             },
5621
5622             "last-child" : function(c){
5623                 var r = [], ri = -1, n;
5624                 for(var i = 0, ci; ci = n = c[i]; i++){
5625                     while((n = n.nextSibling) && n.nodeType != 1);
5626                     if(!n){
5627                         r[++ri] = ci;
5628                     }
5629                 }
5630                 return r;
5631             },
5632
5633             "nth-child" : function(c, a) {
5634                 var r = [], ri = -1;
5635                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
5636                 var f = (m[1] || 1) - 0, l = m[2] - 0;
5637                 for(var i = 0, n; n = c[i]; i++){
5638                     var pn = n.parentNode;
5639                     if (batch != pn._batch) {
5640                         var j = 0;
5641                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
5642                             if(cn.nodeType == 1){
5643                                cn.nodeIndex = ++j;
5644                             }
5645                         }
5646                         pn._batch = batch;
5647                     }
5648                     if (f == 1) {
5649                         if (l == 0 || n.nodeIndex == l){
5650                             r[++ri] = n;
5651                         }
5652                     } else if ((n.nodeIndex + l) % f == 0){
5653                         r[++ri] = n;
5654                     }
5655                 }
5656
5657                 return r;
5658             },
5659
5660             "only-child" : function(c){
5661                 var r = [], ri = -1;;
5662                 for(var i = 0, ci; ci = c[i]; i++){
5663                     if(!prev(ci) && !next(ci)){
5664                         r[++ri] = ci;
5665                     }
5666                 }
5667                 return r;
5668             },
5669
5670             "empty" : function(c){
5671                 var r = [], ri = -1;
5672                 for(var i = 0, ci; ci = c[i]; i++){
5673                     var cns = ci.childNodes, j = 0, cn, empty = true;
5674                     while(cn = cns[j]){
5675                         ++j;
5676                         if(cn.nodeType == 1 || cn.nodeType == 3){
5677                             empty = false;
5678                             break;
5679                         }
5680                     }
5681                     if(empty){
5682                         r[++ri] = ci;
5683                     }
5684                 }
5685                 return r;
5686             },
5687
5688             "contains" : function(c, v){
5689                 var r = [], ri = -1;
5690                 for(var i = 0, ci; ci = c[i]; i++){
5691                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
5692                         r[++ri] = ci;
5693                     }
5694                 }
5695                 return r;
5696             },
5697
5698             "nodeValue" : function(c, v){
5699                 var r = [], ri = -1;
5700                 for(var i = 0, ci; ci = c[i]; i++){
5701                     if(ci.firstChild && ci.firstChild.nodeValue == v){
5702                         r[++ri] = ci;
5703                     }
5704                 }
5705                 return r;
5706             },
5707
5708             "checked" : function(c){
5709                 var r = [], ri = -1;
5710                 for(var i = 0, ci; ci = c[i]; i++){
5711                     if(ci.checked == true){
5712                         r[++ri] = ci;
5713                     }
5714                 }
5715                 return r;
5716             },
5717
5718             "not" : function(c, ss){
5719                 return Roo.DomQuery.filter(c, ss, true);
5720             },
5721
5722             "odd" : function(c){
5723                 return this["nth-child"](c, "odd");
5724             },
5725
5726             "even" : function(c){
5727                 return this["nth-child"](c, "even");
5728             },
5729
5730             "nth" : function(c, a){
5731                 return c[a-1] || [];
5732             },
5733
5734             "first" : function(c){
5735                 return c[0] || [];
5736             },
5737
5738             "last" : function(c){
5739                 return c[c.length-1] || [];
5740             },
5741
5742             "has" : function(c, ss){
5743                 var s = Roo.DomQuery.select;
5744                 var r = [], ri = -1;
5745                 for(var i = 0, ci; ci = c[i]; i++){
5746                     if(s(ss, ci).length > 0){
5747                         r[++ri] = ci;
5748                     }
5749                 }
5750                 return r;
5751             },
5752
5753             "next" : function(c, ss){
5754                 var is = Roo.DomQuery.is;
5755                 var r = [], ri = -1;
5756                 for(var i = 0, ci; ci = c[i]; i++){
5757                     var n = next(ci);
5758                     if(n && is(n, ss)){
5759                         r[++ri] = ci;
5760                     }
5761                 }
5762                 return r;
5763             },
5764
5765             "prev" : function(c, ss){
5766                 var is = Roo.DomQuery.is;
5767                 var r = [], ri = -1;
5768                 for(var i = 0, ci; ci = c[i]; i++){
5769                     var n = prev(ci);
5770                     if(n && is(n, ss)){
5771                         r[++ri] = ci;
5772                     }
5773                 }
5774                 return r;
5775             }
5776         }
5777     };
5778 }();
5779
5780 /**
5781  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
5782  * @param {String} path The selector/xpath query
5783  * @param {Node} root (optional) The start of the query (defaults to document).
5784  * @return {Array}
5785  * @member Roo
5786  * @method query
5787  */
5788 Roo.query = Roo.DomQuery.select;
5789 /*
5790  * Based on:
5791  * Ext JS Library 1.1.1
5792  * Copyright(c) 2006-2007, Ext JS, LLC.
5793  *
5794  * Originally Released Under LGPL - original licence link has changed is not relivant.
5795  *
5796  * Fork - LGPL
5797  * <script type="text/javascript">
5798  */
5799
5800 /**
5801  * @class Roo.util.Observable
5802  * Base class that provides a common interface for publishing events. Subclasses are expected to
5803  * to have a property "events" with all the events defined.<br>
5804  * For example:
5805  * <pre><code>
5806  Employee = function(name){
5807     this.name = name;
5808     this.addEvents({
5809         "fired" : true,
5810         "quit" : true
5811     });
5812  }
5813  Roo.extend(Employee, Roo.util.Observable);
5814 </code></pre>
5815  * @param {Object} config properties to use (incuding events / listeners)
5816  */
5817
5818 Roo.util.Observable = function(cfg){
5819     
5820     cfg = cfg|| {};
5821     this.addEvents(cfg.events || {});
5822     if (cfg.events) {
5823         delete cfg.events; // make sure
5824     }
5825      
5826     Roo.apply(this, cfg);
5827     
5828     if(this.listeners){
5829         this.on(this.listeners);
5830         delete this.listeners;
5831     }
5832 };
5833 Roo.util.Observable.prototype = {
5834     /** 
5835  * @cfg {Object} listeners  list of events and functions to call for this object, 
5836  * For example :
5837  * <pre><code>
5838     listeners :  { 
5839        'click' : function(e) {
5840            ..... 
5841         } ,
5842         .... 
5843     } 
5844   </code></pre>
5845  */
5846     
5847     
5848     /**
5849      * Fires the specified event with the passed parameters (minus the event name).
5850      * @param {String} eventName
5851      * @param {Object...} args Variable number of parameters are passed to handlers
5852      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
5853      */
5854     fireEvent : function(){
5855         var ce = this.events[arguments[0].toLowerCase()];
5856         if(typeof ce == "object"){
5857             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
5858         }else{
5859             return true;
5860         }
5861     },
5862
5863     // private
5864     filterOptRe : /^(?:scope|delay|buffer|single)$/,
5865
5866     /**
5867      * Appends an event handler to this component
5868      * @param {String}   eventName The type of event to listen for
5869      * @param {Function} handler The method the event invokes
5870      * @param {Object}   scope (optional) The scope in which to execute the handler
5871      * function. The handler function's "this" context.
5872      * @param {Object}   options (optional) An object containing handler configuration
5873      * properties. This may contain any of the following properties:<ul>
5874      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
5875      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
5876      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
5877      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
5878      * by the specified number of milliseconds. If the event fires again within that time, the original
5879      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
5880      * </ul><br>
5881      * <p>
5882      * <b>Combining Options</b><br>
5883      * Using the options argument, it is possible to combine different types of listeners:<br>
5884      * <br>
5885      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
5886                 <pre><code>
5887                 el.on('click', this.onClick, this, {
5888                         single: true,
5889                 delay: 100,
5890                 forumId: 4
5891                 });
5892                 </code></pre>
5893      * <p>
5894      * <b>Attaching multiple handlers in 1 call</b><br>
5895      * The method also allows for a single argument to be passed which is a config object containing properties
5896      * which specify multiple handlers.
5897      * <pre><code>
5898                 el.on({
5899                         'click': {
5900                         fn: this.onClick,
5901                         scope: this,
5902                         delay: 100
5903                 }, 
5904                 'mouseover': {
5905                         fn: this.onMouseOver,
5906                         scope: this
5907                 },
5908                 'mouseout': {
5909                         fn: this.onMouseOut,
5910                         scope: this
5911                 }
5912                 });
5913                 </code></pre>
5914      * <p>
5915      * Or a shorthand syntax which passes the same scope object to all handlers:
5916         <pre><code>
5917                 el.on({
5918                         'click': this.onClick,
5919                 'mouseover': this.onMouseOver,
5920                 'mouseout': this.onMouseOut,
5921                 scope: this
5922                 });
5923                 </code></pre>
5924      */
5925     addListener : function(eventName, fn, scope, o){
5926         if(typeof eventName == "object"){
5927             o = eventName;
5928             for(var e in o){
5929                 if(this.filterOptRe.test(e)){
5930                     continue;
5931                 }
5932                 if(typeof o[e] == "function"){
5933                     // shared options
5934                     this.addListener(e, o[e], o.scope,  o);
5935                 }else{
5936                     // individual options
5937                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
5938                 }
5939             }
5940             return;
5941         }
5942         o = (!o || typeof o == "boolean") ? {} : o;
5943         eventName = eventName.toLowerCase();
5944         var ce = this.events[eventName] || true;
5945         if(typeof ce == "boolean"){
5946             ce = new Roo.util.Event(this, eventName);
5947             this.events[eventName] = ce;
5948         }
5949         ce.addListener(fn, scope, o);
5950     },
5951
5952     /**
5953      * Removes a listener
5954      * @param {String}   eventName     The type of event to listen for
5955      * @param {Function} handler        The handler to remove
5956      * @param {Object}   scope  (optional) The scope (this object) for the handler
5957      */
5958     removeListener : function(eventName, fn, scope){
5959         var ce = this.events[eventName.toLowerCase()];
5960         if(typeof ce == "object"){
5961             ce.removeListener(fn, scope);
5962         }
5963     },
5964
5965     /**
5966      * Removes all listeners for this object
5967      */
5968     purgeListeners : function(){
5969         for(var evt in this.events){
5970             if(typeof this.events[evt] == "object"){
5971                  this.events[evt].clearListeners();
5972             }
5973         }
5974     },
5975
5976     relayEvents : function(o, events){
5977         var createHandler = function(ename){
5978             return function(){
5979                  
5980                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
5981             };
5982         };
5983         for(var i = 0, len = events.length; i < len; i++){
5984             var ename = events[i];
5985             if(!this.events[ename]){
5986                 this.events[ename] = true;
5987             };
5988             o.on(ename, createHandler(ename), this);
5989         }
5990     },
5991
5992     /**
5993      * Used to define events on this Observable
5994      * @param {Object} object The object with the events defined
5995      */
5996     addEvents : function(o){
5997         if(!this.events){
5998             this.events = {};
5999         }
6000         Roo.applyIf(this.events, o);
6001     },
6002
6003     /**
6004      * Checks to see if this object has any listeners for a specified event
6005      * @param {String} eventName The name of the event to check for
6006      * @return {Boolean} True if the event is being listened for, else false
6007      */
6008     hasListener : function(eventName){
6009         var e = this.events[eventName];
6010         return typeof e == "object" && e.listeners.length > 0;
6011     }
6012 };
6013 /**
6014  * Appends an event handler to this element (shorthand for addListener)
6015  * @param {String}   eventName     The type of event to listen for
6016  * @param {Function} handler        The method the event invokes
6017  * @param {Object}   scope (optional) The scope in which to execute the handler
6018  * function. The handler function's "this" context.
6019  * @param {Object}   options  (optional)
6020  * @method
6021  */
6022 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6023 /**
6024  * Removes a listener (shorthand for removeListener)
6025  * @param {String}   eventName     The type of event to listen for
6026  * @param {Function} handler        The handler to remove
6027  * @param {Object}   scope  (optional) The scope (this object) for the handler
6028  * @method
6029  */
6030 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6031
6032 /**
6033  * Starts capture on the specified Observable. All events will be passed
6034  * to the supplied function with the event name + standard signature of the event
6035  * <b>before</b> the event is fired. If the supplied function returns false,
6036  * the event will not fire.
6037  * @param {Observable} o The Observable to capture
6038  * @param {Function} fn The function to call
6039  * @param {Object} scope (optional) The scope (this object) for the fn
6040  * @static
6041  */
6042 Roo.util.Observable.capture = function(o, fn, scope){
6043     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6044 };
6045
6046 /**
6047  * Removes <b>all</b> added captures from the Observable.
6048  * @param {Observable} o The Observable to release
6049  * @static
6050  */
6051 Roo.util.Observable.releaseCapture = function(o){
6052     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6053 };
6054
6055 (function(){
6056
6057     var createBuffered = function(h, o, scope){
6058         var task = new Roo.util.DelayedTask();
6059         return function(){
6060             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6061         };
6062     };
6063
6064     var createSingle = function(h, e, fn, scope){
6065         return function(){
6066             e.removeListener(fn, scope);
6067             return h.apply(scope, arguments);
6068         };
6069     };
6070
6071     var createDelayed = function(h, o, scope){
6072         return function(){
6073             var args = Array.prototype.slice.call(arguments, 0);
6074             setTimeout(function(){
6075                 h.apply(scope, args);
6076             }, o.delay || 10);
6077         };
6078     };
6079
6080     Roo.util.Event = function(obj, name){
6081         this.name = name;
6082         this.obj = obj;
6083         this.listeners = [];
6084     };
6085
6086     Roo.util.Event.prototype = {
6087         addListener : function(fn, scope, options){
6088             var o = options || {};
6089             scope = scope || this.obj;
6090             if(!this.isListening(fn, scope)){
6091                 var l = {fn: fn, scope: scope, options: o};
6092                 var h = fn;
6093                 if(o.delay){
6094                     h = createDelayed(h, o, scope);
6095                 }
6096                 if(o.single){
6097                     h = createSingle(h, this, fn, scope);
6098                 }
6099                 if(o.buffer){
6100                     h = createBuffered(h, o, scope);
6101                 }
6102                 l.fireFn = h;
6103                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6104                     this.listeners.push(l);
6105                 }else{
6106                     this.listeners = this.listeners.slice(0);
6107                     this.listeners.push(l);
6108                 }
6109             }
6110         },
6111
6112         findListener : function(fn, scope){
6113             scope = scope || this.obj;
6114             var ls = this.listeners;
6115             for(var i = 0, len = ls.length; i < len; i++){
6116                 var l = ls[i];
6117                 if(l.fn == fn && l.scope == scope){
6118                     return i;
6119                 }
6120             }
6121             return -1;
6122         },
6123
6124         isListening : function(fn, scope){
6125             return this.findListener(fn, scope) != -1;
6126         },
6127
6128         removeListener : function(fn, scope){
6129             var index;
6130             if((index = this.findListener(fn, scope)) != -1){
6131                 if(!this.firing){
6132                     this.listeners.splice(index, 1);
6133                 }else{
6134                     this.listeners = this.listeners.slice(0);
6135                     this.listeners.splice(index, 1);
6136                 }
6137                 return true;
6138             }
6139             return false;
6140         },
6141
6142         clearListeners : function(){
6143             this.listeners = [];
6144         },
6145
6146         fire : function(){
6147             var ls = this.listeners, scope, len = ls.length;
6148             if(len > 0){
6149                 this.firing = true;
6150                 var args = Array.prototype.slice.call(arguments, 0);                
6151                 for(var i = 0; i < len; i++){
6152                     var l = ls[i];
6153                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6154                         this.firing = false;
6155                         return false;
6156                     }
6157                 }
6158                 this.firing = false;
6159             }
6160             return true;
6161         }
6162     };
6163 })();/*
6164  * RooJS Library 
6165  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6166  *
6167  * Licence LGPL 
6168  *
6169  */
6170  
6171 /**
6172  * @class Roo.Document
6173  * @extends Roo.util.Observable
6174  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6175  * 
6176  * @param {Object} config the methods and properties of the 'base' class for the application.
6177  * 
6178  *  Generic Page handler - implement this to start your app..
6179  * 
6180  * eg.
6181  *  MyProject = new Roo.Document({
6182         events : {
6183             'load' : true // your events..
6184         },
6185         listeners : {
6186             'ready' : function() {
6187                 // fired on Roo.onReady()
6188             }
6189         }
6190  * 
6191  */
6192 Roo.Document = function(cfg) {
6193      
6194     this.addEvents({ 
6195         'ready' : true
6196     });
6197     Roo.util.Observable.call(this,cfg);
6198     
6199     var _this = this;
6200     
6201     Roo.onReady(function() {
6202         _this.fireEvent('ready');
6203     },null,false);
6204     
6205     
6206 }
6207
6208 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6209  * Based on:
6210  * Ext JS Library 1.1.1
6211  * Copyright(c) 2006-2007, Ext JS, LLC.
6212  *
6213  * Originally Released Under LGPL - original licence link has changed is not relivant.
6214  *
6215  * Fork - LGPL
6216  * <script type="text/javascript">
6217  */
6218
6219 /**
6220  * @class Roo.EventManager
6221  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6222  * several useful events directly.
6223  * See {@link Roo.EventObject} for more details on normalized event objects.
6224  * @singleton
6225  */
6226 Roo.EventManager = function(){
6227     var docReadyEvent, docReadyProcId, docReadyState = false;
6228     var resizeEvent, resizeTask, textEvent, textSize;
6229     var E = Roo.lib.Event;
6230     var D = Roo.lib.Dom;
6231
6232     
6233     
6234
6235     var fireDocReady = function(){
6236         if(!docReadyState){
6237             docReadyState = true;
6238             Roo.isReady = true;
6239             if(docReadyProcId){
6240                 clearInterval(docReadyProcId);
6241             }
6242             if(Roo.isGecko || Roo.isOpera) {
6243                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6244             }
6245             if(Roo.isIE){
6246                 var defer = document.getElementById("ie-deferred-loader");
6247                 if(defer){
6248                     defer.onreadystatechange = null;
6249                     defer.parentNode.removeChild(defer);
6250                 }
6251             }
6252             if(docReadyEvent){
6253                 docReadyEvent.fire();
6254                 docReadyEvent.clearListeners();
6255             }
6256         }
6257     };
6258     
6259     var initDocReady = function(){
6260         docReadyEvent = new Roo.util.Event();
6261         if(Roo.isGecko || Roo.isOpera) {
6262             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6263         }else if(Roo.isIE){
6264             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6265             var defer = document.getElementById("ie-deferred-loader");
6266             defer.onreadystatechange = function(){
6267                 if(this.readyState == "complete"){
6268                     fireDocReady();
6269                 }
6270             };
6271         }else if(Roo.isSafari){ 
6272             docReadyProcId = setInterval(function(){
6273                 var rs = document.readyState;
6274                 if(rs == "complete") {
6275                     fireDocReady();     
6276                  }
6277             }, 10);
6278         }
6279         // no matter what, make sure it fires on load
6280         E.on(window, "load", fireDocReady);
6281     };
6282
6283     var createBuffered = function(h, o){
6284         var task = new Roo.util.DelayedTask(h);
6285         return function(e){
6286             // create new event object impl so new events don't wipe out properties
6287             e = new Roo.EventObjectImpl(e);
6288             task.delay(o.buffer, h, null, [e]);
6289         };
6290     };
6291
6292     var createSingle = function(h, el, ename, fn){
6293         return function(e){
6294             Roo.EventManager.removeListener(el, ename, fn);
6295             h(e);
6296         };
6297     };
6298
6299     var createDelayed = function(h, o){
6300         return function(e){
6301             // create new event object impl so new events don't wipe out properties
6302             e = new Roo.EventObjectImpl(e);
6303             setTimeout(function(){
6304                 h(e);
6305             }, o.delay || 10);
6306         };
6307     };
6308     var transitionEndVal = false;
6309     
6310     var transitionEnd = function()
6311     {
6312         if (transitionEndVal) {
6313             return transitionEndVal;
6314         }
6315         var el = document.createElement('div');
6316
6317         var transEndEventNames = {
6318             WebkitTransition : 'webkitTransitionEnd',
6319             MozTransition    : 'transitionend',
6320             OTransition      : 'oTransitionEnd otransitionend',
6321             transition       : 'transitionend'
6322         };
6323     
6324         for (var name in transEndEventNames) {
6325             if (el.style[name] !== undefined) {
6326                 transitionEndVal = transEndEventNames[name];
6327                 return  transitionEndVal ;
6328             }
6329         }
6330     }
6331     
6332   
6333
6334     var listen = function(element, ename, opt, fn, scope)
6335     {
6336         var o = (!opt || typeof opt == "boolean") ? {} : opt;
6337         fn = fn || o.fn; scope = scope || o.scope;
6338         var el = Roo.getDom(element);
6339         
6340         
6341         if(!el){
6342             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
6343         }
6344         
6345         if (ename == 'transitionend') {
6346             ename = transitionEnd();
6347         }
6348         var h = function(e){
6349             e = Roo.EventObject.setEvent(e);
6350             var t;
6351             if(o.delegate){
6352                 t = e.getTarget(o.delegate, el);
6353                 if(!t){
6354                     return;
6355                 }
6356             }else{
6357                 t = e.target;
6358             }
6359             if(o.stopEvent === true){
6360                 e.stopEvent();
6361             }
6362             if(o.preventDefault === true){
6363                e.preventDefault();
6364             }
6365             if(o.stopPropagation === true){
6366                 e.stopPropagation();
6367             }
6368
6369             if(o.normalized === false){
6370                 e = e.browserEvent;
6371             }
6372
6373             fn.call(scope || el, e, t, o);
6374         };
6375         if(o.delay){
6376             h = createDelayed(h, o);
6377         }
6378         if(o.single){
6379             h = createSingle(h, el, ename, fn);
6380         }
6381         if(o.buffer){
6382             h = createBuffered(h, o);
6383         }
6384         
6385         fn._handlers = fn._handlers || [];
6386         
6387         
6388         fn._handlers.push([Roo.id(el), ename, h]);
6389         
6390         
6391          
6392         E.on(el, ename, h); // this adds the actuall listener to the object..
6393         
6394         
6395         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
6396             el.addEventListener("DOMMouseScroll", h, false);
6397             E.on(window, 'unload', function(){
6398                 el.removeEventListener("DOMMouseScroll", h, false);
6399             });
6400         }
6401         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6402             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
6403         }
6404         return h;
6405     };
6406
6407     var stopListening = function(el, ename, fn){
6408         var id = Roo.id(el), hds = fn._handlers, hd = fn;
6409         if(hds){
6410             for(var i = 0, len = hds.length; i < len; i++){
6411                 var h = hds[i];
6412                 if(h[0] == id && h[1] == ename){
6413                     hd = h[2];
6414                     hds.splice(i, 1);
6415                     break;
6416                 }
6417             }
6418         }
6419         E.un(el, ename, hd);
6420         el = Roo.getDom(el);
6421         if(ename == "mousewheel" && el.addEventListener){
6422             el.removeEventListener("DOMMouseScroll", hd, false);
6423         }
6424         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
6425             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
6426         }
6427     };
6428
6429     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
6430     
6431     var pub = {
6432         
6433         
6434         /** 
6435          * Fix for doc tools
6436          * @scope Roo.EventManager
6437          */
6438         
6439         
6440         /** 
6441          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
6442          * object with a Roo.EventObject
6443          * @param {Function} fn        The method the event invokes
6444          * @param {Object}   scope    An object that becomes the scope of the handler
6445          * @param {boolean}  override If true, the obj passed in becomes
6446          *                             the execution scope of the listener
6447          * @return {Function} The wrapped function
6448          * @deprecated
6449          */
6450         wrap : function(fn, scope, override){
6451             return function(e){
6452                 Roo.EventObject.setEvent(e);
6453                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
6454             };
6455         },
6456         
6457         /**
6458      * Appends an event handler to an element (shorthand for addListener)
6459      * @param {String/HTMLElement}   element        The html element or id to assign the
6460      * @param {String}   eventName The type of event to listen for
6461      * @param {Function} handler The method the event invokes
6462      * @param {Object}   scope (optional) The scope in which to execute the handler
6463      * function. The handler function's "this" context.
6464      * @param {Object}   options (optional) An object containing handler configuration
6465      * properties. This may contain any of the following properties:<ul>
6466      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6467      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6468      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6469      * <li>preventDefault {Boolean} True to prevent the default action</li>
6470      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6471      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6472      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6473      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6474      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6475      * by the specified number of milliseconds. If the event fires again within that time, the original
6476      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6477      * </ul><br>
6478      * <p>
6479      * <b>Combining Options</b><br>
6480      * Using the options argument, it is possible to combine different types of listeners:<br>
6481      * <br>
6482      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6483      * Code:<pre><code>
6484 el.on('click', this.onClick, this, {
6485     single: true,
6486     delay: 100,
6487     stopEvent : true,
6488     forumId: 4
6489 });</code></pre>
6490      * <p>
6491      * <b>Attaching multiple handlers in 1 call</b><br>
6492       * The method also allows for a single argument to be passed which is a config object containing properties
6493      * which specify multiple handlers.
6494      * <p>
6495      * Code:<pre><code>
6496 el.on({
6497     'click' : {
6498         fn: this.onClick
6499         scope: this,
6500         delay: 100
6501     },
6502     'mouseover' : {
6503         fn: this.onMouseOver
6504         scope: this
6505     },
6506     'mouseout' : {
6507         fn: this.onMouseOut
6508         scope: this
6509     }
6510 });</code></pre>
6511      * <p>
6512      * Or a shorthand syntax:<br>
6513      * Code:<pre><code>
6514 el.on({
6515     'click' : this.onClick,
6516     'mouseover' : this.onMouseOver,
6517     'mouseout' : this.onMouseOut
6518     scope: this
6519 });</code></pre>
6520      */
6521         addListener : function(element, eventName, fn, scope, options){
6522             if(typeof eventName == "object"){
6523                 var o = eventName;
6524                 for(var e in o){
6525                     if(propRe.test(e)){
6526                         continue;
6527                     }
6528                     if(typeof o[e] == "function"){
6529                         // shared options
6530                         listen(element, e, o, o[e], o.scope);
6531                     }else{
6532                         // individual options
6533                         listen(element, e, o[e]);
6534                     }
6535                 }
6536                 return;
6537             }
6538             return listen(element, eventName, options, fn, scope);
6539         },
6540         
6541         /**
6542          * Removes an event handler
6543          *
6544          * @param {String/HTMLElement}   element        The id or html element to remove the 
6545          *                             event from
6546          * @param {String}   eventName     The type of event
6547          * @param {Function} fn
6548          * @return {Boolean} True if a listener was actually removed
6549          */
6550         removeListener : function(element, eventName, fn){
6551             return stopListening(element, eventName, fn);
6552         },
6553         
6554         /**
6555          * Fires when the document is ready (before onload and before images are loaded). Can be 
6556          * accessed shorthanded Roo.onReady().
6557          * @param {Function} fn        The method the event invokes
6558          * @param {Object}   scope    An  object that becomes the scope of the handler
6559          * @param {boolean}  options
6560          */
6561         onDocumentReady : function(fn, scope, options){
6562             if(docReadyState){ // if it already fired
6563                 docReadyEvent.addListener(fn, scope, options);
6564                 docReadyEvent.fire();
6565                 docReadyEvent.clearListeners();
6566                 return;
6567             }
6568             if(!docReadyEvent){
6569                 initDocReady();
6570             }
6571             docReadyEvent.addListener(fn, scope, options);
6572         },
6573         
6574         /**
6575          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
6576          * @param {Function} fn        The method the event invokes
6577          * @param {Object}   scope    An object that becomes the scope of the handler
6578          * @param {boolean}  options
6579          */
6580         onWindowResize : function(fn, scope, options){
6581             if(!resizeEvent){
6582                 resizeEvent = new Roo.util.Event();
6583                 resizeTask = new Roo.util.DelayedTask(function(){
6584                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6585                 });
6586                 E.on(window, "resize", function(){
6587                     if(Roo.isIE){
6588                         resizeTask.delay(50);
6589                     }else{
6590                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6591                     }
6592                 });
6593             }
6594             resizeEvent.addListener(fn, scope, options);
6595         },
6596
6597         /**
6598          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
6599          * @param {Function} fn        The method the event invokes
6600          * @param {Object}   scope    An object that becomes the scope of the handler
6601          * @param {boolean}  options
6602          */
6603         onTextResize : function(fn, scope, options){
6604             if(!textEvent){
6605                 textEvent = new Roo.util.Event();
6606                 var textEl = new Roo.Element(document.createElement('div'));
6607                 textEl.dom.className = 'x-text-resize';
6608                 textEl.dom.innerHTML = 'X';
6609                 textEl.appendTo(document.body);
6610                 textSize = textEl.dom.offsetHeight;
6611                 setInterval(function(){
6612                     if(textEl.dom.offsetHeight != textSize){
6613                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
6614                     }
6615                 }, this.textResizeInterval);
6616             }
6617             textEvent.addListener(fn, scope, options);
6618         },
6619
6620         /**
6621          * Removes the passed window resize listener.
6622          * @param {Function} fn        The method the event invokes
6623          * @param {Object}   scope    The scope of handler
6624          */
6625         removeResizeListener : function(fn, scope){
6626             if(resizeEvent){
6627                 resizeEvent.removeListener(fn, scope);
6628             }
6629         },
6630
6631         // private
6632         fireResize : function(){
6633             if(resizeEvent){
6634                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
6635             }   
6636         },
6637         /**
6638          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
6639          */
6640         ieDeferSrc : false,
6641         /**
6642          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
6643          */
6644         textResizeInterval : 50
6645     };
6646     
6647     /**
6648      * Fix for doc tools
6649      * @scopeAlias pub=Roo.EventManager
6650      */
6651     
6652      /**
6653      * Appends an event handler to an element (shorthand for addListener)
6654      * @param {String/HTMLElement}   element        The html element or id to assign the
6655      * @param {String}   eventName The type of event to listen for
6656      * @param {Function} handler The method the event invokes
6657      * @param {Object}   scope (optional) The scope in which to execute the handler
6658      * function. The handler function's "this" context.
6659      * @param {Object}   options (optional) An object containing handler configuration
6660      * properties. This may contain any of the following properties:<ul>
6661      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6662      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
6663      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
6664      * <li>preventDefault {Boolean} True to prevent the default action</li>
6665      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
6666      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
6667      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6668      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6669      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6670      * by the specified number of milliseconds. If the event fires again within that time, the original
6671      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6672      * </ul><br>
6673      * <p>
6674      * <b>Combining Options</b><br>
6675      * Using the options argument, it is possible to combine different types of listeners:<br>
6676      * <br>
6677      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
6678      * Code:<pre><code>
6679 el.on('click', this.onClick, this, {
6680     single: true,
6681     delay: 100,
6682     stopEvent : true,
6683     forumId: 4
6684 });</code></pre>
6685      * <p>
6686      * <b>Attaching multiple handlers in 1 call</b><br>
6687       * The method also allows for a single argument to be passed which is a config object containing properties
6688      * which specify multiple handlers.
6689      * <p>
6690      * Code:<pre><code>
6691 el.on({
6692     'click' : {
6693         fn: this.onClick
6694         scope: this,
6695         delay: 100
6696     },
6697     'mouseover' : {
6698         fn: this.onMouseOver
6699         scope: this
6700     },
6701     'mouseout' : {
6702         fn: this.onMouseOut
6703         scope: this
6704     }
6705 });</code></pre>
6706      * <p>
6707      * Or a shorthand syntax:<br>
6708      * Code:<pre><code>
6709 el.on({
6710     'click' : this.onClick,
6711     'mouseover' : this.onMouseOver,
6712     'mouseout' : this.onMouseOut
6713     scope: this
6714 });</code></pre>
6715      */
6716     pub.on = pub.addListener;
6717     pub.un = pub.removeListener;
6718
6719     pub.stoppedMouseDownEvent = new Roo.util.Event();
6720     return pub;
6721 }();
6722 /**
6723   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
6724   * @param {Function} fn        The method the event invokes
6725   * @param {Object}   scope    An  object that becomes the scope of the handler
6726   * @param {boolean}  override If true, the obj passed in becomes
6727   *                             the execution scope of the listener
6728   * @member Roo
6729   * @method onReady
6730  */
6731 Roo.onReady = Roo.EventManager.onDocumentReady;
6732
6733 Roo.onReady(function(){
6734     var bd = Roo.get(document.body);
6735     if(!bd){ return; }
6736
6737     var cls = [
6738             Roo.isIE ? "roo-ie"
6739             : Roo.isIE11 ? "roo-ie11"
6740             : Roo.isEdge ? "roo-edge"
6741             : Roo.isGecko ? "roo-gecko"
6742             : Roo.isOpera ? "roo-opera"
6743             : Roo.isSafari ? "roo-safari" : ""];
6744
6745     if(Roo.isMac){
6746         cls.push("roo-mac");
6747     }
6748     if(Roo.isLinux){
6749         cls.push("roo-linux");
6750     }
6751     if(Roo.isIOS){
6752         cls.push("roo-ios");
6753     }
6754     if(Roo.isTouch){
6755         cls.push("roo-touch");
6756     }
6757     if(Roo.isBorderBox){
6758         cls.push('roo-border-box');
6759     }
6760     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
6761         var p = bd.dom.parentNode;
6762         if(p){
6763             p.className += ' roo-strict';
6764         }
6765     }
6766     bd.addClass(cls.join(' '));
6767 });
6768
6769 /**
6770  * @class Roo.EventObject
6771  * EventObject exposes the Yahoo! UI Event functionality directly on the object
6772  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
6773  * Example:
6774  * <pre><code>
6775  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
6776     e.preventDefault();
6777     var target = e.getTarget();
6778     ...
6779  }
6780  var myDiv = Roo.get("myDiv");
6781  myDiv.on("click", handleClick);
6782  //or
6783  Roo.EventManager.on("myDiv", 'click', handleClick);
6784  Roo.EventManager.addListener("myDiv", 'click', handleClick);
6785  </code></pre>
6786  * @singleton
6787  */
6788 Roo.EventObject = function(){
6789     
6790     var E = Roo.lib.Event;
6791     
6792     // safari keypress events for special keys return bad keycodes
6793     var safariKeys = {
6794         63234 : 37, // left
6795         63235 : 39, // right
6796         63232 : 38, // up
6797         63233 : 40, // down
6798         63276 : 33, // page up
6799         63277 : 34, // page down
6800         63272 : 46, // delete
6801         63273 : 36, // home
6802         63275 : 35  // end
6803     };
6804
6805     // normalize button clicks
6806     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
6807                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
6808
6809     Roo.EventObjectImpl = function(e){
6810         if(e){
6811             this.setEvent(e.browserEvent || e);
6812         }
6813     };
6814     Roo.EventObjectImpl.prototype = {
6815         /**
6816          * Used to fix doc tools.
6817          * @scope Roo.EventObject.prototype
6818          */
6819             
6820
6821         
6822         
6823         /** The normal browser event */
6824         browserEvent : null,
6825         /** The button pressed in a mouse event */
6826         button : -1,
6827         /** True if the shift key was down during the event */
6828         shiftKey : false,
6829         /** True if the control key was down during the event */
6830         ctrlKey : false,
6831         /** True if the alt key was down during the event */
6832         altKey : false,
6833
6834         /** Key constant 
6835         * @type Number */
6836         BACKSPACE : 8,
6837         /** Key constant 
6838         * @type Number */
6839         TAB : 9,
6840         /** Key constant 
6841         * @type Number */
6842         RETURN : 13,
6843         /** Key constant 
6844         * @type Number */
6845         ENTER : 13,
6846         /** Key constant 
6847         * @type Number */
6848         SHIFT : 16,
6849         /** Key constant 
6850         * @type Number */
6851         CONTROL : 17,
6852         /** Key constant 
6853         * @type Number */
6854         ESC : 27,
6855         /** Key constant 
6856         * @type Number */
6857         SPACE : 32,
6858         /** Key constant 
6859         * @type Number */
6860         PAGEUP : 33,
6861         /** Key constant 
6862         * @type Number */
6863         PAGEDOWN : 34,
6864         /** Key constant 
6865         * @type Number */
6866         END : 35,
6867         /** Key constant 
6868         * @type Number */
6869         HOME : 36,
6870         /** Key constant 
6871         * @type Number */
6872         LEFT : 37,
6873         /** Key constant 
6874         * @type Number */
6875         UP : 38,
6876         /** Key constant 
6877         * @type Number */
6878         RIGHT : 39,
6879         /** Key constant 
6880         * @type Number */
6881         DOWN : 40,
6882         /** Key constant 
6883         * @type Number */
6884         DELETE : 46,
6885         /** Key constant 
6886         * @type Number */
6887         F5 : 116,
6888
6889            /** @private */
6890         setEvent : function(e){
6891             if(e == this || (e && e.browserEvent)){ // already wrapped
6892                 return e;
6893             }
6894             this.browserEvent = e;
6895             if(e){
6896                 // normalize buttons
6897                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
6898                 if(e.type == 'click' && this.button == -1){
6899                     this.button = 0;
6900                 }
6901                 this.type = e.type;
6902                 this.shiftKey = e.shiftKey;
6903                 // mac metaKey behaves like ctrlKey
6904                 this.ctrlKey = e.ctrlKey || e.metaKey;
6905                 this.altKey = e.altKey;
6906                 // in getKey these will be normalized for the mac
6907                 this.keyCode = e.keyCode;
6908                 // keyup warnings on firefox.
6909                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
6910                 // cache the target for the delayed and or buffered events
6911                 this.target = E.getTarget(e);
6912                 // same for XY
6913                 this.xy = E.getXY(e);
6914             }else{
6915                 this.button = -1;
6916                 this.shiftKey = false;
6917                 this.ctrlKey = false;
6918                 this.altKey = false;
6919                 this.keyCode = 0;
6920                 this.charCode =0;
6921                 this.target = null;
6922                 this.xy = [0, 0];
6923             }
6924             return this;
6925         },
6926
6927         /**
6928          * Stop the event (preventDefault and stopPropagation)
6929          */
6930         stopEvent : function(){
6931             if(this.browserEvent){
6932                 if(this.browserEvent.type == 'mousedown'){
6933                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6934                 }
6935                 E.stopEvent(this.browserEvent);
6936             }
6937         },
6938
6939         /**
6940          * Prevents the browsers default handling of the event.
6941          */
6942         preventDefault : function(){
6943             if(this.browserEvent){
6944                 E.preventDefault(this.browserEvent);
6945             }
6946         },
6947
6948         /** @private */
6949         isNavKeyPress : function(){
6950             var k = this.keyCode;
6951             k = Roo.isSafari ? (safariKeys[k] || k) : k;
6952             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
6953         },
6954
6955         isSpecialKey : function(){
6956             var k = this.keyCode;
6957             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
6958             (k == 16) || (k == 17) ||
6959             (k >= 18 && k <= 20) ||
6960             (k >= 33 && k <= 35) ||
6961             (k >= 36 && k <= 39) ||
6962             (k >= 44 && k <= 45);
6963         },
6964         /**
6965          * Cancels bubbling of the event.
6966          */
6967         stopPropagation : function(){
6968             if(this.browserEvent){
6969                 if(this.type == 'mousedown'){
6970                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
6971                 }
6972                 E.stopPropagation(this.browserEvent);
6973             }
6974         },
6975
6976         /**
6977          * Gets the key code for the event.
6978          * @return {Number}
6979          */
6980         getCharCode : function(){
6981             return this.charCode || this.keyCode;
6982         },
6983
6984         /**
6985          * Returns a normalized keyCode for the event.
6986          * @return {Number} The key code
6987          */
6988         getKey : function(){
6989             var k = this.keyCode || this.charCode;
6990             return Roo.isSafari ? (safariKeys[k] || k) : k;
6991         },
6992
6993         /**
6994          * Gets the x coordinate of the event.
6995          * @return {Number}
6996          */
6997         getPageX : function(){
6998             return this.xy[0];
6999         },
7000
7001         /**
7002          * Gets the y coordinate of the event.
7003          * @return {Number}
7004          */
7005         getPageY : function(){
7006             return this.xy[1];
7007         },
7008
7009         /**
7010          * Gets the time of the event.
7011          * @return {Number}
7012          */
7013         getTime : function(){
7014             if(this.browserEvent){
7015                 return E.getTime(this.browserEvent);
7016             }
7017             return null;
7018         },
7019
7020         /**
7021          * Gets the page coordinates of the event.
7022          * @return {Array} The xy values like [x, y]
7023          */
7024         getXY : function(){
7025             return this.xy;
7026         },
7027
7028         /**
7029          * Gets the target for the event.
7030          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7031          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7032                 search as a number or element (defaults to 10 || document.body)
7033          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7034          * @return {HTMLelement}
7035          */
7036         getTarget : function(selector, maxDepth, returnEl){
7037             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7038         },
7039         /**
7040          * Gets the related target.
7041          * @return {HTMLElement}
7042          */
7043         getRelatedTarget : function(){
7044             if(this.browserEvent){
7045                 return E.getRelatedTarget(this.browserEvent);
7046             }
7047             return null;
7048         },
7049
7050         /**
7051          * Normalizes mouse wheel delta across browsers
7052          * @return {Number} The delta
7053          */
7054         getWheelDelta : function(){
7055             var e = this.browserEvent;
7056             var delta = 0;
7057             if(e.wheelDelta){ /* IE/Opera. */
7058                 delta = e.wheelDelta/120;
7059             }else if(e.detail){ /* Mozilla case. */
7060                 delta = -e.detail/3;
7061             }
7062             return delta;
7063         },
7064
7065         /**
7066          * Returns true if the control, meta, shift or alt key was pressed during this event.
7067          * @return {Boolean}
7068          */
7069         hasModifier : function(){
7070             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7071         },
7072
7073         /**
7074          * Returns true if the target of this event equals el or is a child of el
7075          * @param {String/HTMLElement/Element} el
7076          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7077          * @return {Boolean}
7078          */
7079         within : function(el, related){
7080             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7081             return t && Roo.fly(el).contains(t);
7082         },
7083
7084         getPoint : function(){
7085             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7086         }
7087     };
7088
7089     return new Roo.EventObjectImpl();
7090 }();
7091             
7092     /*
7093  * Based on:
7094  * Ext JS Library 1.1.1
7095  * Copyright(c) 2006-2007, Ext JS, LLC.
7096  *
7097  * Originally Released Under LGPL - original licence link has changed is not relivant.
7098  *
7099  * Fork - LGPL
7100  * <script type="text/javascript">
7101  */
7102
7103  
7104 // was in Composite Element!??!?!
7105  
7106 (function(){
7107     var D = Roo.lib.Dom;
7108     var E = Roo.lib.Event;
7109     var A = Roo.lib.Anim;
7110
7111     // local style camelizing for speed
7112     var propCache = {};
7113     var camelRe = /(-[a-z])/gi;
7114     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7115     var view = document.defaultView;
7116
7117 /**
7118  * @class Roo.Element
7119  * Represents an Element in the DOM.<br><br>
7120  * Usage:<br>
7121 <pre><code>
7122 var el = Roo.get("my-div");
7123
7124 // or with getEl
7125 var el = getEl("my-div");
7126
7127 // or with a DOM element
7128 var el = Roo.get(myDivElement);
7129 </code></pre>
7130  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7131  * each call instead of constructing a new one.<br><br>
7132  * <b>Animations</b><br />
7133  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7134  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7135 <pre>
7136 Option    Default   Description
7137 --------- --------  ---------------------------------------------
7138 duration  .35       The duration of the animation in seconds
7139 easing    easeOut   The YUI easing method
7140 callback  none      A function to execute when the anim completes
7141 scope     this      The scope (this) of the callback function
7142 </pre>
7143 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7144 * manipulate the animation. Here's an example:
7145 <pre><code>
7146 var el = Roo.get("my-div");
7147
7148 // no animation
7149 el.setWidth(100);
7150
7151 // default animation
7152 el.setWidth(100, true);
7153
7154 // animation with some options set
7155 el.setWidth(100, {
7156     duration: 1,
7157     callback: this.foo,
7158     scope: this
7159 });
7160
7161 // using the "anim" property to get the Anim object
7162 var opt = {
7163     duration: 1,
7164     callback: this.foo,
7165     scope: this
7166 };
7167 el.setWidth(100, opt);
7168 ...
7169 if(opt.anim.isAnimated()){
7170     opt.anim.stop();
7171 }
7172 </code></pre>
7173 * <b> Composite (Collections of) Elements</b><br />
7174  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7175  * @constructor Create a new Element directly.
7176  * @param {String/HTMLElement} element
7177  * @param {Boolean} forceNew (optional) By default the constructor checks to see if there is already an instance of this element in the cache and if there is it returns the same instance. This will skip that check (useful for extending this class).
7178  */
7179     Roo.Element = function(element, forceNew)
7180     {
7181         var dom = typeof element == "string" ?
7182                 document.getElementById(element) : element;
7183         
7184         this.listeners = {};
7185         
7186         if(!dom){ // invalid id/element
7187             return null;
7188         }
7189         var id = dom.id;
7190         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7191             return Roo.Element.cache[id];
7192         }
7193
7194         /**
7195          * The DOM element
7196          * @type HTMLElement
7197          */
7198         this.dom = dom;
7199
7200         /**
7201          * The DOM element ID
7202          * @type String
7203          */
7204         this.id = id || Roo.id(dom);
7205         
7206         return this; // assumed for cctor?
7207     };
7208
7209     var El = Roo.Element;
7210
7211     El.prototype = {
7212         /**
7213          * The element's default display mode  (defaults to "") 
7214          * @type String
7215          */
7216         originalDisplay : "",
7217
7218         
7219         // note this is overridden in BS version..
7220         visibilityMode : 1, 
7221         /**
7222          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7223          * @type String
7224          */
7225         defaultUnit : "px",
7226         
7227         /**
7228          * Sets the element's visibility mode. When setVisible() is called it
7229          * will use this to determine whether to set the visibility or the display property.
7230          * @param visMode Element.VISIBILITY or Element.DISPLAY
7231          * @return {Roo.Element} this
7232          */
7233         setVisibilityMode : function(visMode){
7234             this.visibilityMode = visMode;
7235             return this;
7236         },
7237         /**
7238          * Convenience method for setVisibilityMode(Element.DISPLAY)
7239          * @param {String} display (optional) What to set display to when visible
7240          * @return {Roo.Element} this
7241          */
7242         enableDisplayMode : function(display){
7243             this.setVisibilityMode(El.DISPLAY);
7244             if(typeof display != "undefined") { this.originalDisplay = display; }
7245             return this;
7246         },
7247
7248         /**
7249          * Looks at this node and then at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7250          * @param {String} selector The simple selector to test
7251          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7252                 search as a number or element (defaults to 10 || document.body)
7253          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7254          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7255          */
7256         findParent : function(simpleSelector, maxDepth, returnEl){
7257             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7258             maxDepth = maxDepth || 50;
7259             if(typeof maxDepth != "number"){
7260                 stopEl = Roo.getDom(maxDepth);
7261                 maxDepth = 10;
7262             }
7263             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7264                 if(dq.is(p, simpleSelector)){
7265                     return returnEl ? Roo.get(p) : p;
7266                 }
7267                 depth++;
7268                 p = p.parentNode;
7269             }
7270             return null;
7271         },
7272
7273
7274         /**
7275          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7276          * @param {String} selector The simple selector to test
7277          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7278                 search as a number or element (defaults to 10 || document.body)
7279          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7280          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7281          */
7282         findParentNode : function(simpleSelector, maxDepth, returnEl){
7283             var p = Roo.fly(this.dom.parentNode, '_internal');
7284             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7285         },
7286         
7287         /**
7288          * Looks at  the scrollable parent element
7289          */
7290         findScrollableParent : function()
7291         {
7292             var overflowRegex = /(auto|scroll)/;
7293             
7294             if(this.getStyle('position') === 'fixed'){
7295                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7296             }
7297             
7298             var excludeStaticParent = this.getStyle('position') === "absolute";
7299             
7300             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
7301                 
7302                 if (excludeStaticParent && parent.getStyle('position') === "static") {
7303                     continue;
7304                 }
7305                 
7306                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
7307                     return parent;
7308                 }
7309                 
7310                 if(parent.dom.nodeName.toLowerCase() == 'body'){
7311                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7312                 }
7313             }
7314             
7315             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
7316         },
7317
7318         /**
7319          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
7320          * This is a shortcut for findParentNode() that always returns an Roo.Element.
7321          * @param {String} selector The simple selector to test
7322          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7323                 search as a number or element (defaults to 10 || document.body)
7324          * @return {Roo.Element} The matching DOM node (or null if no match was found)
7325          */
7326         up : function(simpleSelector, maxDepth){
7327             return this.findParentNode(simpleSelector, maxDepth, true);
7328         },
7329
7330
7331
7332         /**
7333          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
7334          * @param {String} selector The simple selector to test
7335          * @return {Boolean} True if this element matches the selector, else false
7336          */
7337         is : function(simpleSelector){
7338             return Roo.DomQuery.is(this.dom, simpleSelector);
7339         },
7340
7341         /**
7342          * Perform animation on this element.
7343          * @param {Object} args The YUI animation control args
7344          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
7345          * @param {Function} onComplete (optional) Function to call when animation completes
7346          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
7347          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
7348          * @return {Roo.Element} this
7349          */
7350         animate : function(args, duration, onComplete, easing, animType){
7351             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
7352             return this;
7353         },
7354
7355         /*
7356          * @private Internal animation call
7357          */
7358         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
7359             animType = animType || 'run';
7360             opt = opt || {};
7361             var anim = Roo.lib.Anim[animType](
7362                 this.dom, args,
7363                 (opt.duration || defaultDur) || .35,
7364                 (opt.easing || defaultEase) || 'easeOut',
7365                 function(){
7366                     Roo.callback(cb, this);
7367                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
7368                 },
7369                 this
7370             );
7371             opt.anim = anim;
7372             return anim;
7373         },
7374
7375         // private legacy anim prep
7376         preanim : function(a, i){
7377             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
7378         },
7379
7380         /**
7381          * Removes worthless text nodes
7382          * @param {Boolean} forceReclean (optional) By default the element
7383          * keeps track if it has been cleaned already so
7384          * you can call this over and over. However, if you update the element and
7385          * need to force a reclean, you can pass true.
7386          */
7387         clean : function(forceReclean){
7388             if(this.isCleaned && forceReclean !== true){
7389                 return this;
7390             }
7391             var ns = /\S/;
7392             var d = this.dom, n = d.firstChild, ni = -1;
7393             while(n){
7394                 var nx = n.nextSibling;
7395                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
7396                     d.removeChild(n);
7397                 }else{
7398                     n.nodeIndex = ++ni;
7399                 }
7400                 n = nx;
7401             }
7402             this.isCleaned = true;
7403             return this;
7404         },
7405
7406         // private
7407         calcOffsetsTo : function(el){
7408             el = Roo.get(el);
7409             var d = el.dom;
7410             var restorePos = false;
7411             if(el.getStyle('position') == 'static'){
7412                 el.position('relative');
7413                 restorePos = true;
7414             }
7415             var x = 0, y =0;
7416             var op = this.dom;
7417             while(op && op != d && op.tagName != 'HTML'){
7418                 x+= op.offsetLeft;
7419                 y+= op.offsetTop;
7420                 op = op.offsetParent;
7421             }
7422             if(restorePos){
7423                 el.position('static');
7424             }
7425             return [x, y];
7426         },
7427
7428         /**
7429          * Scrolls this element into view within the passed container.
7430          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
7431          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
7432          * @return {Roo.Element} this
7433          */
7434         scrollIntoView : function(container, hscroll){
7435             var c = Roo.getDom(container) || document.body;
7436             var el = this.dom;
7437
7438             var o = this.calcOffsetsTo(c),
7439                 l = o[0],
7440                 t = o[1],
7441                 b = t+el.offsetHeight,
7442                 r = l+el.offsetWidth;
7443
7444             var ch = c.clientHeight;
7445             var ct = parseInt(c.scrollTop, 10);
7446             var cl = parseInt(c.scrollLeft, 10);
7447             var cb = ct + ch;
7448             var cr = cl + c.clientWidth;
7449
7450             if(t < ct){
7451                 c.scrollTop = t;
7452             }else if(b > cb){
7453                 c.scrollTop = b-ch;
7454             }
7455
7456             if(hscroll !== false){
7457                 if(l < cl){
7458                     c.scrollLeft = l;
7459                 }else if(r > cr){
7460                     c.scrollLeft = r-c.clientWidth;
7461                 }
7462             }
7463             return this;
7464         },
7465
7466         // private
7467         scrollChildIntoView : function(child, hscroll){
7468             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
7469         },
7470
7471         /**
7472          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
7473          * the new height may not be available immediately.
7474          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
7475          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
7476          * @param {Function} onComplete (optional) Function to call when animation completes
7477          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
7478          * @return {Roo.Element} this
7479          */
7480         autoHeight : function(animate, duration, onComplete, easing){
7481             var oldHeight = this.getHeight();
7482             this.clip();
7483             this.setHeight(1); // force clipping
7484             setTimeout(function(){
7485                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
7486                 if(!animate){
7487                     this.setHeight(height);
7488                     this.unclip();
7489                     if(typeof onComplete == "function"){
7490                         onComplete();
7491                     }
7492                 }else{
7493                     this.setHeight(oldHeight); // restore original height
7494                     this.setHeight(height, animate, duration, function(){
7495                         this.unclip();
7496                         if(typeof onComplete == "function") { onComplete(); }
7497                     }.createDelegate(this), easing);
7498                 }
7499             }.createDelegate(this), 0);
7500             return this;
7501         },
7502
7503         /**
7504          * Returns true if this element is an ancestor of the passed element
7505          * @param {HTMLElement/String} el The element to check
7506          * @return {Boolean} True if this element is an ancestor of el, else false
7507          */
7508         contains : function(el){
7509             if(!el){return false;}
7510             return D.isAncestor(this.dom, el.dom ? el.dom : el);
7511         },
7512
7513         /**
7514          * Checks whether the element is currently visible using both visibility and display properties.
7515          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
7516          * @return {Boolean} True if the element is currently visible, else false
7517          */
7518         isVisible : function(deep) {
7519             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
7520             if(deep !== true || !vis){
7521                 return vis;
7522             }
7523             var p = this.dom.parentNode;
7524             while(p && p.tagName.toLowerCase() != "body"){
7525                 if(!Roo.fly(p, '_isVisible').isVisible()){
7526                     return false;
7527                 }
7528                 p = p.parentNode;
7529             }
7530             return true;
7531         },
7532
7533         /**
7534          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
7535          * @param {String} selector The CSS selector
7536          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
7537          * @return {CompositeElement/CompositeElementLite} The composite element
7538          */
7539         select : function(selector, unique){
7540             return El.select(selector, unique, this.dom);
7541         },
7542
7543         /**
7544          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
7545          * @param {String} selector The CSS selector
7546          * @return {Array} An array of the matched nodes
7547          */
7548         query : function(selector, unique){
7549             return Roo.DomQuery.select(selector, this.dom);
7550         },
7551
7552         /**
7553          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
7554          * @param {String} selector The CSS selector
7555          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7556          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7557          */
7558         child : function(selector, returnDom){
7559             var n = Roo.DomQuery.selectNode(selector, this.dom);
7560             return returnDom ? n : Roo.get(n);
7561         },
7562
7563         /**
7564          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
7565          * @param {String} selector The CSS selector
7566          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
7567          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
7568          */
7569         down : function(selector, returnDom){
7570             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
7571             return returnDom ? n : Roo.get(n);
7572         },
7573
7574         /**
7575          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
7576          * @param {String} group The group the DD object is member of
7577          * @param {Object} config The DD config object
7578          * @param {Object} overrides An object containing methods to override/implement on the DD object
7579          * @return {Roo.dd.DD} The DD object
7580          */
7581         initDD : function(group, config, overrides){
7582             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
7583             return Roo.apply(dd, overrides);
7584         },
7585
7586         /**
7587          * Initializes a {@link Roo.dd.DDProxy} object for this element.
7588          * @param {String} group The group the DDProxy object is member of
7589          * @param {Object} config The DDProxy config object
7590          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
7591          * @return {Roo.dd.DDProxy} The DDProxy object
7592          */
7593         initDDProxy : function(group, config, overrides){
7594             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
7595             return Roo.apply(dd, overrides);
7596         },
7597
7598         /**
7599          * Initializes a {@link Roo.dd.DDTarget} object for this element.
7600          * @param {String} group The group the DDTarget object is member of
7601          * @param {Object} config The DDTarget config object
7602          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
7603          * @return {Roo.dd.DDTarget} The DDTarget object
7604          */
7605         initDDTarget : function(group, config, overrides){
7606             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
7607             return Roo.apply(dd, overrides);
7608         },
7609
7610         /**
7611          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
7612          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
7613          * @param {Boolean} visible Whether the element is visible
7614          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7615          * @return {Roo.Element} this
7616          */
7617          setVisible : function(visible, animate){
7618             if(!animate || !A){
7619                 if(this.visibilityMode == El.DISPLAY){
7620                     this.setDisplayed(visible);
7621                 }else{
7622                     this.fixDisplay();
7623                     this.dom.style.visibility = visible ? "visible" : "hidden";
7624                 }
7625             }else{
7626                 // closure for composites
7627                 var dom = this.dom;
7628                 var visMode = this.visibilityMode;
7629                 if(visible){
7630                     this.setOpacity(.01);
7631                     this.setVisible(true);
7632                 }
7633                 this.anim({opacity: { to: (visible?1:0) }},
7634                       this.preanim(arguments, 1),
7635                       null, .35, 'easeIn', function(){
7636                          if(!visible){
7637                              if(visMode == El.DISPLAY){
7638                                  dom.style.display = "none";
7639                              }else{
7640                                  dom.style.visibility = "hidden";
7641                              }
7642                              Roo.get(dom).setOpacity(1);
7643                          }
7644                      });
7645             }
7646             return this;
7647         },
7648
7649         /**
7650          * Returns true if display is not "none"
7651          * @return {Boolean}
7652          */
7653         isDisplayed : function() {
7654             return this.getStyle("display") != "none";
7655         },
7656
7657         /**
7658          * Toggles the element's visibility or display, depending on visibility mode.
7659          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7660          * @return {Roo.Element} this
7661          */
7662         toggle : function(animate){
7663             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
7664             return this;
7665         },
7666
7667         /**
7668          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
7669          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
7670          * @return {Roo.Element} this
7671          */
7672         setDisplayed : function(value) {
7673             if(typeof value == "boolean"){
7674                value = value ? this.originalDisplay : "none";
7675             }
7676             this.setStyle("display", value);
7677             return this;
7678         },
7679
7680         /**
7681          * Tries to focus the element. Any exceptions are caught and ignored.
7682          * @return {Roo.Element} this
7683          */
7684         focus : function() {
7685             try{
7686                 this.dom.focus();
7687             }catch(e){}
7688             return this;
7689         },
7690
7691         /**
7692          * Tries to blur the element. Any exceptions are caught and ignored.
7693          * @return {Roo.Element} this
7694          */
7695         blur : function() {
7696             try{
7697                 this.dom.blur();
7698             }catch(e){}
7699             return this;
7700         },
7701
7702         /**
7703          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
7704          * @param {String/Array} className The CSS class to add, or an array of classes
7705          * @return {Roo.Element} this
7706          */
7707         addClass : function(className){
7708             if(className instanceof Array){
7709                 for(var i = 0, len = className.length; i < len; i++) {
7710                     this.addClass(className[i]);
7711                 }
7712             }else{
7713                 if(className && !this.hasClass(className)){
7714                     if (this.dom instanceof SVGElement) {
7715                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
7716                     } else {
7717                         this.dom.className = this.dom.className + " " + className;
7718                     }
7719                 }
7720             }
7721             return this;
7722         },
7723
7724         /**
7725          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
7726          * @param {String/Array} className The CSS class to add, or an array of classes
7727          * @return {Roo.Element} this
7728          */
7729         radioClass : function(className){
7730             var siblings = this.dom.parentNode.childNodes;
7731             for(var i = 0; i < siblings.length; i++) {
7732                 var s = siblings[i];
7733                 if(s.nodeType == 1){
7734                     Roo.get(s).removeClass(className);
7735                 }
7736             }
7737             this.addClass(className);
7738             return this;
7739         },
7740
7741         /**
7742          * Removes one or more CSS classes from the element.
7743          * @param {String/Array} className The CSS class to remove, or an array of classes
7744          * @return {Roo.Element} this
7745          */
7746         removeClass : function(className){
7747             
7748             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
7749             if(!className || !cn){
7750                 return this;
7751             }
7752             if(className instanceof Array){
7753                 for(var i = 0, len = className.length; i < len; i++) {
7754                     this.removeClass(className[i]);
7755                 }
7756             }else{
7757                 if(this.hasClass(className)){
7758                     var re = this.classReCache[className];
7759                     if (!re) {
7760                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
7761                        this.classReCache[className] = re;
7762                     }
7763                     if (this.dom instanceof SVGElement) {
7764                         this.dom.className.baseVal = cn.replace(re, " ");
7765                     } else {
7766                         this.dom.className = cn.replace(re, " ");
7767                     }
7768                 }
7769             }
7770             return this;
7771         },
7772
7773         // private
7774         classReCache: {},
7775
7776         /**
7777          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
7778          * @param {String} className The CSS class to toggle
7779          * @return {Roo.Element} this
7780          */
7781         toggleClass : function(className){
7782             if(this.hasClass(className)){
7783                 this.removeClass(className);
7784             }else{
7785                 this.addClass(className);
7786             }
7787             return this;
7788         },
7789
7790         /**
7791          * Checks if the specified CSS class exists on this element's DOM node.
7792          * @param {String} className The CSS class to check for
7793          * @return {Boolean} True if the class exists, else false
7794          */
7795         hasClass : function(className){
7796             if (this.dom instanceof SVGElement) {
7797                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
7798             } 
7799             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
7800         },
7801
7802         /**
7803          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
7804          * @param {String} oldClassName The CSS class to replace
7805          * @param {String} newClassName The replacement CSS class
7806          * @return {Roo.Element} this
7807          */
7808         replaceClass : function(oldClassName, newClassName){
7809             this.removeClass(oldClassName);
7810             this.addClass(newClassName);
7811             return this;
7812         },
7813
7814         /**
7815          * Returns an object with properties matching the styles requested.
7816          * For example, el.getStyles('color', 'font-size', 'width') might return
7817          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
7818          * @param {String} style1 A style name
7819          * @param {String} style2 A style name
7820          * @param {String} etc.
7821          * @return {Object} The style object
7822          */
7823         getStyles : function(){
7824             var a = arguments, len = a.length, r = {};
7825             for(var i = 0; i < len; i++){
7826                 r[a[i]] = this.getStyle(a[i]);
7827             }
7828             return r;
7829         },
7830
7831         /**
7832          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
7833          * @param {String} property The style property whose value is returned.
7834          * @return {String} The current value of the style property for this element.
7835          */
7836         getStyle : function(){
7837             return view && view.getComputedStyle ?
7838                 function(prop){
7839                     var el = this.dom, v, cs, camel;
7840                     if(prop == 'float'){
7841                         prop = "cssFloat";
7842                     }
7843                     if(el.style && (v = el.style[prop])){
7844                         return v;
7845                     }
7846                     if(cs = view.getComputedStyle(el, "")){
7847                         if(!(camel = propCache[prop])){
7848                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
7849                         }
7850                         return cs[camel];
7851                     }
7852                     return null;
7853                 } :
7854                 function(prop){
7855                     var el = this.dom, v, cs, camel;
7856                     if(prop == 'opacity'){
7857                         if(typeof el.style.filter == 'string'){
7858                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
7859                             if(m){
7860                                 var fv = parseFloat(m[1]);
7861                                 if(!isNaN(fv)){
7862                                     return fv ? fv / 100 : 0;
7863                                 }
7864                             }
7865                         }
7866                         return 1;
7867                     }else if(prop == 'float'){
7868                         prop = "styleFloat";
7869                     }
7870                     if(!(camel = propCache[prop])){
7871                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
7872                     }
7873                     if(v = el.style[camel]){
7874                         return v;
7875                     }
7876                     if(cs = el.currentStyle){
7877                         return cs[camel];
7878                     }
7879                     return null;
7880                 };
7881         }(),
7882
7883         /**
7884          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
7885          * @param {String/Object} property The style property to be set, or an object of multiple styles.
7886          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
7887          * @return {Roo.Element} this
7888          */
7889         setStyle : function(prop, value){
7890             if(typeof prop == "string"){
7891                 
7892                 if (prop == 'float') {
7893                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
7894                     return this;
7895                 }
7896                 
7897                 var camel;
7898                 if(!(camel = propCache[prop])){
7899                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
7900                 }
7901                 
7902                 if(camel == 'opacity') {
7903                     this.setOpacity(value);
7904                 }else{
7905                     this.dom.style[camel] = value;
7906                 }
7907             }else{
7908                 for(var style in prop){
7909                     if(typeof prop[style] != "function"){
7910                        this.setStyle(style, prop[style]);
7911                     }
7912                 }
7913             }
7914             return this;
7915         },
7916
7917         /**
7918          * More flexible version of {@link #setStyle} for setting style properties.
7919          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
7920          * a function which returns such a specification.
7921          * @return {Roo.Element} this
7922          */
7923         applyStyles : function(style){
7924             Roo.DomHelper.applyStyles(this.dom, style);
7925             return this;
7926         },
7927
7928         /**
7929           * Gets the current X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7930           * @return {Number} The X position of the element
7931           */
7932         getX : function(){
7933             return D.getX(this.dom);
7934         },
7935
7936         /**
7937           * Gets the current Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7938           * @return {Number} The Y position of the element
7939           */
7940         getY : function(){
7941             return D.getY(this.dom);
7942         },
7943
7944         /**
7945           * Gets the current position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7946           * @return {Array} The XY position of the element
7947           */
7948         getXY : function(){
7949             return D.getXY(this.dom);
7950         },
7951
7952         /**
7953          * Sets the X position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7954          * @param {Number} The X position of the element
7955          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7956          * @return {Roo.Element} this
7957          */
7958         setX : function(x, animate){
7959             if(!animate || !A){
7960                 D.setX(this.dom, x);
7961             }else{
7962                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
7963             }
7964             return this;
7965         },
7966
7967         /**
7968          * Sets the Y position of the element based on page coordinates.  Element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
7969          * @param {Number} The Y position of the element
7970          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
7971          * @return {Roo.Element} this
7972          */
7973         setY : function(y, animate){
7974             if(!animate || !A){
7975                 D.setY(this.dom, y);
7976             }else{
7977                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
7978             }
7979             return this;
7980         },
7981
7982         /**
7983          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
7984          * @param {String} left The left CSS property value
7985          * @return {Roo.Element} this
7986          */
7987         setLeft : function(left){
7988             this.setStyle("left", this.addUnits(left));
7989             return this;
7990         },
7991
7992         /**
7993          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
7994          * @param {String} top The top CSS property value
7995          * @return {Roo.Element} this
7996          */
7997         setTop : function(top){
7998             this.setStyle("top", this.addUnits(top));
7999             return this;
8000         },
8001
8002         /**
8003          * Sets the element's CSS right style.
8004          * @param {String} right The right CSS property value
8005          * @return {Roo.Element} this
8006          */
8007         setRight : function(right){
8008             this.setStyle("right", this.addUnits(right));
8009             return this;
8010         },
8011
8012         /**
8013          * Sets the element's CSS bottom style.
8014          * @param {String} bottom The bottom CSS property value
8015          * @return {Roo.Element} this
8016          */
8017         setBottom : function(bottom){
8018             this.setStyle("bottom", this.addUnits(bottom));
8019             return this;
8020         },
8021
8022         /**
8023          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8024          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8025          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8026          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8027          * @return {Roo.Element} this
8028          */
8029         setXY : function(pos, animate){
8030             if(!animate || !A){
8031                 D.setXY(this.dom, pos);
8032             }else{
8033                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8034             }
8035             return this;
8036         },
8037
8038         /**
8039          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8040          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8041          * @param {Number} x X value for new position (coordinates are page-based)
8042          * @param {Number} y Y value for new position (coordinates are page-based)
8043          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8044          * @return {Roo.Element} this
8045          */
8046         setLocation : function(x, y, animate){
8047             this.setXY([x, y], this.preanim(arguments, 2));
8048             return this;
8049         },
8050
8051         /**
8052          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8053          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8054          * @param {Number} x X value for new position (coordinates are page-based)
8055          * @param {Number} y Y value for new position (coordinates are page-based)
8056          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8057          * @return {Roo.Element} this
8058          */
8059         moveTo : function(x, y, animate){
8060             this.setXY([x, y], this.preanim(arguments, 2));
8061             return this;
8062         },
8063
8064         /**
8065          * Returns the region of the given element.
8066          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8067          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8068          */
8069         getRegion : function(){
8070             return D.getRegion(this.dom);
8071         },
8072
8073         /**
8074          * Returns the offset height of the element
8075          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8076          * @return {Number} The element's height
8077          */
8078         getHeight : function(contentHeight){
8079             var h = this.dom.offsetHeight || 0;
8080             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8081         },
8082
8083         /**
8084          * Returns the offset width of the element
8085          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8086          * @return {Number} The element's width
8087          */
8088         getWidth : function(contentWidth){
8089             var w = this.dom.offsetWidth || 0;
8090             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8091         },
8092
8093         /**
8094          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8095          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8096          * if a height has not been set using CSS.
8097          * @return {Number}
8098          */
8099         getComputedHeight : function(){
8100             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8101             if(!h){
8102                 h = parseInt(this.getStyle('height'), 10) || 0;
8103                 if(!this.isBorderBox()){
8104                     h += this.getFrameWidth('tb');
8105                 }
8106             }
8107             return h;
8108         },
8109
8110         /**
8111          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8112          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8113          * if a width has not been set using CSS.
8114          * @return {Number}
8115          */
8116         getComputedWidth : function(){
8117             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8118             if(!w){
8119                 w = parseInt(this.getStyle('width'), 10) || 0;
8120                 if(!this.isBorderBox()){
8121                     w += this.getFrameWidth('lr');
8122                 }
8123             }
8124             return w;
8125         },
8126
8127         /**
8128          * Returns the size of the element.
8129          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8130          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8131          */
8132         getSize : function(contentSize){
8133             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8134         },
8135
8136         /**
8137          * Returns the width and height of the viewport.
8138          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8139          */
8140         getViewSize : function(){
8141             var d = this.dom, doc = document, aw = 0, ah = 0;
8142             if(d == doc || d == doc.body){
8143                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8144             }else{
8145                 return {
8146                     width : d.clientWidth,
8147                     height: d.clientHeight
8148                 };
8149             }
8150         },
8151
8152         /**
8153          * Returns the value of the "value" attribute
8154          * @param {Boolean} asNumber true to parse the value as a number
8155          * @return {String/Number}
8156          */
8157         getValue : function(asNumber){
8158             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8159         },
8160
8161         // private
8162         adjustWidth : function(width){
8163             if(typeof width == "number"){
8164                 if(this.autoBoxAdjust && !this.isBorderBox()){
8165                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8166                 }
8167                 if(width < 0){
8168                     width = 0;
8169                 }
8170             }
8171             return width;
8172         },
8173
8174         // private
8175         adjustHeight : function(height){
8176             if(typeof height == "number"){
8177                if(this.autoBoxAdjust && !this.isBorderBox()){
8178                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8179                }
8180                if(height < 0){
8181                    height = 0;
8182                }
8183             }
8184             return height;
8185         },
8186
8187         /**
8188          * Set the width of the element
8189          * @param {Number} width The new width
8190          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8191          * @return {Roo.Element} this
8192          */
8193         setWidth : function(width, animate){
8194             width = this.adjustWidth(width);
8195             if(!animate || !A){
8196                 this.dom.style.width = this.addUnits(width);
8197             }else{
8198                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8199             }
8200             return this;
8201         },
8202
8203         /**
8204          * Set the height of the element
8205          * @param {Number} height The new height
8206          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8207          * @return {Roo.Element} this
8208          */
8209          setHeight : function(height, animate){
8210             height = this.adjustHeight(height);
8211             if(!animate || !A){
8212                 this.dom.style.height = this.addUnits(height);
8213             }else{
8214                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8215             }
8216             return this;
8217         },
8218
8219         /**
8220          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8221          * @param {Number} width The new width
8222          * @param {Number} height The new height
8223          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8224          * @return {Roo.Element} this
8225          */
8226          setSize : function(width, height, animate){
8227             if(typeof width == "object"){ // in case of object from getSize()
8228                 height = width.height; width = width.width;
8229             }
8230             width = this.adjustWidth(width); height = this.adjustHeight(height);
8231             if(!animate || !A){
8232                 this.dom.style.width = this.addUnits(width);
8233                 this.dom.style.height = this.addUnits(height);
8234             }else{
8235                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8236             }
8237             return this;
8238         },
8239
8240         /**
8241          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8242          * @param {Number} x X value for new position (coordinates are page-based)
8243          * @param {Number} y Y value for new position (coordinates are page-based)
8244          * @param {Number} width The new width
8245          * @param {Number} height The new height
8246          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8247          * @return {Roo.Element} this
8248          */
8249         setBounds : function(x, y, width, height, animate){
8250             if(!animate || !A){
8251                 this.setSize(width, height);
8252                 this.setLocation(x, y);
8253             }else{
8254                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8255                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8256                               this.preanim(arguments, 4), 'motion');
8257             }
8258             return this;
8259         },
8260
8261         /**
8262          * Sets the element's position and size the the specified region. If animation is true then width, height, x and y will be animated concurrently.
8263          * @param {Roo.lib.Region} region The region to fill
8264          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8265          * @return {Roo.Element} this
8266          */
8267         setRegion : function(region, animate){
8268             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8269             return this;
8270         },
8271
8272         /**
8273          * Appends an event handler
8274          *
8275          * @param {String}   eventName     The type of event to append
8276          * @param {Function} fn        The method the event invokes
8277          * @param {Object} scope       (optional) The scope (this object) of the fn
8278          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8279          */
8280         addListener : function(eventName, fn, scope, options)
8281         {
8282             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8283                 this.addListener('touchstart', this.onTapHandler, this);
8284             }
8285             
8286             // we need to handle a special case where dom element is a svg element.
8287             // in this case we do not actua
8288             if (!this.dom) {
8289                 return;
8290             }
8291             
8292             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
8293                 if (typeof(this.listeners[eventName]) == 'undefined') {
8294                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
8295                 }
8296                 this.listeners[eventName].addListener(fn, scope, options);
8297                 return;
8298             }
8299             
8300                 
8301             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
8302             
8303             
8304         },
8305         tapedTwice : false,
8306         onTapHandler : function(event)
8307         {
8308             if(!this.tapedTwice) {
8309                 this.tapedTwice = true;
8310                 var s = this;
8311                 setTimeout( function() {
8312                     s.tapedTwice = false;
8313                 }, 300 );
8314                 return;
8315             }
8316             event.preventDefault();
8317             var revent = new MouseEvent('dblclick',  {
8318                 view: window,
8319                 bubbles: true,
8320                 cancelable: true
8321             });
8322              
8323             this.dom.dispatchEvent(revent);
8324             //action on double tap goes below
8325              
8326         }, 
8327  
8328         /**
8329          * Removes an event handler from this element
8330          * @param {String} eventName the type of event to remove
8331          * @param {Function} fn the method the event invokes
8332          * @param {Function} scope (needed for svg fake listeners)
8333          * @return {Roo.Element} this
8334          */
8335         removeListener : function(eventName, fn, scope){
8336             Roo.EventManager.removeListener(this.dom,  eventName, fn);
8337             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
8338                 return this;
8339             }
8340             this.listeners[eventName].removeListener(fn, scope);
8341             return this;
8342         },
8343
8344         /**
8345          * Removes all previous added listeners from this element
8346          * @return {Roo.Element} this
8347          */
8348         removeAllListeners : function(){
8349             E.purgeElement(this.dom);
8350             this.listeners = {};
8351             return this;
8352         },
8353
8354         relayEvent : function(eventName, observable){
8355             this.on(eventName, function(e){
8356                 observable.fireEvent(eventName, e);
8357             });
8358         },
8359
8360         
8361         /**
8362          * Set the opacity of the element
8363          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
8364          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8365          * @return {Roo.Element} this
8366          */
8367          setOpacity : function(opacity, animate){
8368             if(!animate || !A){
8369                 var s = this.dom.style;
8370                 if(Roo.isIE){
8371                     s.zoom = 1;
8372                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
8373                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
8374                 }else{
8375                     s.opacity = opacity;
8376                 }
8377             }else{
8378                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
8379             }
8380             return this;
8381         },
8382
8383         /**
8384          * Gets the left X coordinate
8385          * @param {Boolean} local True to get the local css position instead of page coordinate
8386          * @return {Number}
8387          */
8388         getLeft : function(local){
8389             if(!local){
8390                 return this.getX();
8391             }else{
8392                 return parseInt(this.getStyle("left"), 10) || 0;
8393             }
8394         },
8395
8396         /**
8397          * Gets the right X coordinate of the element (element X position + element width)
8398          * @param {Boolean} local True to get the local css position instead of page coordinate
8399          * @return {Number}
8400          */
8401         getRight : function(local){
8402             if(!local){
8403                 return this.getX() + this.getWidth();
8404             }else{
8405                 return (this.getLeft(true) + this.getWidth()) || 0;
8406             }
8407         },
8408
8409         /**
8410          * Gets the top Y coordinate
8411          * @param {Boolean} local True to get the local css position instead of page coordinate
8412          * @return {Number}
8413          */
8414         getTop : function(local) {
8415             if(!local){
8416                 return this.getY();
8417             }else{
8418                 return parseInt(this.getStyle("top"), 10) || 0;
8419             }
8420         },
8421
8422         /**
8423          * Gets the bottom Y coordinate of the element (element Y position + element height)
8424          * @param {Boolean} local True to get the local css position instead of page coordinate
8425          * @return {Number}
8426          */
8427         getBottom : function(local){
8428             if(!local){
8429                 return this.getY() + this.getHeight();
8430             }else{
8431                 return (this.getTop(true) + this.getHeight()) || 0;
8432             }
8433         },
8434
8435         /**
8436         * Initializes positioning on this element. If a desired position is not passed, it will make the
8437         * the element positioned relative IF it is not already positioned.
8438         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
8439         * @param {Number} zIndex (optional) The zIndex to apply
8440         * @param {Number} x (optional) Set the page X position
8441         * @param {Number} y (optional) Set the page Y position
8442         */
8443         position : function(pos, zIndex, x, y){
8444             if(!pos){
8445                if(this.getStyle('position') == 'static'){
8446                    this.setStyle('position', 'relative');
8447                }
8448             }else{
8449                 this.setStyle("position", pos);
8450             }
8451             if(zIndex){
8452                 this.setStyle("z-index", zIndex);
8453             }
8454             if(x !== undefined && y !== undefined){
8455                 this.setXY([x, y]);
8456             }else if(x !== undefined){
8457                 this.setX(x);
8458             }else if(y !== undefined){
8459                 this.setY(y);
8460             }
8461         },
8462
8463         /**
8464         * Clear positioning back to the default when the document was loaded
8465         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
8466         * @return {Roo.Element} this
8467          */
8468         clearPositioning : function(value){
8469             value = value ||'';
8470             this.setStyle({
8471                 "left": value,
8472                 "right": value,
8473                 "top": value,
8474                 "bottom": value,
8475                 "z-index": "",
8476                 "position" : "static"
8477             });
8478             return this;
8479         },
8480
8481         /**
8482         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
8483         * snapshot before performing an update and then restoring the element.
8484         * @return {Object}
8485         */
8486         getPositioning : function(){
8487             var l = this.getStyle("left");
8488             var t = this.getStyle("top");
8489             return {
8490                 "position" : this.getStyle("position"),
8491                 "left" : l,
8492                 "right" : l ? "" : this.getStyle("right"),
8493                 "top" : t,
8494                 "bottom" : t ? "" : this.getStyle("bottom"),
8495                 "z-index" : this.getStyle("z-index")
8496             };
8497         },
8498
8499         /**
8500          * Gets the width of the border(s) for the specified side(s)
8501          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8502          * passing lr would get the border (l)eft width + the border (r)ight width.
8503          * @return {Number} The width of the sides passed added together
8504          */
8505         getBorderWidth : function(side){
8506             return this.addStyles(side, El.borders);
8507         },
8508
8509         /**
8510          * Gets the width of the padding(s) for the specified side(s)
8511          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
8512          * passing lr would get the padding (l)eft + the padding (r)ight.
8513          * @return {Number} The padding of the sides passed added together
8514          */
8515         getPadding : function(side){
8516             return this.addStyles(side, El.paddings);
8517         },
8518
8519         /**
8520         * Set positioning with an object returned by getPositioning().
8521         * @param {Object} posCfg
8522         * @return {Roo.Element} this
8523          */
8524         setPositioning : function(pc){
8525             this.applyStyles(pc);
8526             if(pc.right == "auto"){
8527                 this.dom.style.right = "";
8528             }
8529             if(pc.bottom == "auto"){
8530                 this.dom.style.bottom = "";
8531             }
8532             return this;
8533         },
8534
8535         // private
8536         fixDisplay : function(){
8537             if(this.getStyle("display") == "none"){
8538                 this.setStyle("visibility", "hidden");
8539                 this.setStyle("display", this.originalDisplay); // first try reverting to default
8540                 if(this.getStyle("display") == "none"){ // if that fails, default to block
8541                     this.setStyle("display", "block");
8542                 }
8543             }
8544         },
8545
8546         /**
8547          * Quick set left and top adding default units
8548          * @param {String} left The left CSS property value
8549          * @param {String} top The top CSS property value
8550          * @return {Roo.Element} this
8551          */
8552          setLeftTop : function(left, top){
8553             this.dom.style.left = this.addUnits(left);
8554             this.dom.style.top = this.addUnits(top);
8555             return this;
8556         },
8557
8558         /**
8559          * Move this element relative to its current position.
8560          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
8561          * @param {Number} distance How far to move the element in pixels
8562          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8563          * @return {Roo.Element} this
8564          */
8565          move : function(direction, distance, animate){
8566             var xy = this.getXY();
8567             direction = direction.toLowerCase();
8568             switch(direction){
8569                 case "l":
8570                 case "left":
8571                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
8572                     break;
8573                case "r":
8574                case "right":
8575                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
8576                     break;
8577                case "t":
8578                case "top":
8579                case "up":
8580                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
8581                     break;
8582                case "b":
8583                case "bottom":
8584                case "down":
8585                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
8586                     break;
8587             }
8588             return this;
8589         },
8590
8591         /**
8592          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
8593          * @return {Roo.Element} this
8594          */
8595         clip : function(){
8596             if(!this.isClipped){
8597                this.isClipped = true;
8598                this.originalClip = {
8599                    "o": this.getStyle("overflow"),
8600                    "x": this.getStyle("overflow-x"),
8601                    "y": this.getStyle("overflow-y")
8602                };
8603                this.setStyle("overflow", "hidden");
8604                this.setStyle("overflow-x", "hidden");
8605                this.setStyle("overflow-y", "hidden");
8606             }
8607             return this;
8608         },
8609
8610         /**
8611          *  Return clipping (overflow) to original clipping before clip() was called
8612          * @return {Roo.Element} this
8613          */
8614         unclip : function(){
8615             if(this.isClipped){
8616                 this.isClipped = false;
8617                 var o = this.originalClip;
8618                 if(o.o){this.setStyle("overflow", o.o);}
8619                 if(o.x){this.setStyle("overflow-x", o.x);}
8620                 if(o.y){this.setStyle("overflow-y", o.y);}
8621             }
8622             return this;
8623         },
8624
8625
8626         /**
8627          * Gets the x,y coordinates specified by the anchor position on the element.
8628          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
8629          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
8630          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
8631          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
8632          * @return {Array} [x, y] An array containing the element's x and y coordinates
8633          */
8634         getAnchorXY : function(anchor, local, s){
8635             //Passing a different size is useful for pre-calculating anchors,
8636             //especially for anchored animations that change the el size.
8637
8638             var w, h, vp = false;
8639             if(!s){
8640                 var d = this.dom;
8641                 if(d == document.body || d == document){
8642                     vp = true;
8643                     w = D.getViewWidth(); h = D.getViewHeight();
8644                 }else{
8645                     w = this.getWidth(); h = this.getHeight();
8646                 }
8647             }else{
8648                 w = s.width;  h = s.height;
8649             }
8650             var x = 0, y = 0, r = Math.round;
8651             switch((anchor || "tl").toLowerCase()){
8652                 case "c":
8653                     x = r(w*.5);
8654                     y = r(h*.5);
8655                 break;
8656                 case "t":
8657                     x = r(w*.5);
8658                     y = 0;
8659                 break;
8660                 case "l":
8661                     x = 0;
8662                     y = r(h*.5);
8663                 break;
8664                 case "r":
8665                     x = w;
8666                     y = r(h*.5);
8667                 break;
8668                 case "b":
8669                     x = r(w*.5);
8670                     y = h;
8671                 break;
8672                 case "tl":
8673                     x = 0;
8674                     y = 0;
8675                 break;
8676                 case "bl":
8677                     x = 0;
8678                     y = h;
8679                 break;
8680                 case "br":
8681                     x = w;
8682                     y = h;
8683                 break;
8684                 case "tr":
8685                     x = w;
8686                     y = 0;
8687                 break;
8688             }
8689             if(local === true){
8690                 return [x, y];
8691             }
8692             if(vp){
8693                 var sc = this.getScroll();
8694                 return [x + sc.left, y + sc.top];
8695             }
8696             //Add the element's offset xy
8697             var o = this.getXY();
8698             return [x+o[0], y+o[1]];
8699         },
8700
8701         /**
8702          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
8703          * supported position values.
8704          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8705          * @param {String} position The position to align to.
8706          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8707          * @return {Array} [x, y]
8708          */
8709         getAlignToXY : function(el, p, o)
8710         {
8711             el = Roo.get(el);
8712             var d = this.dom;
8713             if(!el.dom){
8714                 throw "Element.alignTo with an element that doesn't exist";
8715             }
8716             var c = false; //constrain to viewport
8717             var p1 = "", p2 = "";
8718             o = o || [0,0];
8719
8720             if(!p){
8721                 p = "tl-bl";
8722             }else if(p == "?"){
8723                 p = "tl-bl?";
8724             }else if(p.indexOf("-") == -1){
8725                 p = "tl-" + p;
8726             }
8727             p = p.toLowerCase();
8728             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
8729             if(!m){
8730                throw "Element.alignTo with an invalid alignment " + p;
8731             }
8732             p1 = m[1]; p2 = m[2]; c = !!m[3];
8733
8734             //Subtract the aligned el's internal xy from the target's offset xy
8735             //plus custom offset to get the aligned el's new offset xy
8736             var a1 = this.getAnchorXY(p1, true);
8737             var a2 = el.getAnchorXY(p2, false);
8738             var x = a2[0] - a1[0] + o[0];
8739             var y = a2[1] - a1[1] + o[1];
8740             if(c){
8741                 //constrain the aligned el to viewport if necessary
8742                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
8743                 // 5px of margin for ie
8744                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
8745
8746                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
8747                 //perpendicular to the vp border, allow the aligned el to slide on that border,
8748                 //otherwise swap the aligned el to the opposite border of the target.
8749                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
8750                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
8751                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
8752                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
8753
8754                var doc = document;
8755                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
8756                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
8757
8758                if((x+w) > dw + scrollX){
8759                     x = swapX ? r.left-w : dw+scrollX-w;
8760                 }
8761                if(x < scrollX){
8762                    x = swapX ? r.right : scrollX;
8763                }
8764                if((y+h) > dh + scrollY){
8765                     y = swapY ? r.top-h : dh+scrollY-h;
8766                 }
8767                if (y < scrollY){
8768                    y = swapY ? r.bottom : scrollY;
8769                }
8770             }
8771             return [x,y];
8772         },
8773
8774         // private
8775         getConstrainToXY : function(){
8776             var os = {top:0, left:0, bottom:0, right: 0};
8777
8778             return function(el, local, offsets, proposedXY){
8779                 el = Roo.get(el);
8780                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
8781
8782                 var vw, vh, vx = 0, vy = 0;
8783                 if(el.dom == document.body || el.dom == document){
8784                     vw = Roo.lib.Dom.getViewWidth();
8785                     vh = Roo.lib.Dom.getViewHeight();
8786                 }else{
8787                     vw = el.dom.clientWidth;
8788                     vh = el.dom.clientHeight;
8789                     if(!local){
8790                         var vxy = el.getXY();
8791                         vx = vxy[0];
8792                         vy = vxy[1];
8793                     }
8794                 }
8795
8796                 var s = el.getScroll();
8797
8798                 vx += offsets.left + s.left;
8799                 vy += offsets.top + s.top;
8800
8801                 vw -= offsets.right;
8802                 vh -= offsets.bottom;
8803
8804                 var vr = vx+vw;
8805                 var vb = vy+vh;
8806
8807                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
8808                 var x = xy[0], y = xy[1];
8809                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
8810
8811                 // only move it if it needs it
8812                 var moved = false;
8813
8814                 // first validate right/bottom
8815                 if((x + w) > vr){
8816                     x = vr - w;
8817                     moved = true;
8818                 }
8819                 if((y + h) > vb){
8820                     y = vb - h;
8821                     moved = true;
8822                 }
8823                 // then make sure top/left isn't negative
8824                 if(x < vx){
8825                     x = vx;
8826                     moved = true;
8827                 }
8828                 if(y < vy){
8829                     y = vy;
8830                     moved = true;
8831                 }
8832                 return moved ? [x, y] : false;
8833             };
8834         }(),
8835
8836         // private
8837         adjustForConstraints : function(xy, parent, offsets){
8838             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
8839         },
8840
8841         /**
8842          * Aligns this element with another element relative to the specified anchor points. If the other element is the
8843          * document it aligns it to the viewport.
8844          * The position parameter is optional, and can be specified in any one of the following formats:
8845          * <ul>
8846          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
8847          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
8848          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
8849          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
8850          *   <li><b>Two anchors</b>: If two values from the table below are passed separated by a dash, the first value is used as the
8851          *       element's anchor point, and the second value is used as the target's anchor point.</li>
8852          * </ul>
8853          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
8854          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
8855          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
8856          * that specified in order to enforce the viewport constraints.
8857          * Following are all of the supported anchor positions:
8858     <pre>
8859     Value  Description
8860     -----  -----------------------------
8861     tl     The top left corner (default)
8862     t      The center of the top edge
8863     tr     The top right corner
8864     l      The center of the left edge
8865     c      In the center of the element
8866     r      The center of the right edge
8867     bl     The bottom left corner
8868     b      The center of the bottom edge
8869     br     The bottom right corner
8870     </pre>
8871     Example Usage:
8872     <pre><code>
8873     // align el to other-el using the default positioning ("tl-bl", non-constrained)
8874     el.alignTo("other-el");
8875
8876     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
8877     el.alignTo("other-el", "tr?");
8878
8879     // align the bottom right corner of el with the center left edge of other-el
8880     el.alignTo("other-el", "br-l?");
8881
8882     // align the center of el with the bottom left corner of other-el and
8883     // adjust the x position by -6 pixels (and the y position by 0)
8884     el.alignTo("other-el", "c-bl", [-6, 0]);
8885     </code></pre>
8886          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8887          * @param {String} position The position to align to.
8888          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8889          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8890          * @return {Roo.Element} this
8891          */
8892         alignTo : function(element, position, offsets, animate){
8893             var xy = this.getAlignToXY(element, position, offsets);
8894             this.setXY(xy, this.preanim(arguments, 3));
8895             return this;
8896         },
8897
8898         /**
8899          * Anchors an element to another element and realigns it when the window is resized.
8900          * @param {String/HTMLElement/Roo.Element} element The element to align to.
8901          * @param {String} position The position to align to.
8902          * @param {Array} offsets (optional) Offset the positioning by [x, y]
8903          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
8904          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
8905          * is a number, it is used as the buffer delay (defaults to 50ms).
8906          * @param {Function} callback The function to call after the animation finishes
8907          * @return {Roo.Element} this
8908          */
8909         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
8910             var action = function(){
8911                 this.alignTo(el, alignment, offsets, animate);
8912                 Roo.callback(callback, this);
8913             };
8914             Roo.EventManager.onWindowResize(action, this);
8915             var tm = typeof monitorScroll;
8916             if(tm != 'undefined'){
8917                 Roo.EventManager.on(window, 'scroll', action, this,
8918                     {buffer: tm == 'number' ? monitorScroll : 50});
8919             }
8920             action.call(this); // align immediately
8921             return this;
8922         },
8923         /**
8924          * Clears any opacity settings from this element. Required in some cases for IE.
8925          * @return {Roo.Element} this
8926          */
8927         clearOpacity : function(){
8928             if (window.ActiveXObject) {
8929                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
8930                     this.dom.style.filter = "";
8931                 }
8932             } else {
8933                 this.dom.style.opacity = "";
8934                 this.dom.style["-moz-opacity"] = "";
8935                 this.dom.style["-khtml-opacity"] = "";
8936             }
8937             return this;
8938         },
8939
8940         /**
8941          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8942          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8943          * @return {Roo.Element} this
8944          */
8945         hide : function(animate){
8946             this.setVisible(false, this.preanim(arguments, 0));
8947             return this;
8948         },
8949
8950         /**
8951         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
8952         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8953          * @return {Roo.Element} this
8954          */
8955         show : function(animate){
8956             this.setVisible(true, this.preanim(arguments, 0));
8957             return this;
8958         },
8959
8960         /**
8961          * @private Test if size has a unit, otherwise appends the default
8962          */
8963         addUnits : function(size){
8964             return Roo.Element.addUnits(size, this.defaultUnit);
8965         },
8966
8967         /**
8968          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
8969          * @return {Roo.Element} this
8970          */
8971         beginMeasure : function(){
8972             var el = this.dom;
8973             if(el.offsetWidth || el.offsetHeight){
8974                 return this; // offsets work already
8975             }
8976             var changed = [];
8977             var p = this.dom, b = document.body; // start with this element
8978             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
8979                 var pe = Roo.get(p);
8980                 if(pe.getStyle('display') == 'none'){
8981                     changed.push({el: p, visibility: pe.getStyle("visibility")});
8982                     p.style.visibility = "hidden";
8983                     p.style.display = "block";
8984                 }
8985                 p = p.parentNode;
8986             }
8987             this._measureChanged = changed;
8988             return this;
8989
8990         },
8991
8992         /**
8993          * Restores displays to before beginMeasure was called
8994          * @return {Roo.Element} this
8995          */
8996         endMeasure : function(){
8997             var changed = this._measureChanged;
8998             if(changed){
8999                 for(var i = 0, len = changed.length; i < len; i++) {
9000                     var r = changed[i];
9001                     r.el.style.visibility = r.visibility;
9002                     r.el.style.display = "none";
9003                 }
9004                 this._measureChanged = null;
9005             }
9006             return this;
9007         },
9008
9009         /**
9010         * Update the innerHTML of this element, optionally searching for and processing scripts
9011         * @param {String} html The new HTML
9012         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9013         * @param {Function} callback For async script loading you can be noticed when the update completes
9014         * @return {Roo.Element} this
9015          */
9016         update : function(html, loadScripts, callback){
9017             if(typeof html == "undefined"){
9018                 html = "";
9019             }
9020             if(loadScripts !== true){
9021                 this.dom.innerHTML = html;
9022                 if(typeof callback == "function"){
9023                     callback();
9024                 }
9025                 return this;
9026             }
9027             var id = Roo.id();
9028             var dom = this.dom;
9029
9030             html += '<span id="' + id + '"></span>';
9031
9032             E.onAvailable(id, function(){
9033                 var hd = document.getElementsByTagName("head")[0];
9034                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9035                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9036                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9037
9038                 var match;
9039                 while(match = re.exec(html)){
9040                     var attrs = match[1];
9041                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9042                     if(srcMatch && srcMatch[2]){
9043                        var s = document.createElement("script");
9044                        s.src = srcMatch[2];
9045                        var typeMatch = attrs.match(typeRe);
9046                        if(typeMatch && typeMatch[2]){
9047                            s.type = typeMatch[2];
9048                        }
9049                        hd.appendChild(s);
9050                     }else if(match[2] && match[2].length > 0){
9051                         if(window.execScript) {
9052                            window.execScript(match[2]);
9053                         } else {
9054                             /**
9055                              * eval:var:id
9056                              * eval:var:dom
9057                              * eval:var:html
9058                              * 
9059                              */
9060                            window.eval(match[2]);
9061                         }
9062                     }
9063                 }
9064                 var el = document.getElementById(id);
9065                 if(el){el.parentNode.removeChild(el);}
9066                 if(typeof callback == "function"){
9067                     callback();
9068                 }
9069             });
9070             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9071             return this;
9072         },
9073
9074         /**
9075          * Direct access to the UpdateManager update() method (takes the same parameters).
9076          * @param {String/Function} url The url for this request or a function to call to get the url
9077          * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
9078          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9079          * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
9080          * @return {Roo.Element} this
9081          */
9082         load : function(){
9083             var um = this.getUpdateManager();
9084             um.update.apply(um, arguments);
9085             return this;
9086         },
9087
9088         /**
9089         * Gets this element's UpdateManager
9090         * @return {Roo.UpdateManager} The UpdateManager
9091         */
9092         getUpdateManager : function(){
9093             if(!this.updateManager){
9094                 this.updateManager = new Roo.UpdateManager(this);
9095             }
9096             return this.updateManager;
9097         },
9098
9099         /**
9100          * Disables text selection for this element (normalized across browsers)
9101          * @return {Roo.Element} this
9102          */
9103         unselectable : function(){
9104             this.dom.unselectable = "on";
9105             this.swallowEvent("selectstart", true);
9106             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9107             this.addClass("x-unselectable");
9108             return this;
9109         },
9110
9111         /**
9112         * Calculates the x, y to center this element on the screen
9113         * @return {Array} The x, y values [x, y]
9114         */
9115         getCenterXY : function(){
9116             return this.getAlignToXY(document, 'c-c');
9117         },
9118
9119         /**
9120         * Centers the Element in either the viewport, or another Element.
9121         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9122         */
9123         center : function(centerIn){
9124             this.alignTo(centerIn || document, 'c-c');
9125             return this;
9126         },
9127
9128         /**
9129          * Tests various css rules/browsers to determine if this element uses a border box
9130          * @return {Boolean}
9131          */
9132         isBorderBox : function(){
9133             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9134         },
9135
9136         /**
9137          * Return a box {x, y, width, height} that can be used to set another elements
9138          * size/location to match this element.
9139          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9140          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9141          * @return {Object} box An object in the format {x, y, width, height}
9142          */
9143         getBox : function(contentBox, local){
9144             var xy;
9145             if(!local){
9146                 xy = this.getXY();
9147             }else{
9148                 var left = parseInt(this.getStyle("left"), 10) || 0;
9149                 var top = parseInt(this.getStyle("top"), 10) || 0;
9150                 xy = [left, top];
9151             }
9152             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9153             if(!contentBox){
9154                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9155             }else{
9156                 var l = this.getBorderWidth("l")+this.getPadding("l");
9157                 var r = this.getBorderWidth("r")+this.getPadding("r");
9158                 var t = this.getBorderWidth("t")+this.getPadding("t");
9159                 var b = this.getBorderWidth("b")+this.getPadding("b");
9160                 bx = {x: xy[0]+l, y: xy[1]+t, 0: xy[0]+l, 1: xy[1]+t, width: w-(l+r), height: h-(t+b)};
9161             }
9162             bx.right = bx.x + bx.width;
9163             bx.bottom = bx.y + bx.height;
9164             return bx;
9165         },
9166
9167         /**
9168          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9169          for more information about the sides.
9170          * @param {String} sides
9171          * @return {Number}
9172          */
9173         getFrameWidth : function(sides, onlyContentBox){
9174             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9175         },
9176
9177         /**
9178          * Sets the element's box. Use getBox() on another element to get a box obj. If animate is true then width, height, x and y will be animated concurrently.
9179          * @param {Object} box The box to fill {x, y, width, height}
9180          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9181          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9182          * @return {Roo.Element} this
9183          */
9184         setBox : function(box, adjust, animate){
9185             var w = box.width, h = box.height;
9186             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9187                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9188                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9189             }
9190             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9191             return this;
9192         },
9193
9194         /**
9195          * Forces the browser to repaint this element
9196          * @return {Roo.Element} this
9197          */
9198          repaint : function(){
9199             var dom = this.dom;
9200             this.addClass("x-repaint");
9201             setTimeout(function(){
9202                 Roo.get(dom).removeClass("x-repaint");
9203             }, 1);
9204             return this;
9205         },
9206
9207         /**
9208          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9209          * then it returns the calculated width of the sides (see getPadding)
9210          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9211          * @return {Object/Number}
9212          */
9213         getMargins : function(side){
9214             if(!side){
9215                 return {
9216                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9217                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9218                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9219                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9220                 };
9221             }else{
9222                 return this.addStyles(side, El.margins);
9223              }
9224         },
9225
9226         // private
9227         addStyles : function(sides, styles){
9228             var val = 0, v, w;
9229             for(var i = 0, len = sides.length; i < len; i++){
9230                 v = this.getStyle(styles[sides.charAt(i)]);
9231                 if(v){
9232                      w = parseInt(v, 10);
9233                      if(w){ val += w; }
9234                 }
9235             }
9236             return val;
9237         },
9238
9239         /**
9240          * Creates a proxy element of this element
9241          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9242          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9243          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9244          * @return {Roo.Element} The new proxy element
9245          */
9246         createProxy : function(config, renderTo, matchBox){
9247             if(renderTo){
9248                 renderTo = Roo.getDom(renderTo);
9249             }else{
9250                 renderTo = document.body;
9251             }
9252             config = typeof config == "object" ?
9253                 config : {tag : "div", cls: config};
9254             var proxy = Roo.DomHelper.append(renderTo, config, true);
9255             if(matchBox){
9256                proxy.setBox(this.getBox());
9257             }
9258             return proxy;
9259         },
9260
9261         /**
9262          * Puts a mask over this element to disable user interaction. Requires core.css.
9263          * This method can only be applied to elements which accept child nodes.
9264          * @param {String} msg (optional) A message to display in the mask
9265          * @param {String} msgCls (optional) A css class to apply to the msg element
9266          * @return {Element} The mask  element
9267          */
9268         mask : function(msg, msgCls)
9269         {
9270             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9271                 this.setStyle("position", "relative");
9272             }
9273             if(!this._mask){
9274                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9275             }
9276             
9277             this.addClass("x-masked");
9278             this._mask.setDisplayed(true);
9279             
9280             // we wander
9281             var z = 0;
9282             var dom = this.dom;
9283             while (dom && dom.style) {
9284                 if (!isNaN(parseInt(dom.style.zIndex))) {
9285                     z = Math.max(z, parseInt(dom.style.zIndex));
9286                 }
9287                 dom = dom.parentNode;
9288             }
9289             // if we are masking the body - then it hides everything..
9290             if (this.dom == document.body) {
9291                 z = 1000000;
9292                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
9293                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
9294             }
9295            
9296             if(typeof msg == 'string'){
9297                 if(!this._maskMsg){
9298                     this._maskMsg = Roo.DomHelper.append(this.dom, {
9299                         cls: "roo-el-mask-msg", 
9300                         cn: [
9301                             {
9302                                 tag: 'i',
9303                                 cls: 'fa fa-spinner fa-spin'
9304                             },
9305                             {
9306                                 tag: 'div'
9307                             }   
9308                         ]
9309                     }, true);
9310                 }
9311                 var mm = this._maskMsg;
9312                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
9313                 if (mm.dom.lastChild) { // weird IE issue?
9314                     mm.dom.lastChild.innerHTML = msg;
9315                 }
9316                 mm.setDisplayed(true);
9317                 mm.center(this);
9318                 mm.setStyle('z-index', z + 102);
9319             }
9320             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
9321                 this._mask.setHeight(this.getHeight());
9322             }
9323             this._mask.setStyle('z-index', z + 100);
9324             
9325             return this._mask;
9326         },
9327
9328         /**
9329          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
9330          * it is cached for reuse.
9331          */
9332         unmask : function(removeEl){
9333             if(this._mask){
9334                 if(removeEl === true){
9335                     this._mask.remove();
9336                     delete this._mask;
9337                     if(this._maskMsg){
9338                         this._maskMsg.remove();
9339                         delete this._maskMsg;
9340                     }
9341                 }else{
9342                     this._mask.setDisplayed(false);
9343                     if(this._maskMsg){
9344                         this._maskMsg.setDisplayed(false);
9345                     }
9346                 }
9347             }
9348             this.removeClass("x-masked");
9349         },
9350
9351         /**
9352          * Returns true if this element is masked
9353          * @return {Boolean}
9354          */
9355         isMasked : function(){
9356             return this._mask && this._mask.isVisible();
9357         },
9358
9359         /**
9360          * Creates an iframe shim for this element to keep selects and other windowed objects from
9361          * showing through.
9362          * @return {Roo.Element} The new shim element
9363          */
9364         createShim : function(){
9365             var el = document.createElement('iframe');
9366             el.frameBorder = 'no';
9367             el.className = 'roo-shim';
9368             if(Roo.isIE && Roo.isSecure){
9369                 el.src = Roo.SSL_SECURE_URL;
9370             }
9371             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
9372             shim.autoBoxAdjust = false;
9373             return shim;
9374         },
9375
9376         /**
9377          * Removes this element from the DOM and deletes it from the cache
9378          */
9379         remove : function(){
9380             if(this.dom.parentNode){
9381                 this.dom.parentNode.removeChild(this.dom);
9382             }
9383             delete El.cache[this.dom.id];
9384         },
9385
9386         /**
9387          * Sets up event handlers to add and remove a css class when the mouse is over this element
9388          * @param {String} className
9389          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
9390          * mouseout events for children elements
9391          * @return {Roo.Element} this
9392          */
9393         addClassOnOver : function(className, preventFlicker){
9394             this.on("mouseover", function(){
9395                 Roo.fly(this, '_internal').addClass(className);
9396             }, this.dom);
9397             var removeFn = function(e){
9398                 if(preventFlicker !== true || !e.within(this, true)){
9399                     Roo.fly(this, '_internal').removeClass(className);
9400                 }
9401             };
9402             this.on("mouseout", removeFn, this.dom);
9403             return this;
9404         },
9405
9406         /**
9407          * Sets up event handlers to add and remove a css class when this element has the focus
9408          * @param {String} className
9409          * @return {Roo.Element} this
9410          */
9411         addClassOnFocus : function(className){
9412             this.on("focus", function(){
9413                 Roo.fly(this, '_internal').addClass(className);
9414             }, this.dom);
9415             this.on("blur", function(){
9416                 Roo.fly(this, '_internal').removeClass(className);
9417             }, this.dom);
9418             return this;
9419         },
9420         /**
9421          * Sets up event handlers to add and remove a css class when the mouse is down and then up on this element (a click effect)
9422          * @param {String} className
9423          * @return {Roo.Element} this
9424          */
9425         addClassOnClick : function(className){
9426             var dom = this.dom;
9427             this.on("mousedown", function(){
9428                 Roo.fly(dom, '_internal').addClass(className);
9429                 var d = Roo.get(document);
9430                 var fn = function(){
9431                     Roo.fly(dom, '_internal').removeClass(className);
9432                     d.removeListener("mouseup", fn);
9433                 };
9434                 d.on("mouseup", fn);
9435             });
9436             return this;
9437         },
9438
9439         /**
9440          * Stops the specified event from bubbling and optionally prevents the default action
9441          * @param {String} eventName
9442          * @param {Boolean} preventDefault (optional) true to prevent the default action too
9443          * @return {Roo.Element} this
9444          */
9445         swallowEvent : function(eventName, preventDefault){
9446             var fn = function(e){
9447                 e.stopPropagation();
9448                 if(preventDefault){
9449                     e.preventDefault();
9450                 }
9451             };
9452             if(eventName instanceof Array){
9453                 for(var i = 0, len = eventName.length; i < len; i++){
9454                      this.on(eventName[i], fn);
9455                 }
9456                 return this;
9457             }
9458             this.on(eventName, fn);
9459             return this;
9460         },
9461
9462         /**
9463          * @private
9464          */
9465         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
9466
9467         /**
9468          * Sizes this element to its parent element's dimensions performing
9469          * neccessary box adjustments.
9470          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
9471          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
9472          * @return {Roo.Element} this
9473          */
9474         fitToParent : function(monitorResize, targetParent) {
9475           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
9476           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
9477           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
9478             return this;
9479           }
9480           var p = Roo.get(targetParent || this.dom.parentNode);
9481           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
9482           if (monitorResize === true) {
9483             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
9484             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
9485           }
9486           return this;
9487         },
9488
9489         /**
9490          * Gets the next sibling, skipping text nodes
9491          * @return {HTMLElement} The next sibling or null
9492          */
9493         getNextSibling : function(){
9494             var n = this.dom.nextSibling;
9495             while(n && n.nodeType != 1){
9496                 n = n.nextSibling;
9497             }
9498             return n;
9499         },
9500
9501         /**
9502          * Gets the previous sibling, skipping text nodes
9503          * @return {HTMLElement} The previous sibling or null
9504          */
9505         getPrevSibling : function(){
9506             var n = this.dom.previousSibling;
9507             while(n && n.nodeType != 1){
9508                 n = n.previousSibling;
9509             }
9510             return n;
9511         },
9512
9513
9514         /**
9515          * Appends the passed element(s) to this element
9516          * @param {String/HTMLElement/Array/Element/CompositeElement} el
9517          * @return {Roo.Element} this
9518          */
9519         appendChild: function(el){
9520             el = Roo.get(el);
9521             el.appendTo(this);
9522             return this;
9523         },
9524
9525         /**
9526          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
9527          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
9528          * automatically generated with the specified attributes.
9529          * @param {HTMLElement} insertBefore (optional) a child element of this element
9530          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
9531          * @return {Roo.Element} The new child element
9532          */
9533         createChild: function(config, insertBefore, returnDom){
9534             config = config || {tag:'div'};
9535             if(insertBefore){
9536                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
9537             }
9538             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
9539         },
9540
9541         /**
9542          * Appends this element to the passed element
9543          * @param {String/HTMLElement/Element} el The new parent element
9544          * @return {Roo.Element} this
9545          */
9546         appendTo: function(el){
9547             el = Roo.getDom(el);
9548             el.appendChild(this.dom);
9549             return this;
9550         },
9551
9552         /**
9553          * Inserts this element before the passed element in the DOM
9554          * @param {String/HTMLElement/Element} el The element to insert before
9555          * @return {Roo.Element} this
9556          */
9557         insertBefore: function(el){
9558             el = Roo.getDom(el);
9559             el.parentNode.insertBefore(this.dom, el);
9560             return this;
9561         },
9562
9563         /**
9564          * Inserts this element after the passed element in the DOM
9565          * @param {String/HTMLElement/Element} el The element to insert after
9566          * @return {Roo.Element} this
9567          */
9568         insertAfter: function(el){
9569             el = Roo.getDom(el);
9570             el.parentNode.insertBefore(this.dom, el.nextSibling);
9571             return this;
9572         },
9573
9574         /**
9575          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
9576          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9577          * @return {Roo.Element} The new child
9578          */
9579         insertFirst: function(el, returnDom){
9580             el = el || {};
9581             if(typeof el == 'object' && !el.nodeType){ // dh config
9582                 return this.createChild(el, this.dom.firstChild, returnDom);
9583             }else{
9584                 el = Roo.getDom(el);
9585                 this.dom.insertBefore(el, this.dom.firstChild);
9586                 return !returnDom ? Roo.get(el) : el;
9587             }
9588         },
9589
9590         /**
9591          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
9592          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
9593          * @param {String} where (optional) 'before' or 'after' defaults to before
9594          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9595          * @return {Roo.Element} the inserted Element
9596          */
9597         insertSibling: function(el, where, returnDom){
9598             where = where ? where.toLowerCase() : 'before';
9599             el = el || {};
9600             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
9601
9602             if(typeof el == 'object' && !el.nodeType){ // dh config
9603                 if(where == 'after' && !this.dom.nextSibling){
9604                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
9605                 }else{
9606                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
9607                 }
9608
9609             }else{
9610                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
9611                             where == 'before' ? this.dom : this.dom.nextSibling);
9612                 if(!returnDom){
9613                     rt = Roo.get(rt);
9614                 }
9615             }
9616             return rt;
9617         },
9618
9619         /**
9620          * Creates and wraps this element with another element
9621          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
9622          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
9623          * @return {HTMLElement/Element} The newly created wrapper element
9624          */
9625         wrap: function(config, returnDom){
9626             if(!config){
9627                 config = {tag: "div"};
9628             }
9629             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
9630             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
9631             return newEl;
9632         },
9633
9634         /**
9635          * Replaces the passed element with this element
9636          * @param {String/HTMLElement/Element} el The element to replace
9637          * @return {Roo.Element} this
9638          */
9639         replace: function(el){
9640             el = Roo.get(el);
9641             this.insertBefore(el);
9642             el.remove();
9643             return this;
9644         },
9645
9646         /**
9647          * Inserts an html fragment into this element
9648          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
9649          * @param {String} html The HTML fragment
9650          * @param {Boolean} returnEl True to return an Roo.Element
9651          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
9652          */
9653         insertHtml : function(where, html, returnEl){
9654             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
9655             return returnEl ? Roo.get(el) : el;
9656         },
9657
9658         /**
9659          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
9660          * @param {Object} o The object with the attributes
9661          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
9662          * @return {Roo.Element} this
9663          */
9664         set : function(o, useSet){
9665             var el = this.dom;
9666             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
9667             for(var attr in o){
9668                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
9669                 if(attr=="cls"){
9670                     el.className = o["cls"];
9671                 }else{
9672                     if(useSet) {
9673                         el.setAttribute(attr, o[attr]);
9674                     } else {
9675                         el[attr] = o[attr];
9676                     }
9677                 }
9678             }
9679             if(o.style){
9680                 Roo.DomHelper.applyStyles(el, o.style);
9681             }
9682             return this;
9683         },
9684
9685         /**
9686          * Convenience method for constructing a KeyMap
9687          * @param {Number/Array/Object/String} key Either a string with the keys to listen for, the numeric key code, array of key codes or an object with the following options:
9688          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
9689          * @param {Function} fn The function to call
9690          * @param {Object} scope (optional) The scope of the function
9691          * @return {Roo.KeyMap} The KeyMap created
9692          */
9693         addKeyListener : function(key, fn, scope){
9694             var config;
9695             if(typeof key != "object" || key instanceof Array){
9696                 config = {
9697                     key: key,
9698                     fn: fn,
9699                     scope: scope
9700                 };
9701             }else{
9702                 config = {
9703                     key : key.key,
9704                     shift : key.shift,
9705                     ctrl : key.ctrl,
9706                     alt : key.alt,
9707                     fn: fn,
9708                     scope: scope
9709                 };
9710             }
9711             return new Roo.KeyMap(this, config);
9712         },
9713
9714         /**
9715          * Creates a KeyMap for this element
9716          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
9717          * @return {Roo.KeyMap} The KeyMap created
9718          */
9719         addKeyMap : function(config){
9720             return new Roo.KeyMap(this, config);
9721         },
9722
9723         /**
9724          * Returns true if this element is scrollable.
9725          * @return {Boolean}
9726          */
9727          isScrollable : function(){
9728             var dom = this.dom;
9729             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
9730         },
9731
9732         /**
9733          * Scrolls this element the specified scroll point. It does NOT do bounds checking so if you scroll to a weird value it will try to do it. For auto bounds checking, use scroll().
9734          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
9735          * @param {Number} value The new scroll value
9736          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9737          * @return {Element} this
9738          */
9739
9740         scrollTo : function(side, value, animate){
9741             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
9742             if(!animate || !A){
9743                 this.dom[prop] = value;
9744             }else{
9745                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
9746                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
9747             }
9748             return this;
9749         },
9750
9751         /**
9752          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
9753          * within this element's scrollable range.
9754          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9755          * @param {Number} distance How far to scroll the element in pixels
9756          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9757          * @return {Boolean} Returns true if a scroll was triggered or false if the element
9758          * was scrolled as far as it could go.
9759          */
9760          scroll : function(direction, distance, animate){
9761              if(!this.isScrollable()){
9762                  return;
9763              }
9764              var el = this.dom;
9765              var l = el.scrollLeft, t = el.scrollTop;
9766              var w = el.scrollWidth, h = el.scrollHeight;
9767              var cw = el.clientWidth, ch = el.clientHeight;
9768              direction = direction.toLowerCase();
9769              var scrolled = false;
9770              var a = this.preanim(arguments, 2);
9771              switch(direction){
9772                  case "l":
9773                  case "left":
9774                      if(w - l > cw){
9775                          var v = Math.min(l + distance, w-cw);
9776                          this.scrollTo("left", v, a);
9777                          scrolled = true;
9778                      }
9779                      break;
9780                 case "r":
9781                 case "right":
9782                      if(l > 0){
9783                          var v = Math.max(l - distance, 0);
9784                          this.scrollTo("left", v, a);
9785                          scrolled = true;
9786                      }
9787                      break;
9788                 case "t":
9789                 case "top":
9790                 case "up":
9791                      if(t > 0){
9792                          var v = Math.max(t - distance, 0);
9793                          this.scrollTo("top", v, a);
9794                          scrolled = true;
9795                      }
9796                      break;
9797                 case "b":
9798                 case "bottom":
9799                 case "down":
9800                      if(h - t > ch){
9801                          var v = Math.min(t + distance, h-ch);
9802                          this.scrollTo("top", v, a);
9803                          scrolled = true;
9804                      }
9805                      break;
9806              }
9807              return scrolled;
9808         },
9809
9810         /**
9811          * Translates the passed page coordinates into left/top css values for this element
9812          * @param {Number/Array} x The page x or an array containing [x, y]
9813          * @param {Number} y The page y
9814          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
9815          */
9816         translatePoints : function(x, y){
9817             if(typeof x == 'object' || x instanceof Array){
9818                 y = x[1]; x = x[0];
9819             }
9820             var p = this.getStyle('position');
9821             var o = this.getXY();
9822
9823             var l = parseInt(this.getStyle('left'), 10);
9824             var t = parseInt(this.getStyle('top'), 10);
9825
9826             if(isNaN(l)){
9827                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
9828             }
9829             if(isNaN(t)){
9830                 t = (p == "relative") ? 0 : this.dom.offsetTop;
9831             }
9832
9833             return {left: (x - o[0] + l), top: (y - o[1] + t)};
9834         },
9835
9836         /**
9837          * Returns the current scroll position of the element.
9838          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
9839          */
9840         getScroll : function(){
9841             var d = this.dom, doc = document;
9842             if(d == doc || d == doc.body){
9843                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
9844                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
9845                 return {left: l, top: t};
9846             }else{
9847                 return {left: d.scrollLeft, top: d.scrollTop};
9848             }
9849         },
9850
9851         /**
9852          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
9853          * are convert to standard 6 digit hex color.
9854          * @param {String} attr The css attribute
9855          * @param {String} defaultValue The default value to use when a valid color isn't found
9856          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
9857          * YUI color anims.
9858          */
9859         getColor : function(attr, defaultValue, prefix){
9860             var v = this.getStyle(attr);
9861             if(!v || v == "transparent" || v == "inherit") {
9862                 return defaultValue;
9863             }
9864             var color = typeof prefix == "undefined" ? "#" : prefix;
9865             if(v.substr(0, 4) == "rgb("){
9866                 var rvs = v.slice(4, v.length -1).split(",");
9867                 for(var i = 0; i < 3; i++){
9868                     var h = parseInt(rvs[i]).toString(16);
9869                     if(h < 16){
9870                         h = "0" + h;
9871                     }
9872                     color += h;
9873                 }
9874             } else {
9875                 if(v.substr(0, 1) == "#"){
9876                     if(v.length == 4) {
9877                         for(var i = 1; i < 4; i++){
9878                             var c = v.charAt(i);
9879                             color +=  c + c;
9880                         }
9881                     }else if(v.length == 7){
9882                         color += v.substr(1);
9883                     }
9884                 }
9885             }
9886             return(color.length > 5 ? color.toLowerCase() : defaultValue);
9887         },
9888
9889         /**
9890          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
9891          * gradient background, rounded corners and a 4-way shadow.
9892          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
9893          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
9894          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
9895          * @return {Roo.Element} this
9896          */
9897         boxWrap : function(cls){
9898             cls = cls || 'x-box';
9899             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
9900             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
9901             return el;
9902         },
9903
9904         /**
9905          * Returns the value of a namespaced attribute from the element's underlying DOM node.
9906          * @param {String} namespace The namespace in which to look for the attribute
9907          * @param {String} name The attribute name
9908          * @return {String} The attribute value
9909          */
9910         getAttributeNS : Roo.isIE ? function(ns, name){
9911             var d = this.dom;
9912             var type = typeof d[ns+":"+name];
9913             if(type != 'undefined' && type != 'unknown'){
9914                 return d[ns+":"+name];
9915             }
9916             return d[name];
9917         } : function(ns, name){
9918             var d = this.dom;
9919             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
9920         },
9921         
9922         
9923         /**
9924          * Sets or Returns the value the dom attribute value
9925          * @param {String|Object} name The attribute name (or object to set multiple attributes)
9926          * @param {String} value (optional) The value to set the attribute to
9927          * @return {String} The attribute value
9928          */
9929         attr : function(name){
9930             if (arguments.length > 1) {
9931                 this.dom.setAttribute(name, arguments[1]);
9932                 return arguments[1];
9933             }
9934             if (typeof(name) == 'object') {
9935                 for(var i in name) {
9936                     this.attr(i, name[i]);
9937                 }
9938                 return name;
9939             }
9940             
9941             
9942             if (!this.dom.hasAttribute(name)) {
9943                 return undefined;
9944             }
9945             return this.dom.getAttribute(name);
9946         }
9947         
9948         
9949         
9950     };
9951
9952     var ep = El.prototype;
9953
9954     /**
9955      * Appends an event handler (Shorthand for addListener)
9956      * @param {String}   eventName     The type of event to append
9957      * @param {Function} fn        The method the event invokes
9958      * @param {Object} scope       (optional) The scope (this object) of the fn
9959      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9960      * @method
9961      */
9962     ep.on = ep.addListener;
9963         // backwards compat
9964     ep.mon = ep.addListener;
9965
9966     /**
9967      * Removes an event handler from this element (shorthand for removeListener)
9968      * @param {String} eventName the type of event to remove
9969      * @param {Function} fn the method the event invokes
9970      * @return {Roo.Element} this
9971      * @method
9972      */
9973     ep.un = ep.removeListener;
9974
9975     /**
9976      * true to automatically adjust width and height settings for box-model issues (default to true)
9977      */
9978     ep.autoBoxAdjust = true;
9979
9980     // private
9981     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
9982
9983     // private
9984     El.addUnits = function(v, defaultUnit){
9985         if(v === "" || v == "auto"){
9986             return v;
9987         }
9988         if(v === undefined){
9989             return '';
9990         }
9991         if(typeof v == "number" || !El.unitPattern.test(v)){
9992             return v + (defaultUnit || 'px');
9993         }
9994         return v;
9995     };
9996
9997     // special markup used throughout Roo when box wrapping elements
9998     El.boxMarkup = '<div class="{0}-tl"><div class="{0}-tr"><div class="{0}-tc"></div></div></div><div class="{0}-ml"><div class="{0}-mr"><div class="{0}-mc"></div></div></div><div class="{0}-bl"><div class="{0}-br"><div class="{0}-bc"></div></div></div>';
9999     /**
10000      * Visibility mode constant - Use visibility to hide element
10001      * @static
10002      * @type Number
10003      */
10004     El.VISIBILITY = 1;
10005     /**
10006      * Visibility mode constant - Use display to hide element
10007      * @static
10008      * @type Number
10009      */
10010     El.DISPLAY = 2;
10011
10012     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10013     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10014     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10015
10016
10017
10018     /**
10019      * @private
10020      */
10021     El.cache = {};
10022
10023     var docEl;
10024
10025     /**
10026      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10027      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10028      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10029      * @return {Element} The Element object
10030      * @static
10031      */
10032     El.get = function(el){
10033         var ex, elm, id;
10034         if(!el){ return null; }
10035         if(typeof el == "string"){ // element id
10036             if(!(elm = document.getElementById(el))){
10037                 return null;
10038             }
10039             if(ex = El.cache[el]){
10040                 ex.dom = elm;
10041             }else{
10042                 ex = El.cache[el] = new El(elm);
10043             }
10044             return ex;
10045         }else if(el.tagName){ // dom element
10046             if(!(id = el.id)){
10047                 id = Roo.id(el);
10048             }
10049             if(ex = El.cache[id]){
10050                 ex.dom = el;
10051             }else{
10052                 ex = El.cache[id] = new El(el);
10053             }
10054             return ex;
10055         }else if(el instanceof El){
10056             if(el != docEl){
10057                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10058                                                               // catch case where it hasn't been appended
10059                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10060             }
10061             return el;
10062         }else if(el.isComposite){
10063             return el;
10064         }else if(el instanceof Array){
10065             return El.select(el);
10066         }else if(el == document){
10067             // create a bogus element object representing the document object
10068             if(!docEl){
10069                 var f = function(){};
10070                 f.prototype = El.prototype;
10071                 docEl = new f();
10072                 docEl.dom = document;
10073             }
10074             return docEl;
10075         }
10076         return null;
10077     };
10078
10079     // private
10080     El.uncache = function(el){
10081         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10082             if(a[i]){
10083                 delete El.cache[a[i].id || a[i]];
10084             }
10085         }
10086     };
10087
10088     // private
10089     // Garbage collection - uncache elements/purge listeners on orphaned elements
10090     // so we don't hold a reference and cause the browser to retain them
10091     El.garbageCollect = function(){
10092         if(!Roo.enableGarbageCollector){
10093             clearInterval(El.collectorThread);
10094             return;
10095         }
10096         for(var eid in El.cache){
10097             var el = El.cache[eid], d = el.dom;
10098             // -------------------------------------------------------
10099             // Determining what is garbage:
10100             // -------------------------------------------------------
10101             // !d
10102             // dom node is null, definitely garbage
10103             // -------------------------------------------------------
10104             // !d.parentNode
10105             // no parentNode == direct orphan, definitely garbage
10106             // -------------------------------------------------------
10107             // !d.offsetParent && !document.getElementById(eid)
10108             // display none elements have no offsetParent so we will
10109             // also try to look it up by it's id. However, check
10110             // offsetParent first so we don't do unneeded lookups.
10111             // This enables collection of elements that are not orphans
10112             // directly, but somewhere up the line they have an orphan
10113             // parent.
10114             // -------------------------------------------------------
10115             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10116                 delete El.cache[eid];
10117                 if(d && Roo.enableListenerCollection){
10118                     E.purgeElement(d);
10119                 }
10120             }
10121         }
10122     }
10123     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10124
10125
10126     // dom is optional
10127     El.Flyweight = function(dom){
10128         this.dom = dom;
10129     };
10130     El.Flyweight.prototype = El.prototype;
10131
10132     El._flyweights = {};
10133     /**
10134      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10135      * the dom node can be overwritten by other code.
10136      * @param {String/HTMLElement} el The dom node or id
10137      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10138      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10139      * @static
10140      * @return {Element} The shared Element object
10141      */
10142     El.fly = function(el, named){
10143         named = named || '_global';
10144         el = Roo.getDom(el);
10145         if(!el){
10146             return null;
10147         }
10148         if(!El._flyweights[named]){
10149             El._flyweights[named] = new El.Flyweight();
10150         }
10151         El._flyweights[named].dom = el;
10152         return El._flyweights[named];
10153     };
10154
10155     /**
10156      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10157      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10158      * Shorthand of {@link Roo.Element#get}
10159      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10160      * @return {Element} The Element object
10161      * @member Roo
10162      * @method get
10163      */
10164     Roo.get = El.get;
10165     /**
10166      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10167      * the dom node can be overwritten by other code.
10168      * Shorthand of {@link Roo.Element#fly}
10169      * @param {String/HTMLElement} el The dom node or id
10170      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10171      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10172      * @static
10173      * @return {Element} The shared Element object
10174      * @member Roo
10175      * @method fly
10176      */
10177     Roo.fly = El.fly;
10178
10179     // speedy lookup for elements never to box adjust
10180     var noBoxAdjust = Roo.isStrict ? {
10181         select:1
10182     } : {
10183         input:1, select:1, textarea:1
10184     };
10185     if(Roo.isIE || Roo.isGecko){
10186         noBoxAdjust['button'] = 1;
10187     }
10188
10189
10190     Roo.EventManager.on(window, 'unload', function(){
10191         delete El.cache;
10192         delete El._flyweights;
10193     });
10194 })();
10195
10196
10197
10198
10199 if(Roo.DomQuery){
10200     Roo.Element.selectorFunction = Roo.DomQuery.select;
10201 }
10202
10203 Roo.Element.select = function(selector, unique, root){
10204     var els;
10205     if(typeof selector == "string"){
10206         els = Roo.Element.selectorFunction(selector, root);
10207     }else if(selector.length !== undefined){
10208         els = selector;
10209     }else{
10210         throw "Invalid selector";
10211     }
10212     if(unique === true){
10213         return new Roo.CompositeElement(els);
10214     }else{
10215         return new Roo.CompositeElementLite(els);
10216     }
10217 };
10218 /**
10219  * Selects elements based on the passed CSS selector to enable working on them as 1.
10220  * @param {String/Array} selector The CSS selector or an array of elements
10221  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10222  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10223  * @return {CompositeElementLite/CompositeElement}
10224  * @member Roo
10225  * @method select
10226  */
10227 Roo.select = Roo.Element.select;
10228
10229
10230
10231
10232
10233
10234
10235
10236
10237
10238
10239
10240
10241
10242 /*
10243  * Based on:
10244  * Ext JS Library 1.1.1
10245  * Copyright(c) 2006-2007, Ext JS, LLC.
10246  *
10247  * Originally Released Under LGPL - original licence link has changed is not relivant.
10248  *
10249  * Fork - LGPL
10250  * <script type="text/javascript">
10251  */
10252
10253
10254
10255 //Notifies Element that fx methods are available
10256 Roo.enableFx = true;
10257
10258 /**
10259  * @class Roo.Fx
10260  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10261  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10262  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10263  * Element effects to work.</p><br/>
10264  *
10265  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10266  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10267  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10268  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10269  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10270  * expected results and should be done with care.</p><br/>
10271  *
10272  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10273  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10274 <pre>
10275 Value  Description
10276 -----  -----------------------------
10277 tl     The top left corner
10278 t      The center of the top edge
10279 tr     The top right corner
10280 l      The center of the left edge
10281 r      The center of the right edge
10282 bl     The bottom left corner
10283 b      The center of the bottom edge
10284 br     The bottom right corner
10285 </pre>
10286  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10287  * below are common options that can be passed to any Fx method.</b>
10288  * @cfg {Function} callback A function called when the effect is finished
10289  * @cfg {Object} scope The scope of the effect function
10290  * @cfg {String} easing A valid Easing value for the effect
10291  * @cfg {String} afterCls A css class to apply after the effect
10292  * @cfg {Number} duration The length of time (in seconds) that the effect should last
10293  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
10294  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
10295  * effects that end with the element being visually hidden, ignored otherwise)
10296  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
10297  * a function which returns such a specification that will be applied to the Element after the effect finishes
10298  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
10299  * @cfg {Boolean} concurrent Whether to allow subsequently-queued effects to run at the same time as the current effect, or to ensure that they run in sequence
10300  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
10301  */
10302 Roo.Fx = {
10303         /**
10304          * Slides the element into view.  An anchor point can be optionally passed to set the point of
10305          * origin for the slide effect.  This function automatically handles wrapping the element with
10306          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10307          * Usage:
10308          *<pre><code>
10309 // default: slide the element in from the top
10310 el.slideIn();
10311
10312 // custom: slide the element in from the right with a 2-second duration
10313 el.slideIn('r', { duration: 2 });
10314
10315 // common config options shown with default values
10316 el.slideIn('t', {
10317     easing: 'easeOut',
10318     duration: .5
10319 });
10320 </code></pre>
10321          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10322          * @param {Object} options (optional) Object literal with any of the Fx config options
10323          * @return {Roo.Element} The Element
10324          */
10325     slideIn : function(anchor, o){
10326         var el = this.getFxEl();
10327         o = o || {};
10328
10329         el.queueFx(o, function(){
10330
10331             anchor = anchor || "t";
10332
10333             // fix display to visibility
10334             this.fixDisplay();
10335
10336             // restore values after effect
10337             var r = this.getFxRestore();
10338             var b = this.getBox();
10339             // fixed size for slide
10340             this.setSize(b);
10341
10342             // wrap if needed
10343             var wrap = this.fxWrap(r.pos, o, "hidden");
10344
10345             var st = this.dom.style;
10346             st.visibility = "visible";
10347             st.position = "absolute";
10348
10349             // clear out temp styles after slide and unwrap
10350             var after = function(){
10351                 el.fxUnwrap(wrap, r.pos, o);
10352                 st.width = r.width;
10353                 st.height = r.height;
10354                 el.afterFx(o);
10355             };
10356             // time to calc the positions
10357             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
10358
10359             switch(anchor.toLowerCase()){
10360                 case "t":
10361                     wrap.setSize(b.width, 0);
10362                     st.left = st.bottom = "0";
10363                     a = {height: bh};
10364                 break;
10365                 case "l":
10366                     wrap.setSize(0, b.height);
10367                     st.right = st.top = "0";
10368                     a = {width: bw};
10369                 break;
10370                 case "r":
10371                     wrap.setSize(0, b.height);
10372                     wrap.setX(b.right);
10373                     st.left = st.top = "0";
10374                     a = {width: bw, points: pt};
10375                 break;
10376                 case "b":
10377                     wrap.setSize(b.width, 0);
10378                     wrap.setY(b.bottom);
10379                     st.left = st.top = "0";
10380                     a = {height: bh, points: pt};
10381                 break;
10382                 case "tl":
10383                     wrap.setSize(0, 0);
10384                     st.right = st.bottom = "0";
10385                     a = {width: bw, height: bh};
10386                 break;
10387                 case "bl":
10388                     wrap.setSize(0, 0);
10389                     wrap.setY(b.y+b.height);
10390                     st.right = st.top = "0";
10391                     a = {width: bw, height: bh, points: pt};
10392                 break;
10393                 case "br":
10394                     wrap.setSize(0, 0);
10395                     wrap.setXY([b.right, b.bottom]);
10396                     st.left = st.top = "0";
10397                     a = {width: bw, height: bh, points: pt};
10398                 break;
10399                 case "tr":
10400                     wrap.setSize(0, 0);
10401                     wrap.setX(b.x+b.width);
10402                     st.left = st.bottom = "0";
10403                     a = {width: bw, height: bh, points: pt};
10404                 break;
10405             }
10406             this.dom.style.visibility = "visible";
10407             wrap.show();
10408
10409             arguments.callee.anim = wrap.fxanim(a,
10410                 o,
10411                 'motion',
10412                 .5,
10413                 'easeOut', after);
10414         });
10415         return this;
10416     },
10417     
10418         /**
10419          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
10420          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
10421          * 'hidden') but block elements will still take up space in the document.  The element must be removed
10422          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
10423          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
10424          * Usage:
10425          *<pre><code>
10426 // default: slide the element out to the top
10427 el.slideOut();
10428
10429 // custom: slide the element out to the right with a 2-second duration
10430 el.slideOut('r', { duration: 2 });
10431
10432 // common config options shown with default values
10433 el.slideOut('t', {
10434     easing: 'easeOut',
10435     duration: .5,
10436     remove: false,
10437     useDisplay: false
10438 });
10439 </code></pre>
10440          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
10441          * @param {Object} options (optional) Object literal with any of the Fx config options
10442          * @return {Roo.Element} The Element
10443          */
10444     slideOut : function(anchor, o){
10445         var el = this.getFxEl();
10446         o = o || {};
10447
10448         el.queueFx(o, function(){
10449
10450             anchor = anchor || "t";
10451
10452             // restore values after effect
10453             var r = this.getFxRestore();
10454             
10455             var b = this.getBox();
10456             // fixed size for slide
10457             this.setSize(b);
10458
10459             // wrap if needed
10460             var wrap = this.fxWrap(r.pos, o, "visible");
10461
10462             var st = this.dom.style;
10463             st.visibility = "visible";
10464             st.position = "absolute";
10465
10466             wrap.setSize(b);
10467
10468             var after = function(){
10469                 if(o.useDisplay){
10470                     el.setDisplayed(false);
10471                 }else{
10472                     el.hide();
10473                 }
10474
10475                 el.fxUnwrap(wrap, r.pos, o);
10476
10477                 st.width = r.width;
10478                 st.height = r.height;
10479
10480                 el.afterFx(o);
10481             };
10482
10483             var a, zero = {to: 0};
10484             switch(anchor.toLowerCase()){
10485                 case "t":
10486                     st.left = st.bottom = "0";
10487                     a = {height: zero};
10488                 break;
10489                 case "l":
10490                     st.right = st.top = "0";
10491                     a = {width: zero};
10492                 break;
10493                 case "r":
10494                     st.left = st.top = "0";
10495                     a = {width: zero, points: {to:[b.right, b.y]}};
10496                 break;
10497                 case "b":
10498                     st.left = st.top = "0";
10499                     a = {height: zero, points: {to:[b.x, b.bottom]}};
10500                 break;
10501                 case "tl":
10502                     st.right = st.bottom = "0";
10503                     a = {width: zero, height: zero};
10504                 break;
10505                 case "bl":
10506                     st.right = st.top = "0";
10507                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
10508                 break;
10509                 case "br":
10510                     st.left = st.top = "0";
10511                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
10512                 break;
10513                 case "tr":
10514                     st.left = st.bottom = "0";
10515                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
10516                 break;
10517             }
10518
10519             arguments.callee.anim = wrap.fxanim(a,
10520                 o,
10521                 'motion',
10522                 .5,
10523                 "easeOut", after);
10524         });
10525         return this;
10526     },
10527
10528         /**
10529          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
10530          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
10531          * The element must be removed from the DOM using the 'remove' config option if desired.
10532          * Usage:
10533          *<pre><code>
10534 // default
10535 el.puff();
10536
10537 // common config options shown with default values
10538 el.puff({
10539     easing: 'easeOut',
10540     duration: .5,
10541     remove: false,
10542     useDisplay: false
10543 });
10544 </code></pre>
10545          * @param {Object} options (optional) Object literal with any of the Fx config options
10546          * @return {Roo.Element} The Element
10547          */
10548     puff : function(o){
10549         var el = this.getFxEl();
10550         o = o || {};
10551
10552         el.queueFx(o, function(){
10553             this.clearOpacity();
10554             this.show();
10555
10556             // restore values after effect
10557             var r = this.getFxRestore();
10558             var st = this.dom.style;
10559
10560             var after = function(){
10561                 if(o.useDisplay){
10562                     el.setDisplayed(false);
10563                 }else{
10564                     el.hide();
10565                 }
10566
10567                 el.clearOpacity();
10568
10569                 el.setPositioning(r.pos);
10570                 st.width = r.width;
10571                 st.height = r.height;
10572                 st.fontSize = '';
10573                 el.afterFx(o);
10574             };
10575
10576             var width = this.getWidth();
10577             var height = this.getHeight();
10578
10579             arguments.callee.anim = this.fxanim({
10580                     width : {to: this.adjustWidth(width * 2)},
10581                     height : {to: this.adjustHeight(height * 2)},
10582                     points : {by: [-(width * .5), -(height * .5)]},
10583                     opacity : {to: 0},
10584                     fontSize: {to:200, unit: "%"}
10585                 },
10586                 o,
10587                 'motion',
10588                 .5,
10589                 "easeOut", after);
10590         });
10591         return this;
10592     },
10593
10594         /**
10595          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
10596          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
10597          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
10598          * Usage:
10599          *<pre><code>
10600 // default
10601 el.switchOff();
10602
10603 // all config options shown with default values
10604 el.switchOff({
10605     easing: 'easeIn',
10606     duration: .3,
10607     remove: false,
10608     useDisplay: false
10609 });
10610 </code></pre>
10611          * @param {Object} options (optional) Object literal with any of the Fx config options
10612          * @return {Roo.Element} The Element
10613          */
10614     switchOff : function(o){
10615         var el = this.getFxEl();
10616         o = o || {};
10617
10618         el.queueFx(o, function(){
10619             this.clearOpacity();
10620             this.clip();
10621
10622             // restore values after effect
10623             var r = this.getFxRestore();
10624             var st = this.dom.style;
10625
10626             var after = function(){
10627                 if(o.useDisplay){
10628                     el.setDisplayed(false);
10629                 }else{
10630                     el.hide();
10631                 }
10632
10633                 el.clearOpacity();
10634                 el.setPositioning(r.pos);
10635                 st.width = r.width;
10636                 st.height = r.height;
10637
10638                 el.afterFx(o);
10639             };
10640
10641             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
10642                 this.clearOpacity();
10643                 (function(){
10644                     this.fxanim({
10645                         height:{to:1},
10646                         points:{by:[0, this.getHeight() * .5]}
10647                     }, o, 'motion', 0.3, 'easeIn', after);
10648                 }).defer(100, this);
10649             });
10650         });
10651         return this;
10652     },
10653
10654     /**
10655      * Highlights the Element by setting a color (applies to the background-color by default, but can be
10656      * changed using the "attr" config option) and then fading back to the original color. If no original
10657      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
10658      * Usage:
10659 <pre><code>
10660 // default: highlight background to yellow
10661 el.highlight();
10662
10663 // custom: highlight foreground text to blue for 2 seconds
10664 el.highlight("0000ff", { attr: 'color', duration: 2 });
10665
10666 // common config options shown with default values
10667 el.highlight("ffff9c", {
10668     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
10669     endColor: (current color) or "ffffff",
10670     easing: 'easeIn',
10671     duration: 1
10672 });
10673 </code></pre>
10674      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
10675      * @param {Object} options (optional) Object literal with any of the Fx config options
10676      * @return {Roo.Element} The Element
10677      */ 
10678     highlight : function(color, o){
10679         var el = this.getFxEl();
10680         o = o || {};
10681
10682         el.queueFx(o, function(){
10683             color = color || "ffff9c";
10684             attr = o.attr || "backgroundColor";
10685
10686             this.clearOpacity();
10687             this.show();
10688
10689             var origColor = this.getColor(attr);
10690             var restoreColor = this.dom.style[attr];
10691             endColor = (o.endColor || origColor) || "ffffff";
10692
10693             var after = function(){
10694                 el.dom.style[attr] = restoreColor;
10695                 el.afterFx(o);
10696             };
10697
10698             var a = {};
10699             a[attr] = {from: color, to: endColor};
10700             arguments.callee.anim = this.fxanim(a,
10701                 o,
10702                 'color',
10703                 1,
10704                 'easeIn', after);
10705         });
10706         return this;
10707     },
10708
10709    /**
10710     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
10711     * Usage:
10712 <pre><code>
10713 // default: a single light blue ripple
10714 el.frame();
10715
10716 // custom: 3 red ripples lasting 3 seconds total
10717 el.frame("ff0000", 3, { duration: 3 });
10718
10719 // common config options shown with default values
10720 el.frame("C3DAF9", 1, {
10721     duration: 1 //duration of entire animation (not each individual ripple)
10722     // Note: Easing is not configurable and will be ignored if included
10723 });
10724 </code></pre>
10725     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
10726     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
10727     * @param {Object} options (optional) Object literal with any of the Fx config options
10728     * @return {Roo.Element} The Element
10729     */
10730     frame : function(color, count, o){
10731         var el = this.getFxEl();
10732         o = o || {};
10733
10734         el.queueFx(o, function(){
10735             color = color || "#C3DAF9";
10736             if(color.length == 6){
10737                 color = "#" + color;
10738             }
10739             count = count || 1;
10740             duration = o.duration || 1;
10741             this.show();
10742
10743             var b = this.getBox();
10744             var animFn = function(){
10745                 var proxy = this.createProxy({
10746
10747                      style:{
10748                         visbility:"hidden",
10749                         position:"absolute",
10750                         "z-index":"35000", // yee haw
10751                         border:"0px solid " + color
10752                      }
10753                   });
10754                 var scale = Roo.isBorderBox ? 2 : 1;
10755                 proxy.animate({
10756                     top:{from:b.y, to:b.y - 20},
10757                     left:{from:b.x, to:b.x - 20},
10758                     borderWidth:{from:0, to:10},
10759                     opacity:{from:1, to:0},
10760                     height:{from:b.height, to:(b.height + (20*scale))},
10761                     width:{from:b.width, to:(b.width + (20*scale))}
10762                 }, duration, function(){
10763                     proxy.remove();
10764                 });
10765                 if(--count > 0){
10766                      animFn.defer((duration/2)*1000, this);
10767                 }else{
10768                     el.afterFx(o);
10769                 }
10770             };
10771             animFn.call(this);
10772         });
10773         return this;
10774     },
10775
10776    /**
10777     * Creates a pause before any subsequent queued effects begin.  If there are
10778     * no effects queued after the pause it will have no effect.
10779     * Usage:
10780 <pre><code>
10781 el.pause(1);
10782 </code></pre>
10783     * @param {Number} seconds The length of time to pause (in seconds)
10784     * @return {Roo.Element} The Element
10785     */
10786     pause : function(seconds){
10787         var el = this.getFxEl();
10788         var o = {};
10789
10790         el.queueFx(o, function(){
10791             setTimeout(function(){
10792                 el.afterFx(o);
10793             }, seconds * 1000);
10794         });
10795         return this;
10796     },
10797
10798    /**
10799     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
10800     * using the "endOpacity" config option.
10801     * Usage:
10802 <pre><code>
10803 // default: fade in from opacity 0 to 100%
10804 el.fadeIn();
10805
10806 // custom: fade in from opacity 0 to 75% over 2 seconds
10807 el.fadeIn({ endOpacity: .75, duration: 2});
10808
10809 // common config options shown with default values
10810 el.fadeIn({
10811     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
10812     easing: 'easeOut',
10813     duration: .5
10814 });
10815 </code></pre>
10816     * @param {Object} options (optional) Object literal with any of the Fx config options
10817     * @return {Roo.Element} The Element
10818     */
10819     fadeIn : function(o){
10820         var el = this.getFxEl();
10821         o = o || {};
10822         el.queueFx(o, function(){
10823             this.setOpacity(0);
10824             this.fixDisplay();
10825             this.dom.style.visibility = 'visible';
10826             var to = o.endOpacity || 1;
10827             arguments.callee.anim = this.fxanim({opacity:{to:to}},
10828                 o, null, .5, "easeOut", function(){
10829                 if(to == 1){
10830                     this.clearOpacity();
10831                 }
10832                 el.afterFx(o);
10833             });
10834         });
10835         return this;
10836     },
10837
10838    /**
10839     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
10840     * using the "endOpacity" config option.
10841     * Usage:
10842 <pre><code>
10843 // default: fade out from the element's current opacity to 0
10844 el.fadeOut();
10845
10846 // custom: fade out from the element's current opacity to 25% over 2 seconds
10847 el.fadeOut({ endOpacity: .25, duration: 2});
10848
10849 // common config options shown with default values
10850 el.fadeOut({
10851     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
10852     easing: 'easeOut',
10853     duration: .5
10854     remove: false,
10855     useDisplay: false
10856 });
10857 </code></pre>
10858     * @param {Object} options (optional) Object literal with any of the Fx config options
10859     * @return {Roo.Element} The Element
10860     */
10861     fadeOut : function(o){
10862         var el = this.getFxEl();
10863         o = o || {};
10864         el.queueFx(o, function(){
10865             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
10866                 o, null, .5, "easeOut", function(){
10867                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
10868                      this.dom.style.display = "none";
10869                 }else{
10870                      this.dom.style.visibility = "hidden";
10871                 }
10872                 this.clearOpacity();
10873                 el.afterFx(o);
10874             });
10875         });
10876         return this;
10877     },
10878
10879    /**
10880     * Animates the transition of an element's dimensions from a starting height/width
10881     * to an ending height/width.
10882     * Usage:
10883 <pre><code>
10884 // change height and width to 100x100 pixels
10885 el.scale(100, 100);
10886
10887 // common config options shown with default values.  The height and width will default to
10888 // the element's existing values if passed as null.
10889 el.scale(
10890     [element's width],
10891     [element's height], {
10892     easing: 'easeOut',
10893     duration: .35
10894 });
10895 </code></pre>
10896     * @param {Number} width  The new width (pass undefined to keep the original width)
10897     * @param {Number} height  The new height (pass undefined to keep the original height)
10898     * @param {Object} options (optional) Object literal with any of the Fx config options
10899     * @return {Roo.Element} The Element
10900     */
10901     scale : function(w, h, o){
10902         this.shift(Roo.apply({}, o, {
10903             width: w,
10904             height: h
10905         }));
10906         return this;
10907     },
10908
10909    /**
10910     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
10911     * Any of these properties not specified in the config object will not be changed.  This effect 
10912     * requires that at least one new dimension, position or opacity setting must be passed in on
10913     * the config object in order for the function to have any effect.
10914     * Usage:
10915 <pre><code>
10916 // slide the element horizontally to x position 200 while changing the height and opacity
10917 el.shift({ x: 200, height: 50, opacity: .8 });
10918
10919 // common config options shown with default values.
10920 el.shift({
10921     width: [element's width],
10922     height: [element's height],
10923     x: [element's x position],
10924     y: [element's y position],
10925     opacity: [element's opacity],
10926     easing: 'easeOut',
10927     duration: .35
10928 });
10929 </code></pre>
10930     * @param {Object} options  Object literal with any of the Fx config options
10931     * @return {Roo.Element} The Element
10932     */
10933     shift : function(o){
10934         var el = this.getFxEl();
10935         o = o || {};
10936         el.queueFx(o, function(){
10937             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
10938             if(w !== undefined){
10939                 a.width = {to: this.adjustWidth(w)};
10940             }
10941             if(h !== undefined){
10942                 a.height = {to: this.adjustHeight(h)};
10943             }
10944             if(x !== undefined || y !== undefined){
10945                 a.points = {to: [
10946                     x !== undefined ? x : this.getX(),
10947                     y !== undefined ? y : this.getY()
10948                 ]};
10949             }
10950             if(op !== undefined){
10951                 a.opacity = {to: op};
10952             }
10953             if(o.xy !== undefined){
10954                 a.points = {to: o.xy};
10955             }
10956             arguments.callee.anim = this.fxanim(a,
10957                 o, 'motion', .35, "easeOut", function(){
10958                 el.afterFx(o);
10959             });
10960         });
10961         return this;
10962     },
10963
10964         /**
10965          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
10966          * ending point of the effect.
10967          * Usage:
10968          *<pre><code>
10969 // default: slide the element downward while fading out
10970 el.ghost();
10971
10972 // custom: slide the element out to the right with a 2-second duration
10973 el.ghost('r', { duration: 2 });
10974
10975 // common config options shown with default values
10976 el.ghost('b', {
10977     easing: 'easeOut',
10978     duration: .5
10979     remove: false,
10980     useDisplay: false
10981 });
10982 </code></pre>
10983          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
10984          * @param {Object} options (optional) Object literal with any of the Fx config options
10985          * @return {Roo.Element} The Element
10986          */
10987     ghost : function(anchor, o){
10988         var el = this.getFxEl();
10989         o = o || {};
10990
10991         el.queueFx(o, function(){
10992             anchor = anchor || "b";
10993
10994             // restore values after effect
10995             var r = this.getFxRestore();
10996             var w = this.getWidth(),
10997                 h = this.getHeight();
10998
10999             var st = this.dom.style;
11000
11001             var after = function(){
11002                 if(o.useDisplay){
11003                     el.setDisplayed(false);
11004                 }else{
11005                     el.hide();
11006                 }
11007
11008                 el.clearOpacity();
11009                 el.setPositioning(r.pos);
11010                 st.width = r.width;
11011                 st.height = r.height;
11012
11013                 el.afterFx(o);
11014             };
11015
11016             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11017             switch(anchor.toLowerCase()){
11018                 case "t":
11019                     pt.by = [0, -h];
11020                 break;
11021                 case "l":
11022                     pt.by = [-w, 0];
11023                 break;
11024                 case "r":
11025                     pt.by = [w, 0];
11026                 break;
11027                 case "b":
11028                     pt.by = [0, h];
11029                 break;
11030                 case "tl":
11031                     pt.by = [-w, -h];
11032                 break;
11033                 case "bl":
11034                     pt.by = [-w, h];
11035                 break;
11036                 case "br":
11037                     pt.by = [w, h];
11038                 break;
11039                 case "tr":
11040                     pt.by = [w, -h];
11041                 break;
11042             }
11043
11044             arguments.callee.anim = this.fxanim(a,
11045                 o,
11046                 'motion',
11047                 .5,
11048                 "easeOut", after);
11049         });
11050         return this;
11051     },
11052
11053         /**
11054          * Ensures that all effects queued after syncFx is called on the element are
11055          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11056          * @return {Roo.Element} The Element
11057          */
11058     syncFx : function(){
11059         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11060             block : false,
11061             concurrent : true,
11062             stopFx : false
11063         });
11064         return this;
11065     },
11066
11067         /**
11068          * Ensures that all effects queued after sequenceFx is called on the element are
11069          * run in sequence.  This is the opposite of {@link #syncFx}.
11070          * @return {Roo.Element} The Element
11071          */
11072     sequenceFx : function(){
11073         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11074             block : false,
11075             concurrent : false,
11076             stopFx : false
11077         });
11078         return this;
11079     },
11080
11081         /* @private */
11082     nextFx : function(){
11083         var ef = this.fxQueue[0];
11084         if(ef){
11085             ef.call(this);
11086         }
11087     },
11088
11089         /**
11090          * Returns true if the element has any effects actively running or queued, else returns false.
11091          * @return {Boolean} True if element has active effects, else false
11092          */
11093     hasActiveFx : function(){
11094         return this.fxQueue && this.fxQueue[0];
11095     },
11096
11097         /**
11098          * Stops any running effects and clears the element's internal effects queue if it contains
11099          * any additional effects that haven't started yet.
11100          * @return {Roo.Element} The Element
11101          */
11102     stopFx : function(){
11103         if(this.hasActiveFx()){
11104             var cur = this.fxQueue[0];
11105             if(cur && cur.anim && cur.anim.isAnimated()){
11106                 this.fxQueue = [cur]; // clear out others
11107                 cur.anim.stop(true);
11108             }
11109         }
11110         return this;
11111     },
11112
11113         /* @private */
11114     beforeFx : function(o){
11115         if(this.hasActiveFx() && !o.concurrent){
11116            if(o.stopFx){
11117                this.stopFx();
11118                return true;
11119            }
11120            return false;
11121         }
11122         return true;
11123     },
11124
11125         /**
11126          * Returns true if the element is currently blocking so that no other effect can be queued
11127          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11128          * used to ensure that an effect initiated by a user action runs to completion prior to the
11129          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11130          * @return {Boolean} True if blocking, else false
11131          */
11132     hasFxBlock : function(){
11133         var q = this.fxQueue;
11134         return q && q[0] && q[0].block;
11135     },
11136
11137         /* @private */
11138     queueFx : function(o, fn){
11139         if(!this.fxQueue){
11140             this.fxQueue = [];
11141         }
11142         if(!this.hasFxBlock()){
11143             Roo.applyIf(o, this.fxDefaults);
11144             if(!o.concurrent){
11145                 var run = this.beforeFx(o);
11146                 fn.block = o.block;
11147                 this.fxQueue.push(fn);
11148                 if(run){
11149                     this.nextFx();
11150                 }
11151             }else{
11152                 fn.call(this);
11153             }
11154         }
11155         return this;
11156     },
11157
11158         /* @private */
11159     fxWrap : function(pos, o, vis){
11160         var wrap;
11161         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11162             var wrapXY;
11163             if(o.fixPosition){
11164                 wrapXY = this.getXY();
11165             }
11166             var div = document.createElement("div");
11167             div.style.visibility = vis;
11168             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11169             wrap.setPositioning(pos);
11170             if(wrap.getStyle("position") == "static"){
11171                 wrap.position("relative");
11172             }
11173             this.clearPositioning('auto');
11174             wrap.clip();
11175             wrap.dom.appendChild(this.dom);
11176             if(wrapXY){
11177                 wrap.setXY(wrapXY);
11178             }
11179         }
11180         return wrap;
11181     },
11182
11183         /* @private */
11184     fxUnwrap : function(wrap, pos, o){
11185         this.clearPositioning();
11186         this.setPositioning(pos);
11187         if(!o.wrap){
11188             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11189             wrap.remove();
11190         }
11191     },
11192
11193         /* @private */
11194     getFxRestore : function(){
11195         var st = this.dom.style;
11196         return {pos: this.getPositioning(), width: st.width, height : st.height};
11197     },
11198
11199         /* @private */
11200     afterFx : function(o){
11201         if(o.afterStyle){
11202             this.applyStyles(o.afterStyle);
11203         }
11204         if(o.afterCls){
11205             this.addClass(o.afterCls);
11206         }
11207         if(o.remove === true){
11208             this.remove();
11209         }
11210         Roo.callback(o.callback, o.scope, [this]);
11211         if(!o.concurrent){
11212             this.fxQueue.shift();
11213             this.nextFx();
11214         }
11215     },
11216
11217         /* @private */
11218     getFxEl : function(){ // support for composite element fx
11219         return Roo.get(this.dom);
11220     },
11221
11222         /* @private */
11223     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11224         animType = animType || 'run';
11225         opt = opt || {};
11226         var anim = Roo.lib.Anim[animType](
11227             this.dom, args,
11228             (opt.duration || defaultDur) || .35,
11229             (opt.easing || defaultEase) || 'easeOut',
11230             function(){
11231                 Roo.callback(cb, this);
11232             },
11233             this
11234         );
11235         opt.anim = anim;
11236         return anim;
11237     }
11238 };
11239
11240 // backwords compat
11241 Roo.Fx.resize = Roo.Fx.scale;
11242
11243 //When included, Roo.Fx is automatically applied to Element so that all basic
11244 //effects are available directly via the Element API
11245 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11246  * Based on:
11247  * Ext JS Library 1.1.1
11248  * Copyright(c) 2006-2007, Ext JS, LLC.
11249  *
11250  * Originally Released Under LGPL - original licence link has changed is not relivant.
11251  *
11252  * Fork - LGPL
11253  * <script type="text/javascript">
11254  */
11255
11256
11257 /**
11258  * @class Roo.CompositeElement
11259  * Standard composite class. Creates a Roo.Element for every element in the collection.
11260  * <br><br>
11261  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11262  * actions will be performed on all the elements in this collection.</b>
11263  * <br><br>
11264  * All methods return <i>this</i> and can be chained.
11265  <pre><code>
11266  var els = Roo.select("#some-el div.some-class", true);
11267  // or select directly from an existing element
11268  var el = Roo.get('some-el');
11269  el.select('div.some-class', true);
11270
11271  els.setWidth(100); // all elements become 100 width
11272  els.hide(true); // all elements fade out and hide
11273  // or
11274  els.setWidth(100).hide(true);
11275  </code></pre>
11276  */
11277 Roo.CompositeElement = function(els){
11278     this.elements = [];
11279     this.addElements(els);
11280 };
11281 Roo.CompositeElement.prototype = {
11282     isComposite: true,
11283     addElements : function(els){
11284         if(!els) {
11285             return this;
11286         }
11287         if(typeof els == "string"){
11288             els = Roo.Element.selectorFunction(els);
11289         }
11290         var yels = this.elements;
11291         var index = yels.length-1;
11292         for(var i = 0, len = els.length; i < len; i++) {
11293                 yels[++index] = Roo.get(els[i]);
11294         }
11295         return this;
11296     },
11297
11298     /**
11299     * Clears this composite and adds the elements returned by the passed selector.
11300     * @param {String/Array} els A string CSS selector, an array of elements or an element
11301     * @return {CompositeElement} this
11302     */
11303     fill : function(els){
11304         this.elements = [];
11305         this.add(els);
11306         return this;
11307     },
11308
11309     /**
11310     * Filters this composite to only elements that match the passed selector.
11311     * @param {String} selector A string CSS selector
11312     * @param {Boolean} inverse return inverse filter (not matches)
11313     * @return {CompositeElement} this
11314     */
11315     filter : function(selector, inverse){
11316         var els = [];
11317         inverse = inverse || false;
11318         this.each(function(el){
11319             var match = inverse ? !el.is(selector) : el.is(selector);
11320             if(match){
11321                 els[els.length] = el.dom;
11322             }
11323         });
11324         this.fill(els);
11325         return this;
11326     },
11327
11328     invoke : function(fn, args){
11329         var els = this.elements;
11330         for(var i = 0, len = els.length; i < len; i++) {
11331                 Roo.Element.prototype[fn].apply(els[i], args);
11332         }
11333         return this;
11334     },
11335     /**
11336     * Adds elements to this composite.
11337     * @param {String/Array} els A string CSS selector, an array of elements or an element
11338     * @return {CompositeElement} this
11339     */
11340     add : function(els){
11341         if(typeof els == "string"){
11342             this.addElements(Roo.Element.selectorFunction(els));
11343         }else if(els.length !== undefined){
11344             this.addElements(els);
11345         }else{
11346             this.addElements([els]);
11347         }
11348         return this;
11349     },
11350     /**
11351     * Calls the passed function passing (el, this, index) for each element in this composite.
11352     * @param {Function} fn The function to call
11353     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11354     * @return {CompositeElement} this
11355     */
11356     each : function(fn, scope){
11357         var els = this.elements;
11358         for(var i = 0, len = els.length; i < len; i++){
11359             if(fn.call(scope || els[i], els[i], this, i) === false) {
11360                 break;
11361             }
11362         }
11363         return this;
11364     },
11365
11366     /**
11367      * Returns the Element object at the specified index
11368      * @param {Number} index
11369      * @return {Roo.Element}
11370      */
11371     item : function(index){
11372         return this.elements[index] || null;
11373     },
11374
11375     /**
11376      * Returns the first Element
11377      * @return {Roo.Element}
11378      */
11379     first : function(){
11380         return this.item(0);
11381     },
11382
11383     /**
11384      * Returns the last Element
11385      * @return {Roo.Element}
11386      */
11387     last : function(){
11388         return this.item(this.elements.length-1);
11389     },
11390
11391     /**
11392      * Returns the number of elements in this composite
11393      * @return Number
11394      */
11395     getCount : function(){
11396         return this.elements.length;
11397     },
11398
11399     /**
11400      * Returns true if this composite contains the passed element
11401      * @return Boolean
11402      */
11403     contains : function(el){
11404         return this.indexOf(el) !== -1;
11405     },
11406
11407     /**
11408      * Returns true if this composite contains the passed element
11409      * @return Boolean
11410      */
11411     indexOf : function(el){
11412         return this.elements.indexOf(Roo.get(el));
11413     },
11414
11415
11416     /**
11417     * Removes the specified element(s).
11418     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
11419     * or an array of any of those.
11420     * @param {Boolean} removeDom (optional) True to also remove the element from the document
11421     * @return {CompositeElement} this
11422     */
11423     removeElement : function(el, removeDom){
11424         if(el instanceof Array){
11425             for(var i = 0, len = el.length; i < len; i++){
11426                 this.removeElement(el[i]);
11427             }
11428             return this;
11429         }
11430         var index = typeof el == 'number' ? el : this.indexOf(el);
11431         if(index !== -1){
11432             if(removeDom){
11433                 var d = this.elements[index];
11434                 if(d.dom){
11435                     d.remove();
11436                 }else{
11437                     d.parentNode.removeChild(d);
11438                 }
11439             }
11440             this.elements.splice(index, 1);
11441         }
11442         return this;
11443     },
11444
11445     /**
11446     * Replaces the specified element with the passed element.
11447     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
11448     * to replace.
11449     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
11450     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
11451     * @return {CompositeElement} this
11452     */
11453     replaceElement : function(el, replacement, domReplace){
11454         var index = typeof el == 'number' ? el : this.indexOf(el);
11455         if(index !== -1){
11456             if(domReplace){
11457                 this.elements[index].replaceWith(replacement);
11458             }else{
11459                 this.elements.splice(index, 1, Roo.get(replacement))
11460             }
11461         }
11462         return this;
11463     },
11464
11465     /**
11466      * Removes all elements.
11467      */
11468     clear : function(){
11469         this.elements = [];
11470     }
11471 };
11472 (function(){
11473     Roo.CompositeElement.createCall = function(proto, fnName){
11474         if(!proto[fnName]){
11475             proto[fnName] = function(){
11476                 return this.invoke(fnName, arguments);
11477             };
11478         }
11479     };
11480     for(var fnName in Roo.Element.prototype){
11481         if(typeof Roo.Element.prototype[fnName] == "function"){
11482             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
11483         }
11484     };
11485 })();
11486 /*
11487  * Based on:
11488  * Ext JS Library 1.1.1
11489  * Copyright(c) 2006-2007, Ext JS, LLC.
11490  *
11491  * Originally Released Under LGPL - original licence link has changed is not relivant.
11492  *
11493  * Fork - LGPL
11494  * <script type="text/javascript">
11495  */
11496
11497 /**
11498  * @class Roo.CompositeElementLite
11499  * @extends Roo.CompositeElement
11500  * Flyweight composite class. Reuses the same Roo.Element for element operations.
11501  <pre><code>
11502  var els = Roo.select("#some-el div.some-class");
11503  // or select directly from an existing element
11504  var el = Roo.get('some-el');
11505  el.select('div.some-class');
11506
11507  els.setWidth(100); // all elements become 100 width
11508  els.hide(true); // all elements fade out and hide
11509  // or
11510  els.setWidth(100).hide(true);
11511  </code></pre><br><br>
11512  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11513  * actions will be performed on all the elements in this collection.</b>
11514  */
11515 Roo.CompositeElementLite = function(els){
11516     Roo.CompositeElementLite.superclass.constructor.call(this, els);
11517     this.el = new Roo.Element.Flyweight();
11518 };
11519 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
11520     addElements : function(els){
11521         if(els){
11522             if(els instanceof Array){
11523                 this.elements = this.elements.concat(els);
11524             }else{
11525                 var yels = this.elements;
11526                 var index = yels.length-1;
11527                 for(var i = 0, len = els.length; i < len; i++) {
11528                     yels[++index] = els[i];
11529                 }
11530             }
11531         }
11532         return this;
11533     },
11534     invoke : function(fn, args){
11535         var els = this.elements;
11536         var el = this.el;
11537         for(var i = 0, len = els.length; i < len; i++) {
11538             el.dom = els[i];
11539                 Roo.Element.prototype[fn].apply(el, args);
11540         }
11541         return this;
11542     },
11543     /**
11544      * Returns a flyweight Element of the dom element object at the specified index
11545      * @param {Number} index
11546      * @return {Roo.Element}
11547      */
11548     item : function(index){
11549         if(!this.elements[index]){
11550             return null;
11551         }
11552         this.el.dom = this.elements[index];
11553         return this.el;
11554     },
11555
11556     // fixes scope with flyweight
11557     addListener : function(eventName, handler, scope, opt){
11558         var els = this.elements;
11559         for(var i = 0, len = els.length; i < len; i++) {
11560             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
11561         }
11562         return this;
11563     },
11564
11565     /**
11566     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
11567     * passed is the flyweight (shared) Roo.Element instance, so if you require a
11568     * a reference to the dom node, use el.dom.</b>
11569     * @param {Function} fn The function to call
11570     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
11571     * @return {CompositeElement} this
11572     */
11573     each : function(fn, scope){
11574         var els = this.elements;
11575         var el = this.el;
11576         for(var i = 0, len = els.length; i < len; i++){
11577             el.dom = els[i];
11578                 if(fn.call(scope || el, el, this, i) === false){
11579                 break;
11580             }
11581         }
11582         return this;
11583     },
11584
11585     indexOf : function(el){
11586         return this.elements.indexOf(Roo.getDom(el));
11587     },
11588
11589     replaceElement : function(el, replacement, domReplace){
11590         var index = typeof el == 'number' ? el : this.indexOf(el);
11591         if(index !== -1){
11592             replacement = Roo.getDom(replacement);
11593             if(domReplace){
11594                 var d = this.elements[index];
11595                 d.parentNode.insertBefore(replacement, d);
11596                 d.parentNode.removeChild(d);
11597             }
11598             this.elements.splice(index, 1, replacement);
11599         }
11600         return this;
11601     }
11602 });
11603 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
11604
11605 /*
11606  * Based on:
11607  * Ext JS Library 1.1.1
11608  * Copyright(c) 2006-2007, Ext JS, LLC.
11609  *
11610  * Originally Released Under LGPL - original licence link has changed is not relivant.
11611  *
11612  * Fork - LGPL
11613  * <script type="text/javascript">
11614  */
11615
11616  
11617
11618 /**
11619  * @class Roo.data.Connection
11620  * @extends Roo.util.Observable
11621  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
11622  * either to a configured URL, or to a URL specified at request time. 
11623  * 
11624  * Requests made by this class are asynchronous, and will return immediately. No data from
11625  * the server will be available to the statement immediately following the {@link #request} call.
11626  * To process returned data, use a callback in the request options object, or an event listener.
11627  * 
11628  * Note: If you are doing a file upload, you will not get a normal response object sent back to
11629  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
11630  * The response object is created using the innerHTML of the IFRAME's document as the responseText
11631  * property and, if present, the IFRAME's XML document as the responseXML property.
11632  * 
11633  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
11634  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
11635  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
11636  * standard DOM methods.
11637  * @constructor
11638  * @param {Object} config a configuration object.
11639  */
11640 Roo.data.Connection = function(config){
11641     Roo.apply(this, config);
11642     this.addEvents({
11643         /**
11644          * @event beforerequest
11645          * Fires before a network request is made to retrieve a data object.
11646          * @param {Connection} conn This Connection object.
11647          * @param {Object} options The options config object passed to the {@link #request} method.
11648          */
11649         "beforerequest" : true,
11650         /**
11651          * @event requestcomplete
11652          * Fires if the request was successfully completed.
11653          * @param {Connection} conn This Connection object.
11654          * @param {Object} response The XHR object containing the response data.
11655          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11656          * @param {Object} options The options config object passed to the {@link #request} method.
11657          */
11658         "requestcomplete" : true,
11659         /**
11660          * @event requestexception
11661          * Fires if an error HTTP status was returned from the server.
11662          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
11663          * @param {Connection} conn This Connection object.
11664          * @param {Object} response The XHR object containing the response data.
11665          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
11666          * @param {Object} options The options config object passed to the {@link #request} method.
11667          */
11668         "requestexception" : true
11669     });
11670     Roo.data.Connection.superclass.constructor.call(this);
11671 };
11672
11673 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
11674     /**
11675      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
11676      */
11677     /**
11678      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
11679      * extra parameters to each request made by this object. (defaults to undefined)
11680      */
11681     /**
11682      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
11683      *  to each request made by this object. (defaults to undefined)
11684      */
11685     /**
11686      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
11687      */
11688     /**
11689      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
11690      */
11691     timeout : 30000,
11692     /**
11693      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
11694      * @type Boolean
11695      */
11696     autoAbort:false,
11697
11698     /**
11699      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
11700      * @type Boolean
11701      */
11702     disableCaching: true,
11703
11704     /**
11705      * Sends an HTTP request to a remote server.
11706      * @param {Object} options An object which may contain the following properties:<ul>
11707      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
11708      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
11709      * request, a url encoded string or a function to call to get either.</li>
11710      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
11711      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
11712      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
11713      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
11714      * <li>options {Object} The parameter to the request call.</li>
11715      * <li>success {Boolean} True if the request succeeded.</li>
11716      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11717      * </ul></li>
11718      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
11719      * The callback is passed the following parameters:<ul>
11720      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11721      * <li>options {Object} The parameter to the request call.</li>
11722      * </ul></li>
11723      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
11724      * The callback is passed the following parameters:<ul>
11725      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
11726      * <li>options {Object} The parameter to the request call.</li>
11727      * </ul></li>
11728      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
11729      * for the callback function. Defaults to the browser window.</li>
11730      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
11731      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
11732      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
11733      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
11734      * params for the post data. Any params will be appended to the URL.</li>
11735      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
11736      * </ul>
11737      * @return {Number} transactionId
11738      */
11739     request : function(o){
11740         if(this.fireEvent("beforerequest", this, o) !== false){
11741             var p = o.params;
11742
11743             if(typeof p == "function"){
11744                 p = p.call(o.scope||window, o);
11745             }
11746             if(typeof p == "object"){
11747                 p = Roo.urlEncode(o.params);
11748             }
11749             if(this.extraParams){
11750                 var extras = Roo.urlEncode(this.extraParams);
11751                 p = p ? (p + '&' + extras) : extras;
11752             }
11753
11754             var url = o.url || this.url;
11755             if(typeof url == 'function'){
11756                 url = url.call(o.scope||window, o);
11757             }
11758
11759             if(o.form){
11760                 var form = Roo.getDom(o.form);
11761                 url = url || form.action;
11762
11763                 var enctype = form.getAttribute("enctype");
11764                 
11765                 if (o.formData) {
11766                     return this.doFormDataUpload(o, url);
11767                 }
11768                 
11769                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
11770                     return this.doFormUpload(o, p, url);
11771                 }
11772                 var f = Roo.lib.Ajax.serializeForm(form);
11773                 p = p ? (p + '&' + f) : f;
11774             }
11775             
11776             if (!o.form && o.formData) {
11777                 o.formData = o.formData === true ? new FormData() : o.formData;
11778                 for (var k in o.params) {
11779                     o.formData.append(k,o.params[k]);
11780                 }
11781                     
11782                 return this.doFormDataUpload(o, url);
11783             }
11784             
11785
11786             var hs = o.headers;
11787             if(this.defaultHeaders){
11788                 hs = Roo.apply(hs || {}, this.defaultHeaders);
11789                 if(!o.headers){
11790                     o.headers = hs;
11791                 }
11792             }
11793
11794             var cb = {
11795                 success: this.handleResponse,
11796                 failure: this.handleFailure,
11797                 scope: this,
11798                 argument: {options: o},
11799                 timeout : o.timeout || this.timeout
11800             };
11801
11802             var method = o.method||this.method||(p ? "POST" : "GET");
11803
11804             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
11805                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
11806             }
11807
11808             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11809                 if(o.autoAbort){
11810                     this.abort();
11811                 }
11812             }else if(this.autoAbort !== false){
11813                 this.abort();
11814             }
11815
11816             if((method == 'GET' && p) || o.xmlData){
11817                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
11818                 p = '';
11819             }
11820             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
11821             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
11822             Roo.lib.Ajax.useDefaultHeader == true;
11823             return this.transId;
11824         }else{
11825             Roo.callback(o.callback, o.scope, [o, null, null]);
11826             return null;
11827         }
11828     },
11829
11830     /**
11831      * Determine whether this object has a request outstanding.
11832      * @param {Number} transactionId (Optional) defaults to the last transaction
11833      * @return {Boolean} True if there is an outstanding request.
11834      */
11835     isLoading : function(transId){
11836         if(transId){
11837             return Roo.lib.Ajax.isCallInProgress(transId);
11838         }else{
11839             return this.transId ? true : false;
11840         }
11841     },
11842
11843     /**
11844      * Aborts any outstanding request.
11845      * @param {Number} transactionId (Optional) defaults to the last transaction
11846      */
11847     abort : function(transId){
11848         if(transId || this.isLoading()){
11849             Roo.lib.Ajax.abort(transId || this.transId);
11850         }
11851     },
11852
11853     // private
11854     handleResponse : function(response){
11855         this.transId = false;
11856         var options = response.argument.options;
11857         response.argument = options ? options.argument : null;
11858         this.fireEvent("requestcomplete", this, response, options);
11859         Roo.callback(options.success, options.scope, [response, options]);
11860         Roo.callback(options.callback, options.scope, [options, true, response]);
11861     },
11862
11863     // private
11864     handleFailure : function(response, e){
11865         this.transId = false;
11866         var options = response.argument.options;
11867         response.argument = options ? options.argument : null;
11868         this.fireEvent("requestexception", this, response, options, e);
11869         Roo.callback(options.failure, options.scope, [response, options]);
11870         Roo.callback(options.callback, options.scope, [options, false, response]);
11871     },
11872
11873     // private
11874     doFormUpload : function(o, ps, url){
11875         var id = Roo.id();
11876         var frame = document.createElement('iframe');
11877         frame.id = id;
11878         frame.name = id;
11879         frame.className = 'x-hidden';
11880         if(Roo.isIE){
11881             frame.src = Roo.SSL_SECURE_URL;
11882         }
11883         document.body.appendChild(frame);
11884
11885         if(Roo.isIE){
11886            document.frames[id].name = id;
11887         }
11888
11889         var form = Roo.getDom(o.form);
11890         form.target = id;
11891         form.method = 'POST';
11892         form.enctype = form.encoding = 'multipart/form-data';
11893         if(url){
11894             form.action = url;
11895         }
11896
11897         var hiddens, hd;
11898         if(ps){ // add dynamic params
11899             hiddens = [];
11900             ps = Roo.urlDecode(ps, false);
11901             for(var k in ps){
11902                 if(ps.hasOwnProperty(k)){
11903                     hd = document.createElement('input');
11904                     hd.type = 'hidden';
11905                     hd.name = k;
11906                     hd.value = ps[k];
11907                     form.appendChild(hd);
11908                     hiddens.push(hd);
11909                 }
11910             }
11911         }
11912
11913         function cb(){
11914             var r = {  // bogus response object
11915                 responseText : '',
11916                 responseXML : null
11917             };
11918
11919             r.argument = o ? o.argument : null;
11920
11921             try { //
11922                 var doc;
11923                 if(Roo.isIE){
11924                     doc = frame.contentWindow.document;
11925                 }else {
11926                     doc = (frame.contentDocument || window.frames[id].document);
11927                 }
11928                 if(doc && doc.body){
11929                     r.responseText = doc.body.innerHTML;
11930                 }
11931                 if(doc && doc.XMLDocument){
11932                     r.responseXML = doc.XMLDocument;
11933                 }else {
11934                     r.responseXML = doc;
11935                 }
11936             }
11937             catch(e) {
11938                 // ignore
11939             }
11940
11941             Roo.EventManager.removeListener(frame, 'load', cb, this);
11942
11943             this.fireEvent("requestcomplete", this, r, o);
11944             Roo.callback(o.success, o.scope, [r, o]);
11945             Roo.callback(o.callback, o.scope, [o, true, r]);
11946
11947             setTimeout(function(){document.body.removeChild(frame);}, 100);
11948         }
11949
11950         Roo.EventManager.on(frame, 'load', cb, this);
11951         form.submit();
11952
11953         if(hiddens){ // remove dynamic params
11954             for(var i = 0, len = hiddens.length; i < len; i++){
11955                 form.removeChild(hiddens[i]);
11956             }
11957         }
11958     },
11959     // this is a 'formdata version???'
11960     
11961     
11962     doFormDataUpload : function(o,  url)
11963     {
11964         var formData;
11965         if (o.form) {
11966             var form =  Roo.getDom(o.form);
11967             form.enctype = form.encoding = 'multipart/form-data';
11968             formData = o.formData === true ? new FormData(form) : o.formData;
11969         } else {
11970             formData = o.formData === true ? new FormData() : o.formData;
11971         }
11972         
11973       
11974         var cb = {
11975             success: this.handleResponse,
11976             failure: this.handleFailure,
11977             scope: this,
11978             argument: {options: o},
11979             timeout : o.timeout || this.timeout
11980         };
11981  
11982         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
11983             if(o.autoAbort){
11984                 this.abort();
11985             }
11986         }else if(this.autoAbort !== false){
11987             this.abort();
11988         }
11989
11990         //Roo.lib.Ajax.defaultPostHeader = null;
11991         Roo.lib.Ajax.useDefaultHeader = false;
11992         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
11993         Roo.lib.Ajax.useDefaultHeader = true;
11994  
11995          
11996     }
11997     
11998 });
11999 /*
12000  * Based on:
12001  * Ext JS Library 1.1.1
12002  * Copyright(c) 2006-2007, Ext JS, LLC.
12003  *
12004  * Originally Released Under LGPL - original licence link has changed is not relivant.
12005  *
12006  * Fork - LGPL
12007  * <script type="text/javascript">
12008  */
12009  
12010 /**
12011  * Global Ajax request class.
12012  * 
12013  * @class Roo.Ajax
12014  * @extends Roo.data.Connection
12015  * @static
12016  * 
12017  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12018  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12019  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12020  * @cfg {String} method (Optional)  The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
12021  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12022  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12023  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12024  */
12025 Roo.Ajax = new Roo.data.Connection({
12026     // fix up the docs
12027     /**
12028      * @scope Roo.Ajax
12029      * @type {Boolear} 
12030      */
12031     autoAbort : false,
12032
12033     /**
12034      * Serialize the passed form into a url encoded string
12035      * @scope Roo.Ajax
12036      * @param {String/HTMLElement} form
12037      * @return {String}
12038      */
12039     serializeForm : function(form){
12040         return Roo.lib.Ajax.serializeForm(form);
12041     }
12042 });/*
12043  * Based on:
12044  * Ext JS Library 1.1.1
12045  * Copyright(c) 2006-2007, Ext JS, LLC.
12046  *
12047  * Originally Released Under LGPL - original licence link has changed is not relivant.
12048  *
12049  * Fork - LGPL
12050  * <script type="text/javascript">
12051  */
12052
12053  
12054 /**
12055  * @class Roo.UpdateManager
12056  * @extends Roo.util.Observable
12057  * Provides AJAX-style update for Element object.<br><br>
12058  * Usage:<br>
12059  * <pre><code>
12060  * // Get it from a Roo.Element object
12061  * var el = Roo.get("foo");
12062  * var mgr = el.getUpdateManager();
12063  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12064  * ...
12065  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12066  * <br>
12067  * // or directly (returns the same UpdateManager instance)
12068  * var mgr = new Roo.UpdateManager("myElementId");
12069  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12070  * mgr.on("update", myFcnNeedsToKnow);
12071  * <br>
12072    // short handed call directly from the element object
12073    Roo.get("foo").load({
12074         url: "bar.php",
12075         scripts:true,
12076         params: "for=bar",
12077         text: "Loading Foo..."
12078    });
12079  * </code></pre>
12080  * @constructor
12081  * Create new UpdateManager directly.
12082  * @param {String/HTMLElement/Roo.Element} el The element to update
12083  * @param {Boolean} forceNew (optional) By default the constructor checks to see if the passed element already has an UpdateManager and if it does it returns the same instance. This will skip that check (useful for extending this class).
12084  */
12085 Roo.UpdateManager = function(el, forceNew){
12086     el = Roo.get(el);
12087     if(!forceNew && el.updateManager){
12088         return el.updateManager;
12089     }
12090     /**
12091      * The Element object
12092      * @type Roo.Element
12093      */
12094     this.el = el;
12095     /**
12096      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12097      * @type String
12098      */
12099     this.defaultUrl = null;
12100
12101     this.addEvents({
12102         /**
12103          * @event beforeupdate
12104          * Fired before an update is made, return false from your handler and the update is cancelled.
12105          * @param {Roo.Element} el
12106          * @param {String/Object/Function} url
12107          * @param {String/Object} params
12108          */
12109         "beforeupdate": true,
12110         /**
12111          * @event update
12112          * Fired after successful update is made.
12113          * @param {Roo.Element} el
12114          * @param {Object} oResponseObject The response Object
12115          */
12116         "update": true,
12117         /**
12118          * @event failure
12119          * Fired on update failure.
12120          * @param {Roo.Element} el
12121          * @param {Object} oResponseObject The response Object
12122          */
12123         "failure": true
12124     });
12125     var d = Roo.UpdateManager.defaults;
12126     /**
12127      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12128      * @type String
12129      */
12130     this.sslBlankUrl = d.sslBlankUrl;
12131     /**
12132      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12133      * @type Boolean
12134      */
12135     this.disableCaching = d.disableCaching;
12136     /**
12137      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12138      * @type String
12139      */
12140     this.indicatorText = d.indicatorText;
12141     /**
12142      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12143      * @type String
12144      */
12145     this.showLoadIndicator = d.showLoadIndicator;
12146     /**
12147      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12148      * @type Number
12149      */
12150     this.timeout = d.timeout;
12151
12152     /**
12153      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12154      * @type Boolean
12155      */
12156     this.loadScripts = d.loadScripts;
12157
12158     /**
12159      * Transaction object of current executing transaction
12160      */
12161     this.transaction = null;
12162
12163     /**
12164      * @private
12165      */
12166     this.autoRefreshProcId = null;
12167     /**
12168      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12169      * @type Function
12170      */
12171     this.refreshDelegate = this.refresh.createDelegate(this);
12172     /**
12173      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12174      * @type Function
12175      */
12176     this.updateDelegate = this.update.createDelegate(this);
12177     /**
12178      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12179      * @type Function
12180      */
12181     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12182     /**
12183      * @private
12184      */
12185     this.successDelegate = this.processSuccess.createDelegate(this);
12186     /**
12187      * @private
12188      */
12189     this.failureDelegate = this.processFailure.createDelegate(this);
12190
12191     if(!this.renderer){
12192      /**
12193       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12194       */
12195     this.renderer = new Roo.UpdateManager.BasicRenderer();
12196     }
12197     
12198     Roo.UpdateManager.superclass.constructor.call(this);
12199 };
12200
12201 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12202     /**
12203      * Get the Element this UpdateManager is bound to
12204      * @return {Roo.Element} The element
12205      */
12206     getEl : function(){
12207         return this.el;
12208     },
12209     /**
12210      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12211      * @param {Object/String/Function} url The url for this request or a function to call to get the url or a config object containing any of the following options:
12212 <pre><code>
12213 um.update({<br/>
12214     url: "your-url.php",<br/>
12215     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12216     callback: yourFunction,<br/>
12217     scope: yourObject, //(optional scope)  <br/>
12218     discardUrl: false, <br/>
12219     nocache: false,<br/>
12220     text: "Loading...",<br/>
12221     timeout: 30,<br/>
12222     scripts: false<br/>
12223 });
12224 </code></pre>
12225      * The only required property is url. The optional properties nocache, text and scripts
12226      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12227      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "param1=1&amp;param2=2" or an object {param1: 1, param2: 2}
12228      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12229      * @param {Boolean} discardUrl (optional) By default when you execute an update the defaultUrl is changed to the last used url. If true, it will not store the url.
12230      */
12231     update : function(url, params, callback, discardUrl){
12232         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12233             var method = this.method,
12234                 cfg;
12235             if(typeof url == "object"){ // must be config object
12236                 cfg = url;
12237                 url = cfg.url;
12238                 params = params || cfg.params;
12239                 callback = callback || cfg.callback;
12240                 discardUrl = discardUrl || cfg.discardUrl;
12241                 if(callback && cfg.scope){
12242                     callback = callback.createDelegate(cfg.scope);
12243                 }
12244                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12245                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12246                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12247                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12248                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12249             }
12250             this.showLoading();
12251             if(!discardUrl){
12252                 this.defaultUrl = url;
12253             }
12254             if(typeof url == "function"){
12255                 url = url.call(this);
12256             }
12257
12258             method = method || (params ? "POST" : "GET");
12259             if(method == "GET"){
12260                 url = this.prepareUrl(url);
12261             }
12262
12263             var o = Roo.apply(cfg ||{}, {
12264                 url : url,
12265                 params: params,
12266                 success: this.successDelegate,
12267                 failure: this.failureDelegate,
12268                 callback: undefined,
12269                 timeout: (this.timeout*1000),
12270                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12271             });
12272             Roo.log("updated manager called with timeout of " + o.timeout);
12273             this.transaction = Roo.Ajax.request(o);
12274         }
12275     },
12276
12277     /**
12278      * Performs an async form post, updating this element with the response. If the form has the attribute enctype="multipart/form-data", it assumes it's a file upload.
12279      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12280      * @param {String/HTMLElement} form The form Id or form element
12281      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12282      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12283      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12284      */
12285     formUpdate : function(form, url, reset, callback){
12286         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12287             if(typeof url == "function"){
12288                 url = url.call(this);
12289             }
12290             form = Roo.getDom(form);
12291             this.transaction = Roo.Ajax.request({
12292                 form: form,
12293                 url:url,
12294                 success: this.successDelegate,
12295                 failure: this.failureDelegate,
12296                 timeout: (this.timeout*1000),
12297                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
12298             });
12299             this.showLoading.defer(1, this);
12300         }
12301     },
12302
12303     /**
12304      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
12305      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12306      */
12307     refresh : function(callback){
12308         if(this.defaultUrl == null){
12309             return;
12310         }
12311         this.update(this.defaultUrl, null, callback, true);
12312     },
12313
12314     /**
12315      * Set this element to auto refresh.
12316      * @param {Number} interval How often to update (in seconds).
12317      * @param {String/Function} url (optional) The url for this request or a function to call to get the url (Defaults to the last used url)
12318      * @param {String/Object} params (optional) The parameters to pass as either a url encoded string "&param1=1&param2=2" or as an object {param1: 1, param2: 2}
12319      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
12320      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
12321      */
12322     startAutoRefresh : function(interval, url, params, callback, refreshNow){
12323         if(refreshNow){
12324             this.update(url || this.defaultUrl, params, callback, true);
12325         }
12326         if(this.autoRefreshProcId){
12327             clearInterval(this.autoRefreshProcId);
12328         }
12329         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
12330     },
12331
12332     /**
12333      * Stop auto refresh on this element.
12334      */
12335      stopAutoRefresh : function(){
12336         if(this.autoRefreshProcId){
12337             clearInterval(this.autoRefreshProcId);
12338             delete this.autoRefreshProcId;
12339         }
12340     },
12341
12342     isAutoRefreshing : function(){
12343        return this.autoRefreshProcId ? true : false;
12344     },
12345     /**
12346      * Called to update the element to "Loading" state. Override to perform custom action.
12347      */
12348     showLoading : function(){
12349         if(this.showLoadIndicator){
12350             this.el.update(this.indicatorText);
12351         }
12352     },
12353
12354     /**
12355      * Adds unique parameter to query string if disableCaching = true
12356      * @private
12357      */
12358     prepareUrl : function(url){
12359         if(this.disableCaching){
12360             var append = "_dc=" + (new Date().getTime());
12361             if(url.indexOf("?") !== -1){
12362                 url += "&" + append;
12363             }else{
12364                 url += "?" + append;
12365             }
12366         }
12367         return url;
12368     },
12369
12370     /**
12371      * @private
12372      */
12373     processSuccess : function(response){
12374         this.transaction = null;
12375         if(response.argument.form && response.argument.reset){
12376             try{ // put in try/catch since some older FF releases had problems with this
12377                 response.argument.form.reset();
12378             }catch(e){}
12379         }
12380         if(this.loadScripts){
12381             this.renderer.render(this.el, response, this,
12382                 this.updateComplete.createDelegate(this, [response]));
12383         }else{
12384             this.renderer.render(this.el, response, this);
12385             this.updateComplete(response);
12386         }
12387     },
12388
12389     updateComplete : function(response){
12390         this.fireEvent("update", this.el, response);
12391         if(typeof response.argument.callback == "function"){
12392             response.argument.callback(this.el, true, response);
12393         }
12394     },
12395
12396     /**
12397      * @private
12398      */
12399     processFailure : function(response){
12400         this.transaction = null;
12401         this.fireEvent("failure", this.el, response);
12402         if(typeof response.argument.callback == "function"){
12403             response.argument.callback(this.el, false, response);
12404         }
12405     },
12406
12407     /**
12408      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
12409      * @param {Object} renderer The object implementing the render() method
12410      */
12411     setRenderer : function(renderer){
12412         this.renderer = renderer;
12413     },
12414
12415     getRenderer : function(){
12416        return this.renderer;
12417     },
12418
12419     /**
12420      * Set the defaultUrl used for updates
12421      * @param {String/Function} defaultUrl The url or a function to call to get the url
12422      */
12423     setDefaultUrl : function(defaultUrl){
12424         this.defaultUrl = defaultUrl;
12425     },
12426
12427     /**
12428      * Aborts the executing transaction
12429      */
12430     abort : function(){
12431         if(this.transaction){
12432             Roo.Ajax.abort(this.transaction);
12433         }
12434     },
12435
12436     /**
12437      * Returns true if an update is in progress
12438      * @return {Boolean}
12439      */
12440     isUpdating : function(){
12441         if(this.transaction){
12442             return Roo.Ajax.isLoading(this.transaction);
12443         }
12444         return false;
12445     }
12446 });
12447
12448 /**
12449  * @class Roo.UpdateManager.defaults
12450  * @static (not really - but it helps the doc tool)
12451  * The defaults collection enables customizing the default properties of UpdateManager
12452  */
12453    Roo.UpdateManager.defaults = {
12454        /**
12455          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
12456          * @type Number
12457          */
12458          timeout : 30,
12459
12460          /**
12461          * True to process scripts by default (Defaults to false).
12462          * @type Boolean
12463          */
12464         loadScripts : false,
12465
12466         /**
12467         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
12468         * @type String
12469         */
12470         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
12471         /**
12472          * Whether to append unique parameter on get request to disable caching (Defaults to false).
12473          * @type Boolean
12474          */
12475         disableCaching : false,
12476         /**
12477          * Whether to show indicatorText when loading (Defaults to true).
12478          * @type Boolean
12479          */
12480         showLoadIndicator : true,
12481         /**
12482          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12483          * @type String
12484          */
12485         indicatorText : '<div class="loading-indicator">Loading...</div>'
12486    };
12487
12488 /**
12489  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
12490  *Usage:
12491  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
12492  * @param {String/HTMLElement/Roo.Element} el The element to update
12493  * @param {String} url The url
12494  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
12495  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
12496  * @static
12497  * @deprecated
12498  * @member Roo.UpdateManager
12499  */
12500 Roo.UpdateManager.updateElement = function(el, url, params, options){
12501     var um = Roo.get(el, true).getUpdateManager();
12502     Roo.apply(um, options);
12503     um.update(url, params, options ? options.callback : null);
12504 };
12505 // alias for backwards compat
12506 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
12507 /**
12508  * @class Roo.UpdateManager.BasicRenderer
12509  * Default Content renderer. Updates the elements innerHTML with the responseText.
12510  */
12511 Roo.UpdateManager.BasicRenderer = function(){};
12512
12513 Roo.UpdateManager.BasicRenderer.prototype = {
12514     /**
12515      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
12516      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
12517      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
12518      * @param {Roo.Element} el The element being rendered
12519      * @param {Object} response The YUI Connect response object
12520      * @param {UpdateManager} updateManager The calling update manager
12521      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
12522      */
12523      render : function(el, response, updateManager, callback){
12524         el.update(response.responseText, updateManager.loadScripts, callback);
12525     }
12526 };
12527 /*
12528  * Based on:
12529  * Roo JS
12530  * (c)) Alan Knowles
12531  * Licence : LGPL
12532  */
12533
12534
12535 /**
12536  * @class Roo.DomTemplate
12537  * @extends Roo.Template
12538  * An effort at a dom based template engine..
12539  *
12540  * Similar to XTemplate, except it uses dom parsing to create the template..
12541  *
12542  * Supported features:
12543  *
12544  *  Tags:
12545
12546 <pre><code>
12547       {a_variable} - output encoded.
12548       {a_variable.format:("Y-m-d")} - call a method on the variable
12549       {a_variable:raw} - unencoded output
12550       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
12551       {a_variable:this.method_on_template(...)} - call a method on the template object.
12552  
12553 </code></pre>
12554  *  The tpl tag:
12555 <pre><code>
12556         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
12557         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
12558         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
12559         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
12560   
12561 </code></pre>
12562  *      
12563  */
12564 Roo.DomTemplate = function()
12565 {
12566      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
12567      if (this.html) {
12568         this.compile();
12569      }
12570 };
12571
12572
12573 Roo.extend(Roo.DomTemplate, Roo.Template, {
12574     /**
12575      * id counter for sub templates.
12576      */
12577     id : 0,
12578     /**
12579      * flag to indicate if dom parser is inside a pre,
12580      * it will strip whitespace if not.
12581      */
12582     inPre : false,
12583     
12584     /**
12585      * The various sub templates
12586      */
12587     tpls : false,
12588     
12589     
12590     
12591     /**
12592      *
12593      * basic tag replacing syntax
12594      * WORD:WORD()
12595      *
12596      * // you can fake an object call by doing this
12597      *  x.t:(test,tesT) 
12598      * 
12599      */
12600     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
12601     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
12602     
12603     iterChild : function (node, method) {
12604         
12605         var oldPre = this.inPre;
12606         if (node.tagName == 'PRE') {
12607             this.inPre = true;
12608         }
12609         for( var i = 0; i < node.childNodes.length; i++) {
12610             method.call(this, node.childNodes[i]);
12611         }
12612         this.inPre = oldPre;
12613     },
12614     
12615     
12616     
12617     /**
12618      * compile the template
12619      *
12620      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
12621      *
12622      */
12623     compile: function()
12624     {
12625         var s = this.html;
12626         
12627         // covert the html into DOM...
12628         var doc = false;
12629         var div =false;
12630         try {
12631             doc = document.implementation.createHTMLDocument("");
12632             doc.documentElement.innerHTML =   this.html  ;
12633             div = doc.documentElement;
12634         } catch (e) {
12635             // old IE... - nasty -- it causes all sorts of issues.. with
12636             // images getting pulled from server..
12637             div = document.createElement('div');
12638             div.innerHTML = this.html;
12639         }
12640         //doc.documentElement.innerHTML = htmlBody
12641          
12642         
12643         
12644         this.tpls = [];
12645         var _t = this;
12646         this.iterChild(div, function(n) {_t.compileNode(n, true); });
12647         
12648         var tpls = this.tpls;
12649         
12650         // create a top level template from the snippet..
12651         
12652         //Roo.log(div.innerHTML);
12653         
12654         var tpl = {
12655             uid : 'master',
12656             id : this.id++,
12657             attr : false,
12658             value : false,
12659             body : div.innerHTML,
12660             
12661             forCall : false,
12662             execCall : false,
12663             dom : div,
12664             isTop : true
12665             
12666         };
12667         tpls.unshift(tpl);
12668         
12669         
12670         // compile them...
12671         this.tpls = [];
12672         Roo.each(tpls, function(tp){
12673             this.compileTpl(tp);
12674             this.tpls[tp.id] = tp;
12675         }, this);
12676         
12677         this.master = tpls[0];
12678         return this;
12679         
12680         
12681     },
12682     
12683     compileNode : function(node, istop) {
12684         // test for
12685         //Roo.log(node);
12686         
12687         
12688         // skip anything not a tag..
12689         if (node.nodeType != 1) {
12690             if (node.nodeType == 3 && !this.inPre) {
12691                 // reduce white space..
12692                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
12693                 
12694             }
12695             return;
12696         }
12697         
12698         var tpl = {
12699             uid : false,
12700             id : false,
12701             attr : false,
12702             value : false,
12703             body : '',
12704             
12705             forCall : false,
12706             execCall : false,
12707             dom : false,
12708             isTop : istop
12709             
12710             
12711         };
12712         
12713         
12714         switch(true) {
12715             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
12716             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
12717             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
12718             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
12719             // no default..
12720         }
12721         
12722         
12723         if (!tpl.attr) {
12724             // just itterate children..
12725             this.iterChild(node,this.compileNode);
12726             return;
12727         }
12728         tpl.uid = this.id++;
12729         tpl.value = node.getAttribute('roo-' +  tpl.attr);
12730         node.removeAttribute('roo-'+ tpl.attr);
12731         if (tpl.attr != 'name') {
12732             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
12733             node.parentNode.replaceChild(placeholder,  node);
12734         } else {
12735             
12736             var placeholder =  document.createElement('span');
12737             placeholder.className = 'roo-tpl-' + tpl.value;
12738             node.parentNode.replaceChild(placeholder,  node);
12739         }
12740         
12741         // parent now sees '{domtplXXXX}
12742         this.iterChild(node,this.compileNode);
12743         
12744         // we should now have node body...
12745         var div = document.createElement('div');
12746         div.appendChild(node);
12747         tpl.dom = node;
12748         // this has the unfortunate side effect of converting tagged attributes
12749         // eg. href="{...}" into %7C...%7D
12750         // this has been fixed by searching for those combo's although it's a bit hacky..
12751         
12752         
12753         tpl.body = div.innerHTML;
12754         
12755         
12756          
12757         tpl.id = tpl.uid;
12758         switch(tpl.attr) {
12759             case 'for' :
12760                 switch (tpl.value) {
12761                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
12762                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
12763                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
12764                 }
12765                 break;
12766             
12767             case 'exec':
12768                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12769                 break;
12770             
12771             case 'if':     
12772                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
12773                 break;
12774             
12775             case 'name':
12776                 tpl.id  = tpl.value; // replace non characters???
12777                 break;
12778             
12779         }
12780         
12781         
12782         this.tpls.push(tpl);
12783         
12784         
12785         
12786     },
12787     
12788     
12789     
12790     
12791     /**
12792      * Compile a segment of the template into a 'sub-template'
12793      *
12794      * 
12795      * 
12796      *
12797      */
12798     compileTpl : function(tpl)
12799     {
12800         var fm = Roo.util.Format;
12801         var useF = this.disableFormats !== true;
12802         
12803         var sep = Roo.isGecko ? "+\n" : ",\n";
12804         
12805         var undef = function(str) {
12806             Roo.debug && Roo.log("Property not found :"  + str);
12807             return '';
12808         };
12809           
12810         //Roo.log(tpl.body);
12811         
12812         
12813         
12814         var fn = function(m, lbrace, name, format, args)
12815         {
12816             //Roo.log("ARGS");
12817             //Roo.log(arguments);
12818             args = args ? args.replace(/\\'/g,"'") : args;
12819             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
12820             if (typeof(format) == 'undefined') {
12821                 format =  'htmlEncode'; 
12822             }
12823             if (format == 'raw' ) {
12824                 format = false;
12825             }
12826             
12827             if(name.substr(0, 6) == 'domtpl'){
12828                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
12829             }
12830             
12831             // build an array of options to determine if value is undefined..
12832             
12833             // basically get 'xxxx.yyyy' then do
12834             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
12835             //    (function () { Roo.log("Property not found"); return ''; })() :
12836             //    ......
12837             
12838             var udef_ar = [];
12839             var lookfor = '';
12840             Roo.each(name.split('.'), function(st) {
12841                 lookfor += (lookfor.length ? '.': '') + st;
12842                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
12843             });
12844             
12845             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
12846             
12847             
12848             if(format && useF){
12849                 
12850                 args = args ? ',' + args : "";
12851                  
12852                 if(format.substr(0, 5) != "this."){
12853                     format = "fm." + format + '(';
12854                 }else{
12855                     format = 'this.call("'+ format.substr(5) + '", ';
12856                     args = ", values";
12857                 }
12858                 
12859                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
12860             }
12861              
12862             if (args && args.length) {
12863                 // called with xxyx.yuu:(test,test)
12864                 // change to ()
12865                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
12866             }
12867             // raw.. - :raw modifier..
12868             return "'"+ sep + udef_st  + name + ")"+sep+"'";
12869             
12870         };
12871         var body;
12872         // branched to use + in gecko and [].join() in others
12873         if(Roo.isGecko){
12874             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
12875                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
12876                     "';};};";
12877         }else{
12878             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
12879             body.push(tpl.body.replace(/(\r\n|\n)/g,
12880                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
12881             body.push("'].join('');};};");
12882             body = body.join('');
12883         }
12884         
12885         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
12886        
12887         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
12888         eval(body);
12889         
12890         return this;
12891     },
12892      
12893     /**
12894      * same as applyTemplate, except it's done to one of the subTemplates
12895      * when using named templates, you can do:
12896      *
12897      * var str = pl.applySubTemplate('your-name', values);
12898      *
12899      * 
12900      * @param {Number} id of the template
12901      * @param {Object} values to apply to template
12902      * @param {Object} parent (normaly the instance of this object)
12903      */
12904     applySubTemplate : function(id, values, parent)
12905     {
12906         
12907         
12908         var t = this.tpls[id];
12909         
12910         
12911         try { 
12912             if(t.ifCall && !t.ifCall.call(this, values, parent)){
12913                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
12914                 return '';
12915             }
12916         } catch(e) {
12917             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
12918             Roo.log(values);
12919           
12920             return '';
12921         }
12922         try { 
12923             
12924             if(t.execCall && t.execCall.call(this, values, parent)){
12925                 return '';
12926             }
12927         } catch(e) {
12928             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12929             Roo.log(values);
12930             return '';
12931         }
12932         
12933         try {
12934             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
12935             parent = t.target ? values : parent;
12936             if(t.forCall && vs instanceof Array){
12937                 var buf = [];
12938                 for(var i = 0, len = vs.length; i < len; i++){
12939                     try {
12940                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
12941                     } catch (e) {
12942                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12943                         Roo.log(e.body);
12944                         //Roo.log(t.compiled);
12945                         Roo.log(vs[i]);
12946                     }   
12947                 }
12948                 return buf.join('');
12949             }
12950         } catch (e) {
12951             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
12952             Roo.log(values);
12953             return '';
12954         }
12955         try {
12956             return t.compiled.call(this, vs, parent);
12957         } catch (e) {
12958             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
12959             Roo.log(e.body);
12960             //Roo.log(t.compiled);
12961             Roo.log(values);
12962             return '';
12963         }
12964     },
12965
12966    
12967
12968     applyTemplate : function(values){
12969         return this.master.compiled.call(this, values, {});
12970         //var s = this.subs;
12971     },
12972
12973     apply : function(){
12974         return this.applyTemplate.apply(this, arguments);
12975     }
12976
12977  });
12978
12979 Roo.DomTemplate.from = function(el){
12980     el = Roo.getDom(el);
12981     return new Roo.Domtemplate(el.value || el.innerHTML);
12982 };/*
12983  * Based on:
12984  * Ext JS Library 1.1.1
12985  * Copyright(c) 2006-2007, Ext JS, LLC.
12986  *
12987  * Originally Released Under LGPL - original licence link has changed is not relivant.
12988  *
12989  * Fork - LGPL
12990  * <script type="text/javascript">
12991  */
12992
12993 /**
12994  * @class Roo.util.DelayedTask
12995  * Provides a convenient method of performing setTimeout where a new
12996  * timeout cancels the old timeout. An example would be performing validation on a keypress.
12997  * You can use this class to buffer
12998  * the keypress events for a certain number of milliseconds, and perform only if they stop
12999  * for that amount of time.
13000  * @constructor The parameters to this constructor serve as defaults and are not required.
13001  * @param {Function} fn (optional) The default function to timeout
13002  * @param {Object} scope (optional) The default scope of that timeout
13003  * @param {Array} args (optional) The default Array of arguments
13004  */
13005 Roo.util.DelayedTask = function(fn, scope, args){
13006     var id = null, d, t;
13007
13008     var call = function(){
13009         var now = new Date().getTime();
13010         if(now - t >= d){
13011             clearInterval(id);
13012             id = null;
13013             fn.apply(scope, args || []);
13014         }
13015     };
13016     /**
13017      * Cancels any pending timeout and queues a new one
13018      * @param {Number} delay The milliseconds to delay
13019      * @param {Function} newFn (optional) Overrides function passed to constructor
13020      * @param {Object} newScope (optional) Overrides scope passed to constructor
13021      * @param {Array} newArgs (optional) Overrides args passed to constructor
13022      */
13023     this.delay = function(delay, newFn, newScope, newArgs){
13024         if(id && delay != d){
13025             this.cancel();
13026         }
13027         d = delay;
13028         t = new Date().getTime();
13029         fn = newFn || fn;
13030         scope = newScope || scope;
13031         args = newArgs || args;
13032         if(!id){
13033             id = setInterval(call, d);
13034         }
13035     };
13036
13037     /**
13038      * Cancel the last queued timeout
13039      */
13040     this.cancel = function(){
13041         if(id){
13042             clearInterval(id);
13043             id = null;
13044         }
13045     };
13046 };/*
13047  * Based on:
13048  * Ext JS Library 1.1.1
13049  * Copyright(c) 2006-2007, Ext JS, LLC.
13050  *
13051  * Originally Released Under LGPL - original licence link has changed is not relivant.
13052  *
13053  * Fork - LGPL
13054  * <script type="text/javascript">
13055  */
13056  
13057  
13058 Roo.util.TaskRunner = function(interval){
13059     interval = interval || 10;
13060     var tasks = [], removeQueue = [];
13061     var id = 0;
13062     var running = false;
13063
13064     var stopThread = function(){
13065         running = false;
13066         clearInterval(id);
13067         id = 0;
13068     };
13069
13070     var startThread = function(){
13071         if(!running){
13072             running = true;
13073             id = setInterval(runTasks, interval);
13074         }
13075     };
13076
13077     var removeTask = function(task){
13078         removeQueue.push(task);
13079         if(task.onStop){
13080             task.onStop();
13081         }
13082     };
13083
13084     var runTasks = function(){
13085         if(removeQueue.length > 0){
13086             for(var i = 0, len = removeQueue.length; i < len; i++){
13087                 tasks.remove(removeQueue[i]);
13088             }
13089             removeQueue = [];
13090             if(tasks.length < 1){
13091                 stopThread();
13092                 return;
13093             }
13094         }
13095         var now = new Date().getTime();
13096         for(var i = 0, len = tasks.length; i < len; ++i){
13097             var t = tasks[i];
13098             var itime = now - t.taskRunTime;
13099             if(t.interval <= itime){
13100                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13101                 t.taskRunTime = now;
13102                 if(rt === false || t.taskRunCount === t.repeat){
13103                     removeTask(t);
13104                     return;
13105                 }
13106             }
13107             if(t.duration && t.duration <= (now - t.taskStartTime)){
13108                 removeTask(t);
13109             }
13110         }
13111     };
13112
13113     /**
13114      * Queues a new task.
13115      * @param {Object} task
13116      */
13117     this.start = function(task){
13118         tasks.push(task);
13119         task.taskStartTime = new Date().getTime();
13120         task.taskRunTime = 0;
13121         task.taskRunCount = 0;
13122         startThread();
13123         return task;
13124     };
13125
13126     this.stop = function(task){
13127         removeTask(task);
13128         return task;
13129     };
13130
13131     this.stopAll = function(){
13132         stopThread();
13133         for(var i = 0, len = tasks.length; i < len; i++){
13134             if(tasks[i].onStop){
13135                 tasks[i].onStop();
13136             }
13137         }
13138         tasks = [];
13139         removeQueue = [];
13140     };
13141 };
13142
13143 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13144  * Based on:
13145  * Ext JS Library 1.1.1
13146  * Copyright(c) 2006-2007, Ext JS, LLC.
13147  *
13148  * Originally Released Under LGPL - original licence link has changed is not relivant.
13149  *
13150  * Fork - LGPL
13151  * <script type="text/javascript">
13152  */
13153
13154  
13155 /**
13156  * @class Roo.util.MixedCollection
13157  * @extends Roo.util.Observable
13158  * A Collection class that maintains both numeric indexes and keys and exposes events.
13159  * @constructor
13160  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13161  * collection (defaults to false)
13162  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13163  * and return the key value for that item.  This is used when available to look up the key on items that
13164  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13165  * equivalent to providing an implementation for the {@link #getKey} method.
13166  */
13167 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13168     this.items = [];
13169     this.map = {};
13170     this.keys = [];
13171     this.length = 0;
13172     this.addEvents({
13173         /**
13174          * @event clear
13175          * Fires when the collection is cleared.
13176          */
13177         "clear" : true,
13178         /**
13179          * @event add
13180          * Fires when an item is added to the collection.
13181          * @param {Number} index The index at which the item was added.
13182          * @param {Object} o The item added.
13183          * @param {String} key The key associated with the added item.
13184          */
13185         "add" : true,
13186         /**
13187          * @event replace
13188          * Fires when an item is replaced in the collection.
13189          * @param {String} key he key associated with the new added.
13190          * @param {Object} old The item being replaced.
13191          * @param {Object} new The new item.
13192          */
13193         "replace" : true,
13194         /**
13195          * @event remove
13196          * Fires when an item is removed from the collection.
13197          * @param {Object} o The item being removed.
13198          * @param {String} key (optional) The key associated with the removed item.
13199          */
13200         "remove" : true,
13201         "sort" : true
13202     });
13203     this.allowFunctions = allowFunctions === true;
13204     if(keyFn){
13205         this.getKey = keyFn;
13206     }
13207     Roo.util.MixedCollection.superclass.constructor.call(this);
13208 };
13209
13210 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13211     allowFunctions : false,
13212     
13213 /**
13214  * Adds an item to the collection.
13215  * @param {String} key The key to associate with the item
13216  * @param {Object} o The item to add.
13217  * @return {Object} The item added.
13218  */
13219     add : function(key, o){
13220         if(arguments.length == 1){
13221             o = arguments[0];
13222             key = this.getKey(o);
13223         }
13224         if(typeof key == "undefined" || key === null){
13225             this.length++;
13226             this.items.push(o);
13227             this.keys.push(null);
13228         }else{
13229             var old = this.map[key];
13230             if(old){
13231                 return this.replace(key, o);
13232             }
13233             this.length++;
13234             this.items.push(o);
13235             this.map[key] = o;
13236             this.keys.push(key);
13237         }
13238         this.fireEvent("add", this.length-1, o, key);
13239         return o;
13240     },
13241        
13242 /**
13243   * MixedCollection has a generic way to fetch keys if you implement getKey.
13244 <pre><code>
13245 // normal way
13246 var mc = new Roo.util.MixedCollection();
13247 mc.add(someEl.dom.id, someEl);
13248 mc.add(otherEl.dom.id, otherEl);
13249 //and so on
13250
13251 // using getKey
13252 var mc = new Roo.util.MixedCollection();
13253 mc.getKey = function(el){
13254    return el.dom.id;
13255 };
13256 mc.add(someEl);
13257 mc.add(otherEl);
13258
13259 // or via the constructor
13260 var mc = new Roo.util.MixedCollection(false, function(el){
13261    return el.dom.id;
13262 });
13263 mc.add(someEl);
13264 mc.add(otherEl);
13265 </code></pre>
13266  * @param o {Object} The item for which to find the key.
13267  * @return {Object} The key for the passed item.
13268  */
13269     getKey : function(o){
13270          return o.id; 
13271     },
13272    
13273 /**
13274  * Replaces an item in the collection.
13275  * @param {String} key The key associated with the item to replace, or the item to replace.
13276  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
13277  * @return {Object}  The new item.
13278  */
13279     replace : function(key, o){
13280         if(arguments.length == 1){
13281             o = arguments[0];
13282             key = this.getKey(o);
13283         }
13284         var old = this.item(key);
13285         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
13286              return this.add(key, o);
13287         }
13288         var index = this.indexOfKey(key);
13289         this.items[index] = o;
13290         this.map[key] = o;
13291         this.fireEvent("replace", key, old, o);
13292         return o;
13293     },
13294    
13295 /**
13296  * Adds all elements of an Array or an Object to the collection.
13297  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
13298  * an Array of values, each of which are added to the collection.
13299  */
13300     addAll : function(objs){
13301         if(arguments.length > 1 || objs instanceof Array){
13302             var args = arguments.length > 1 ? arguments : objs;
13303             for(var i = 0, len = args.length; i < len; i++){
13304                 this.add(args[i]);
13305             }
13306         }else{
13307             for(var key in objs){
13308                 if(this.allowFunctions || typeof objs[key] != "function"){
13309                     this.add(key, objs[key]);
13310                 }
13311             }
13312         }
13313     },
13314    
13315 /**
13316  * Executes the specified function once for every item in the collection, passing each
13317  * item as the first and only parameter. returning false from the function will stop the iteration.
13318  * @param {Function} fn The function to execute for each item.
13319  * @param {Object} scope (optional) The scope in which to execute the function.
13320  */
13321     each : function(fn, scope){
13322         var items = [].concat(this.items); // each safe for removal
13323         for(var i = 0, len = items.length; i < len; i++){
13324             if(fn.call(scope || items[i], items[i], i, len) === false){
13325                 break;
13326             }
13327         }
13328     },
13329    
13330 /**
13331  * Executes the specified function once for every key in the collection, passing each
13332  * key, and its associated item as the first two parameters.
13333  * @param {Function} fn The function to execute for each item.
13334  * @param {Object} scope (optional) The scope in which to execute the function.
13335  */
13336     eachKey : function(fn, scope){
13337         for(var i = 0, len = this.keys.length; i < len; i++){
13338             fn.call(scope || window, this.keys[i], this.items[i], i, len);
13339         }
13340     },
13341    
13342 /**
13343  * Returns the first item in the collection which elicits a true return value from the
13344  * passed selection function.
13345  * @param {Function} fn The selection function to execute for each item.
13346  * @param {Object} scope (optional) The scope in which to execute the function.
13347  * @return {Object} The first item in the collection which returned true from the selection function.
13348  */
13349     find : function(fn, scope){
13350         for(var i = 0, len = this.items.length; i < len; i++){
13351             if(fn.call(scope || window, this.items[i], this.keys[i])){
13352                 return this.items[i];
13353             }
13354         }
13355         return null;
13356     },
13357    
13358 /**
13359  * Inserts an item at the specified index in the collection.
13360  * @param {Number} index The index to insert the item at.
13361  * @param {String} key The key to associate with the new item, or the item itself.
13362  * @param {Object} o  (optional) If the second parameter was a key, the new item.
13363  * @return {Object} The item inserted.
13364  */
13365     insert : function(index, key, o){
13366         if(arguments.length == 2){
13367             o = arguments[1];
13368             key = this.getKey(o);
13369         }
13370         if(index >= this.length){
13371             return this.add(key, o);
13372         }
13373         this.length++;
13374         this.items.splice(index, 0, o);
13375         if(typeof key != "undefined" && key != null){
13376             this.map[key] = o;
13377         }
13378         this.keys.splice(index, 0, key);
13379         this.fireEvent("add", index, o, key);
13380         return o;
13381     },
13382    
13383 /**
13384  * Removed an item from the collection.
13385  * @param {Object} o The item to remove.
13386  * @return {Object} The item removed.
13387  */
13388     remove : function(o){
13389         return this.removeAt(this.indexOf(o));
13390     },
13391    
13392 /**
13393  * Remove an item from a specified index in the collection.
13394  * @param {Number} index The index within the collection of the item to remove.
13395  */
13396     removeAt : function(index){
13397         if(index < this.length && index >= 0){
13398             this.length--;
13399             var o = this.items[index];
13400             this.items.splice(index, 1);
13401             var key = this.keys[index];
13402             if(typeof key != "undefined"){
13403                 delete this.map[key];
13404             }
13405             this.keys.splice(index, 1);
13406             this.fireEvent("remove", o, key);
13407         }
13408     },
13409    
13410 /**
13411  * Removed an item associated with the passed key fom the collection.
13412  * @param {String} key The key of the item to remove.
13413  */
13414     removeKey : function(key){
13415         return this.removeAt(this.indexOfKey(key));
13416     },
13417    
13418 /**
13419  * Returns the number of items in the collection.
13420  * @return {Number} the number of items in the collection.
13421  */
13422     getCount : function(){
13423         return this.length; 
13424     },
13425    
13426 /**
13427  * Returns index within the collection of the passed Object.
13428  * @param {Object} o The item to find the index of.
13429  * @return {Number} index of the item.
13430  */
13431     indexOf : function(o){
13432         if(!this.items.indexOf){
13433             for(var i = 0, len = this.items.length; i < len; i++){
13434                 if(this.items[i] == o) {
13435                     return i;
13436                 }
13437             }
13438             return -1;
13439         }else{
13440             return this.items.indexOf(o);
13441         }
13442     },
13443    
13444 /**
13445  * Returns index within the collection of the passed key.
13446  * @param {String} key The key to find the index of.
13447  * @return {Number} index of the key.
13448  */
13449     indexOfKey : function(key){
13450         if(!this.keys.indexOf){
13451             for(var i = 0, len = this.keys.length; i < len; i++){
13452                 if(this.keys[i] == key) {
13453                     return i;
13454                 }
13455             }
13456             return -1;
13457         }else{
13458             return this.keys.indexOf(key);
13459         }
13460     },
13461    
13462 /**
13463  * Returns the item associated with the passed key OR index. Key has priority over index.
13464  * @param {String/Number} key The key or index of the item.
13465  * @return {Object} The item associated with the passed key.
13466  */
13467     item : function(key){
13468         if (key === 'length') {
13469             return null;
13470         }
13471         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
13472         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
13473     },
13474     
13475 /**
13476  * Returns the item at the specified index.
13477  * @param {Number} index The index of the item.
13478  * @return {Object}
13479  */
13480     itemAt : function(index){
13481         return this.items[index];
13482     },
13483     
13484 /**
13485  * Returns the item associated with the passed key.
13486  * @param {String/Number} key The key of the item.
13487  * @return {Object} The item associated with the passed key.
13488  */
13489     key : function(key){
13490         return this.map[key];
13491     },
13492    
13493 /**
13494  * Returns true if the collection contains the passed Object as an item.
13495  * @param {Object} o  The Object to look for in the collection.
13496  * @return {Boolean} True if the collection contains the Object as an item.
13497  */
13498     contains : function(o){
13499         return this.indexOf(o) != -1;
13500     },
13501    
13502 /**
13503  * Returns true if the collection contains the passed Object as a key.
13504  * @param {String} key The key to look for in the collection.
13505  * @return {Boolean} True if the collection contains the Object as a key.
13506  */
13507     containsKey : function(key){
13508         return typeof this.map[key] != "undefined";
13509     },
13510    
13511 /**
13512  * Removes all items from the collection.
13513  */
13514     clear : function(){
13515         this.length = 0;
13516         this.items = [];
13517         this.keys = [];
13518         this.map = {};
13519         this.fireEvent("clear");
13520     },
13521    
13522 /**
13523  * Returns the first item in the collection.
13524  * @return {Object} the first item in the collection..
13525  */
13526     first : function(){
13527         return this.items[0]; 
13528     },
13529    
13530 /**
13531  * Returns the last item in the collection.
13532  * @return {Object} the last item in the collection..
13533  */
13534     last : function(){
13535         return this.items[this.length-1];   
13536     },
13537     
13538     _sort : function(property, dir, fn){
13539         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
13540         fn = fn || function(a, b){
13541             return a-b;
13542         };
13543         var c = [], k = this.keys, items = this.items;
13544         for(var i = 0, len = items.length; i < len; i++){
13545             c[c.length] = {key: k[i], value: items[i], index: i};
13546         }
13547         c.sort(function(a, b){
13548             var v = fn(a[property], b[property]) * dsc;
13549             if(v == 0){
13550                 v = (a.index < b.index ? -1 : 1);
13551             }
13552             return v;
13553         });
13554         for(var i = 0, len = c.length; i < len; i++){
13555             items[i] = c[i].value;
13556             k[i] = c[i].key;
13557         }
13558         this.fireEvent("sort", this);
13559     },
13560     
13561     /**
13562      * Sorts this collection with the passed comparison function
13563      * @param {String} direction (optional) "ASC" or "DESC"
13564      * @param {Function} fn (optional) comparison function
13565      */
13566     sort : function(dir, fn){
13567         this._sort("value", dir, fn);
13568     },
13569     
13570     /**
13571      * Sorts this collection by keys
13572      * @param {String} direction (optional) "ASC" or "DESC"
13573      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
13574      */
13575     keySort : function(dir, fn){
13576         this._sort("key", dir, fn || function(a, b){
13577             return String(a).toUpperCase()-String(b).toUpperCase();
13578         });
13579     },
13580     
13581     /**
13582      * Returns a range of items in this collection
13583      * @param {Number} startIndex (optional) defaults to 0
13584      * @param {Number} endIndex (optional) default to the last item
13585      * @return {Array} An array of items
13586      */
13587     getRange : function(start, end){
13588         var items = this.items;
13589         if(items.length < 1){
13590             return [];
13591         }
13592         start = start || 0;
13593         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
13594         var r = [];
13595         if(start <= end){
13596             for(var i = start; i <= end; i++) {
13597                     r[r.length] = items[i];
13598             }
13599         }else{
13600             for(var i = start; i >= end; i--) {
13601                     r[r.length] = items[i];
13602             }
13603         }
13604         return r;
13605     },
13606         
13607     /**
13608      * Filter the <i>objects</i> in this collection by a specific property. 
13609      * Returns a new collection that has been filtered.
13610      * @param {String} property A property on your objects
13611      * @param {String/RegExp} value Either string that the property values 
13612      * should start with or a RegExp to test against the property
13613      * @return {MixedCollection} The new filtered collection
13614      */
13615     filter : function(property, value){
13616         if(!value.exec){ // not a regex
13617             value = String(value);
13618             if(value.length == 0){
13619                 return this.clone();
13620             }
13621             value = new RegExp("^" + Roo.escapeRe(value), "i");
13622         }
13623         return this.filterBy(function(o){
13624             return o && value.test(o[property]);
13625         });
13626         },
13627     
13628     /**
13629      * Filter by a function. * Returns a new collection that has been filtered.
13630      * The passed function will be called with each 
13631      * object in the collection. If the function returns true, the value is included 
13632      * otherwise it is filtered.
13633      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
13634      * @param {Object} scope (optional) The scope of the function (defaults to this) 
13635      * @return {MixedCollection} The new filtered collection
13636      */
13637     filterBy : function(fn, scope){
13638         var r = new Roo.util.MixedCollection();
13639         r.getKey = this.getKey;
13640         var k = this.keys, it = this.items;
13641         for(var i = 0, len = it.length; i < len; i++){
13642             if(fn.call(scope||this, it[i], k[i])){
13643                                 r.add(k[i], it[i]);
13644                         }
13645         }
13646         return r;
13647     },
13648     
13649     /**
13650      * Creates a duplicate of this collection
13651      * @return {MixedCollection}
13652      */
13653     clone : function(){
13654         var r = new Roo.util.MixedCollection();
13655         var k = this.keys, it = this.items;
13656         for(var i = 0, len = it.length; i < len; i++){
13657             r.add(k[i], it[i]);
13658         }
13659         r.getKey = this.getKey;
13660         return r;
13661     }
13662 });
13663 /**
13664  * Returns the item associated with the passed key or index.
13665  * @method
13666  * @param {String/Number} key The key or index of the item.
13667  * @return {Object} The item associated with the passed key.
13668  */
13669 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
13670  * Based on:
13671  * Ext JS Library 1.1.1
13672  * Copyright(c) 2006-2007, Ext JS, LLC.
13673  *
13674  * Originally Released Under LGPL - original licence link has changed is not relivant.
13675  *
13676  * Fork - LGPL
13677  * <script type="text/javascript">
13678  */
13679 /**
13680  * @class Roo.util.JSON
13681  * Modified version of Douglas Crockford"s json.js that doesn"t
13682  * mess with the Object prototype 
13683  * http://www.json.org/js.html
13684  * @singleton
13685  */
13686 Roo.util.JSON = new (function(){
13687     var useHasOwn = {}.hasOwnProperty ? true : false;
13688     
13689     // crashes Safari in some instances
13690     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
13691     
13692     var pad = function(n) {
13693         return n < 10 ? "0" + n : n;
13694     };
13695     
13696     var m = {
13697         "\b": '\\b',
13698         "\t": '\\t',
13699         "\n": '\\n',
13700         "\f": '\\f',
13701         "\r": '\\r',
13702         '"' : '\\"',
13703         "\\": '\\\\'
13704     };
13705
13706     var encodeString = function(s){
13707         if (/["\\\x00-\x1f]/.test(s)) {
13708             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
13709                 var c = m[b];
13710                 if(c){
13711                     return c;
13712                 }
13713                 c = b.charCodeAt();
13714                 return "\\u00" +
13715                     Math.floor(c / 16).toString(16) +
13716                     (c % 16).toString(16);
13717             }) + '"';
13718         }
13719         return '"' + s + '"';
13720     };
13721     
13722     var encodeArray = function(o){
13723         var a = ["["], b, i, l = o.length, v;
13724             for (i = 0; i < l; i += 1) {
13725                 v = o[i];
13726                 switch (typeof v) {
13727                     case "undefined":
13728                     case "function":
13729                     case "unknown":
13730                         break;
13731                     default:
13732                         if (b) {
13733                             a.push(',');
13734                         }
13735                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
13736                         b = true;
13737                 }
13738             }
13739             a.push("]");
13740             return a.join("");
13741     };
13742     
13743     var encodeDate = function(o){
13744         return '"' + o.getFullYear() + "-" +
13745                 pad(o.getMonth() + 1) + "-" +
13746                 pad(o.getDate()) + "T" +
13747                 pad(o.getHours()) + ":" +
13748                 pad(o.getMinutes()) + ":" +
13749                 pad(o.getSeconds()) + '"';
13750     };
13751     
13752     /**
13753      * Encodes an Object, Array or other value
13754      * @param {Mixed} o The variable to encode
13755      * @return {String} The JSON string
13756      */
13757     this.encode = function(o)
13758     {
13759         // should this be extended to fully wrap stringify..
13760         
13761         if(typeof o == "undefined" || o === null){
13762             return "null";
13763         }else if(o instanceof Array){
13764             return encodeArray(o);
13765         }else if(o instanceof Date){
13766             return encodeDate(o);
13767         }else if(typeof o == "string"){
13768             return encodeString(o);
13769         }else if(typeof o == "number"){
13770             return isFinite(o) ? String(o) : "null";
13771         }else if(typeof o == "boolean"){
13772             return String(o);
13773         }else {
13774             var a = ["{"], b, i, v;
13775             for (i in o) {
13776                 if(!useHasOwn || o.hasOwnProperty(i)) {
13777                     v = o[i];
13778                     switch (typeof v) {
13779                     case "undefined":
13780                     case "function":
13781                     case "unknown":
13782                         break;
13783                     default:
13784                         if(b){
13785                             a.push(',');
13786                         }
13787                         a.push(this.encode(i), ":",
13788                                 v === null ? "null" : this.encode(v));
13789                         b = true;
13790                     }
13791                 }
13792             }
13793             a.push("}");
13794             return a.join("");
13795         }
13796     };
13797     
13798     /**
13799      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
13800      * @param {String} json The JSON string
13801      * @return {Object} The resulting object
13802      */
13803     this.decode = function(json){
13804         
13805         return  /** eval:var:json */ eval("(" + json + ')');
13806     };
13807 })();
13808 /** 
13809  * Shorthand for {@link Roo.util.JSON#encode}
13810  * @member Roo encode 
13811  * @method */
13812 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
13813 /** 
13814  * Shorthand for {@link Roo.util.JSON#decode}
13815  * @member Roo decode 
13816  * @method */
13817 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
13818 /*
13819  * Based on:
13820  * Ext JS Library 1.1.1
13821  * Copyright(c) 2006-2007, Ext JS, LLC.
13822  *
13823  * Originally Released Under LGPL - original licence link has changed is not relivant.
13824  *
13825  * Fork - LGPL
13826  * <script type="text/javascript">
13827  */
13828  
13829 /**
13830  * @class Roo.util.Format
13831  * Reusable data formatting functions
13832  * @singleton
13833  */
13834 Roo.util.Format = function(){
13835     var trimRe = /^\s+|\s+$/g;
13836     return {
13837         /**
13838          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
13839          * @param {String} value The string to truncate
13840          * @param {Number} length The maximum length to allow before truncating
13841          * @return {String} The converted text
13842          */
13843         ellipsis : function(value, len){
13844             if(value && value.length > len){
13845                 return value.substr(0, len-3)+"...";
13846             }
13847             return value;
13848         },
13849
13850         /**
13851          * Checks a reference and converts it to empty string if it is undefined
13852          * @param {Mixed} value Reference to check
13853          * @return {Mixed} Empty string if converted, otherwise the original value
13854          */
13855         undef : function(value){
13856             return typeof value != "undefined" ? value : "";
13857         },
13858
13859         /**
13860          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
13861          * @param {String} value The string to encode
13862          * @return {String} The encoded text
13863          */
13864         htmlEncode : function(value){
13865             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
13866         },
13867
13868         /**
13869          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
13870          * @param {String} value The string to decode
13871          * @return {String} The decoded text
13872          */
13873         htmlDecode : function(value){
13874             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
13875         },
13876
13877         /**
13878          * Trims any whitespace from either side of a string
13879          * @param {String} value The text to trim
13880          * @return {String} The trimmed text
13881          */
13882         trim : function(value){
13883             return String(value).replace(trimRe, "");
13884         },
13885
13886         /**
13887          * Returns a substring from within an original string
13888          * @param {String} value The original text
13889          * @param {Number} start The start index of the substring
13890          * @param {Number} length The length of the substring
13891          * @return {String} The substring
13892          */
13893         substr : function(value, start, length){
13894             return String(value).substr(start, length);
13895         },
13896
13897         /**
13898          * Converts a string to all lower case letters
13899          * @param {String} value The text to convert
13900          * @return {String} The converted text
13901          */
13902         lowercase : function(value){
13903             return String(value).toLowerCase();
13904         },
13905
13906         /**
13907          * Converts a string to all upper case letters
13908          * @param {String} value The text to convert
13909          * @return {String} The converted text
13910          */
13911         uppercase : function(value){
13912             return String(value).toUpperCase();
13913         },
13914
13915         /**
13916          * Converts the first character only of a string to upper case
13917          * @param {String} value The text to convert
13918          * @return {String} The converted text
13919          */
13920         capitalize : function(value){
13921             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
13922         },
13923
13924         // private
13925         call : function(value, fn){
13926             if(arguments.length > 2){
13927                 var args = Array.prototype.slice.call(arguments, 2);
13928                 args.unshift(value);
13929                  
13930                 return /** eval:var:value */  eval(fn).apply(window, args);
13931             }else{
13932                 /** eval:var:value */
13933                 return /** eval:var:value */ eval(fn).call(window, value);
13934             }
13935         },
13936
13937        
13938         /**
13939          * safer version of Math.toFixed..??/
13940          * @param {Number/String} value The numeric value to format
13941          * @param {Number/String} value Decimal places 
13942          * @return {String} The formatted currency string
13943          */
13944         toFixed : function(v, n)
13945         {
13946             // why not use to fixed - precision is buggered???
13947             if (!n) {
13948                 return Math.round(v-0);
13949             }
13950             var fact = Math.pow(10,n+1);
13951             v = (Math.round((v-0)*fact))/fact;
13952             var z = (''+fact).substring(2);
13953             if (v == Math.floor(v)) {
13954                 return Math.floor(v) + '.' + z;
13955             }
13956             
13957             // now just padd decimals..
13958             var ps = String(v).split('.');
13959             var fd = (ps[1] + z);
13960             var r = fd.substring(0,n); 
13961             var rm = fd.substring(n); 
13962             if (rm < 5) {
13963                 return ps[0] + '.' + r;
13964             }
13965             r*=1; // turn it into a number;
13966             r++;
13967             if (String(r).length != n) {
13968                 ps[0]*=1;
13969                 ps[0]++;
13970                 r = String(r).substring(1); // chop the end off.
13971             }
13972             
13973             return ps[0] + '.' + r;
13974              
13975         },
13976         
13977         /**
13978          * Format a number as US currency
13979          * @param {Number/String} value The numeric value to format
13980          * @return {String} The formatted currency string
13981          */
13982         usMoney : function(v){
13983             return '$' + Roo.util.Format.number(v);
13984         },
13985         
13986         /**
13987          * Format a number
13988          * eventually this should probably emulate php's number_format
13989          * @param {Number/String} value The numeric value to format
13990          * @param {Number} decimals number of decimal places
13991          * @param {String} delimiter for thousands (default comma)
13992          * @return {String} The formatted currency string
13993          */
13994         number : function(v, decimals, thousandsDelimiter)
13995         {
13996             // multiply and round.
13997             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
13998             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
13999             
14000             var mul = Math.pow(10, decimals);
14001             var zero = String(mul).substring(1);
14002             v = (Math.round((v-0)*mul))/mul;
14003             
14004             // if it's '0' number.. then
14005             
14006             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14007             v = String(v);
14008             var ps = v.split('.');
14009             var whole = ps[0];
14010             
14011             var r = /(\d+)(\d{3})/;
14012             // add comma's
14013             
14014             if(thousandsDelimiter.length != 0) {
14015                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14016             } 
14017             
14018             var sub = ps[1] ?
14019                     // has decimals..
14020                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14021                     // does not have decimals
14022                     (decimals ? ('.' + zero) : '');
14023             
14024             
14025             return whole + sub ;
14026         },
14027         
14028         /**
14029          * Parse a value into a formatted date using the specified format pattern.
14030          * @param {Mixed} value The value to format
14031          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14032          * @return {String} The formatted date string
14033          */
14034         date : function(v, format){
14035             if(!v){
14036                 return "";
14037             }
14038             if(!(v instanceof Date)){
14039                 v = new Date(Date.parse(v));
14040             }
14041             return v.dateFormat(format || Roo.util.Format.defaults.date);
14042         },
14043
14044         /**
14045          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14046          * @param {String} format Any valid date format string
14047          * @return {Function} The date formatting function
14048          */
14049         dateRenderer : function(format){
14050             return function(v){
14051                 return Roo.util.Format.date(v, format);  
14052             };
14053         },
14054
14055         // private
14056         stripTagsRE : /<\/?[^>]+>/gi,
14057         
14058         /**
14059          * Strips all HTML tags
14060          * @param {Mixed} value The text from which to strip tags
14061          * @return {String} The stripped text
14062          */
14063         stripTags : function(v){
14064             return !v ? v : String(v).replace(this.stripTagsRE, "");
14065         },
14066         
14067         /**
14068          * Size in Mb,Gb etc.
14069          * @param {Number} value The number to be formated
14070          * @param {number} decimals how many decimal places
14071          * @return {String} the formated string
14072          */
14073         size : function(value, decimals)
14074         {
14075             var sizes = ['b', 'k', 'M', 'G', 'T'];
14076             if (value == 0) {
14077                 return 0;
14078             }
14079             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14080             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14081         }
14082         
14083         
14084         
14085     };
14086 }();
14087 Roo.util.Format.defaults = {
14088     date : 'd/M/Y'
14089 };/*
14090  * Based on:
14091  * Ext JS Library 1.1.1
14092  * Copyright(c) 2006-2007, Ext JS, LLC.
14093  *
14094  * Originally Released Under LGPL - original licence link has changed is not relivant.
14095  *
14096  * Fork - LGPL
14097  * <script type="text/javascript">
14098  */
14099
14100
14101  
14102
14103 /**
14104  * @class Roo.MasterTemplate
14105  * @extends Roo.Template
14106  * Provides a template that can have child templates. The syntax is:
14107 <pre><code>
14108 var t = new Roo.MasterTemplate(
14109         '&lt;select name="{name}"&gt;',
14110                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14111         '&lt;/select&gt;'
14112 );
14113 t.add('options', {value: 'foo', text: 'bar'});
14114 // or you can add multiple child elements in one shot
14115 t.addAll('options', [
14116     {value: 'foo', text: 'bar'},
14117     {value: 'foo2', text: 'bar2'},
14118     {value: 'foo3', text: 'bar3'}
14119 ]);
14120 // then append, applying the master template values
14121 t.append('my-form', {name: 'my-select'});
14122 </code></pre>
14123 * A name attribute for the child template is not required if you have only one child
14124 * template or you want to refer to them by index.
14125  */
14126 Roo.MasterTemplate = function(){
14127     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14128     this.originalHtml = this.html;
14129     var st = {};
14130     var m, re = this.subTemplateRe;
14131     re.lastIndex = 0;
14132     var subIndex = 0;
14133     while(m = re.exec(this.html)){
14134         var name = m[1], content = m[2];
14135         st[subIndex] = {
14136             name: name,
14137             index: subIndex,
14138             buffer: [],
14139             tpl : new Roo.Template(content)
14140         };
14141         if(name){
14142             st[name] = st[subIndex];
14143         }
14144         st[subIndex].tpl.compile();
14145         st[subIndex].tpl.call = this.call.createDelegate(this);
14146         subIndex++;
14147     }
14148     this.subCount = subIndex;
14149     this.subs = st;
14150 };
14151 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14152     /**
14153     * The regular expression used to match sub templates
14154     * @type RegExp
14155     * @property
14156     */
14157     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14158
14159     /**
14160      * Applies the passed values to a child template.
14161      * @param {String/Number} name (optional) The name or index of the child template
14162      * @param {Array/Object} values The values to be applied to the template
14163      * @return {MasterTemplate} this
14164      */
14165      add : function(name, values){
14166         if(arguments.length == 1){
14167             values = arguments[0];
14168             name = 0;
14169         }
14170         var s = this.subs[name];
14171         s.buffer[s.buffer.length] = s.tpl.apply(values);
14172         return this;
14173     },
14174
14175     /**
14176      * Applies all the passed values to a child template.
14177      * @param {String/Number} name (optional) The name or index of the child template
14178      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14179      * @param {Boolean} reset (optional) True to reset the template first
14180      * @return {MasterTemplate} this
14181      */
14182     fill : function(name, values, reset){
14183         var a = arguments;
14184         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14185             values = a[0];
14186             name = 0;
14187             reset = a[1];
14188         }
14189         if(reset){
14190             this.reset();
14191         }
14192         for(var i = 0, len = values.length; i < len; i++){
14193             this.add(name, values[i]);
14194         }
14195         return this;
14196     },
14197
14198     /**
14199      * Resets the template for reuse
14200      * @return {MasterTemplate} this
14201      */
14202      reset : function(){
14203         var s = this.subs;
14204         for(var i = 0; i < this.subCount; i++){
14205             s[i].buffer = [];
14206         }
14207         return this;
14208     },
14209
14210     applyTemplate : function(values){
14211         var s = this.subs;
14212         var replaceIndex = -1;
14213         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14214             return s[++replaceIndex].buffer.join("");
14215         });
14216         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14217     },
14218
14219     apply : function(){
14220         return this.applyTemplate.apply(this, arguments);
14221     },
14222
14223     compile : function(){return this;}
14224 });
14225
14226 /**
14227  * Alias for fill().
14228  * @method
14229  */
14230 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14231  /**
14232  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14233  * var tpl = Roo.MasterTemplate.from('element-id');
14234  * @param {String/HTMLElement} el
14235  * @param {Object} config
14236  * @static
14237  */
14238 Roo.MasterTemplate.from = function(el, config){
14239     el = Roo.getDom(el);
14240     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14241 };/*
14242  * Based on:
14243  * Ext JS Library 1.1.1
14244  * Copyright(c) 2006-2007, Ext JS, LLC.
14245  *
14246  * Originally Released Under LGPL - original licence link has changed is not relivant.
14247  *
14248  * Fork - LGPL
14249  * <script type="text/javascript">
14250  */
14251
14252  
14253 /**
14254  * @class Roo.util.CSS
14255  * Utility class for manipulating CSS rules
14256  * @singleton
14257  */
14258 Roo.util.CSS = function(){
14259         var rules = null;
14260         var doc = document;
14261
14262     var camelRe = /(-[a-z])/gi;
14263     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14264
14265    return {
14266    /**
14267     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14268     * tag and appended to the HEAD of the document.
14269     * @param {String|Object} cssText The text containing the css rules
14270     * @param {String} id An id to add to the stylesheet for later removal
14271     * @return {StyleSheet}
14272     */
14273     createStyleSheet : function(cssText, id){
14274         var ss;
14275         var head = doc.getElementsByTagName("head")[0];
14276         var nrules = doc.createElement("style");
14277         nrules.setAttribute("type", "text/css");
14278         if(id){
14279             nrules.setAttribute("id", id);
14280         }
14281         if (typeof(cssText) != 'string') {
14282             // support object maps..
14283             // not sure if this a good idea.. 
14284             // perhaps it should be merged with the general css handling
14285             // and handle js style props.
14286             var cssTextNew = [];
14287             for(var n in cssText) {
14288                 var citems = [];
14289                 for(var k in cssText[n]) {
14290                     citems.push( k + ' : ' +cssText[n][k] + ';' );
14291                 }
14292                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
14293                 
14294             }
14295             cssText = cssTextNew.join("\n");
14296             
14297         }
14298        
14299        
14300        if(Roo.isIE){
14301            head.appendChild(nrules);
14302            ss = nrules.styleSheet;
14303            ss.cssText = cssText;
14304        }else{
14305            try{
14306                 nrules.appendChild(doc.createTextNode(cssText));
14307            }catch(e){
14308                nrules.cssText = cssText; 
14309            }
14310            head.appendChild(nrules);
14311            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
14312        }
14313        this.cacheStyleSheet(ss);
14314        return ss;
14315    },
14316
14317    /**
14318     * Removes a style or link tag by id
14319     * @param {String} id The id of the tag
14320     */
14321    removeStyleSheet : function(id){
14322        var existing = doc.getElementById(id);
14323        if(existing){
14324            existing.parentNode.removeChild(existing);
14325        }
14326    },
14327
14328    /**
14329     * Dynamically swaps an existing stylesheet reference for a new one
14330     * @param {String} id The id of an existing link tag to remove
14331     * @param {String} url The href of the new stylesheet to include
14332     */
14333    swapStyleSheet : function(id, url){
14334        this.removeStyleSheet(id);
14335        var ss = doc.createElement("link");
14336        ss.setAttribute("rel", "stylesheet");
14337        ss.setAttribute("type", "text/css");
14338        ss.setAttribute("id", id);
14339        ss.setAttribute("href", url);
14340        doc.getElementsByTagName("head")[0].appendChild(ss);
14341    },
14342    
14343    /**
14344     * Refresh the rule cache if you have dynamically added stylesheets
14345     * @return {Object} An object (hash) of rules indexed by selector
14346     */
14347    refreshCache : function(){
14348        return this.getRules(true);
14349    },
14350
14351    // private
14352    cacheStyleSheet : function(stylesheet){
14353        if(!rules){
14354            rules = {};
14355        }
14356        try{// try catch for cross domain access issue
14357            var ssRules = stylesheet.cssRules || stylesheet.rules;
14358            for(var j = ssRules.length-1; j >= 0; --j){
14359                rules[ssRules[j].selectorText] = ssRules[j];
14360            }
14361        }catch(e){}
14362    },
14363    
14364    /**
14365     * Gets all css rules for the document
14366     * @param {Boolean} refreshCache true to refresh the internal cache
14367     * @return {Object} An object (hash) of rules indexed by selector
14368     */
14369    getRules : function(refreshCache){
14370                 if(rules == null || refreshCache){
14371                         rules = {};
14372                         var ds = doc.styleSheets;
14373                         for(var i =0, len = ds.length; i < len; i++){
14374                             try{
14375                         this.cacheStyleSheet(ds[i]);
14376                     }catch(e){} 
14377                 }
14378                 }
14379                 return rules;
14380         },
14381         
14382         /**
14383     * Gets an an individual CSS rule by selector(s)
14384     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
14385     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
14386     * @return {CSSRule} The CSS rule or null if one is not found
14387     */
14388    getRule : function(selector, refreshCache){
14389                 var rs = this.getRules(refreshCache);
14390                 if(!(selector instanceof Array)){
14391                     return rs[selector];
14392                 }
14393                 for(var i = 0; i < selector.length; i++){
14394                         if(rs[selector[i]]){
14395                                 return rs[selector[i]];
14396                         }
14397                 }
14398                 return null;
14399         },
14400         
14401         
14402         /**
14403     * Updates a rule property
14404     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
14405     * @param {String} property The css property
14406     * @param {String} value The new value for the property
14407     * @return {Boolean} true If a rule was found and updated
14408     */
14409    updateRule : function(selector, property, value){
14410                 if(!(selector instanceof Array)){
14411                         var rule = this.getRule(selector);
14412                         if(rule){
14413                                 rule.style[property.replace(camelRe, camelFn)] = value;
14414                                 return true;
14415                         }
14416                 }else{
14417                         for(var i = 0; i < selector.length; i++){
14418                                 if(this.updateRule(selector[i], property, value)){
14419                                         return true;
14420                                 }
14421                         }
14422                 }
14423                 return false;
14424         }
14425    };   
14426 }();/*
14427  * Based on:
14428  * Ext JS Library 1.1.1
14429  * Copyright(c) 2006-2007, Ext JS, LLC.
14430  *
14431  * Originally Released Under LGPL - original licence link has changed is not relivant.
14432  *
14433  * Fork - LGPL
14434  * <script type="text/javascript">
14435  */
14436
14437  
14438
14439 /**
14440  * @class Roo.util.ClickRepeater
14441  * @extends Roo.util.Observable
14442  * 
14443  * A wrapper class which can be applied to any element. Fires a "click" event while the
14444  * mouse is pressed. The interval between firings may be specified in the config but
14445  * defaults to 10 milliseconds.
14446  * 
14447  * Optionally, a CSS class may be applied to the element during the time it is pressed.
14448  * 
14449  * @cfg {String/HTMLElement/Element} el The element to act as a button.
14450  * @cfg {Number} delay The initial delay before the repeating event begins firing.
14451  * Similar to an autorepeat key delay.
14452  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
14453  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
14454  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
14455  *           "interval" and "delay" are ignored. "immediate" is honored.
14456  * @cfg {Boolean} preventDefault True to prevent the default click event
14457  * @cfg {Boolean} stopDefault True to stop the default click event
14458  * 
14459  * @history
14460  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
14461  *     2007-02-02 jvs Renamed to ClickRepeater
14462  *   2007-02-03 jvs Modifications for FF Mac and Safari 
14463  *
14464  *  @constructor
14465  * @param {String/HTMLElement/Element} el The element to listen on
14466  * @param {Object} config
14467  **/
14468 Roo.util.ClickRepeater = function(el, config)
14469 {
14470     this.el = Roo.get(el);
14471     this.el.unselectable();
14472
14473     Roo.apply(this, config);
14474
14475     this.addEvents({
14476     /**
14477      * @event mousedown
14478      * Fires when the mouse button is depressed.
14479      * @param {Roo.util.ClickRepeater} this
14480      */
14481         "mousedown" : true,
14482     /**
14483      * @event click
14484      * Fires on a specified interval during the time the element is pressed.
14485      * @param {Roo.util.ClickRepeater} this
14486      */
14487         "click" : true,
14488     /**
14489      * @event mouseup
14490      * Fires when the mouse key is released.
14491      * @param {Roo.util.ClickRepeater} this
14492      */
14493         "mouseup" : true
14494     });
14495
14496     this.el.on("mousedown", this.handleMouseDown, this);
14497     if(this.preventDefault || this.stopDefault){
14498         this.el.on("click", function(e){
14499             if(this.preventDefault){
14500                 e.preventDefault();
14501             }
14502             if(this.stopDefault){
14503                 e.stopEvent();
14504             }
14505         }, this);
14506     }
14507
14508     // allow inline handler
14509     if(this.handler){
14510         this.on("click", this.handler,  this.scope || this);
14511     }
14512
14513     Roo.util.ClickRepeater.superclass.constructor.call(this);
14514 };
14515
14516 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
14517     interval : 20,
14518     delay: 250,
14519     preventDefault : true,
14520     stopDefault : false,
14521     timer : 0,
14522
14523     // private
14524     handleMouseDown : function(){
14525         clearTimeout(this.timer);
14526         this.el.blur();
14527         if(this.pressClass){
14528             this.el.addClass(this.pressClass);
14529         }
14530         this.mousedownTime = new Date();
14531
14532         Roo.get(document).on("mouseup", this.handleMouseUp, this);
14533         this.el.on("mouseout", this.handleMouseOut, this);
14534
14535         this.fireEvent("mousedown", this);
14536         this.fireEvent("click", this);
14537         
14538         this.timer = this.click.defer(this.delay || this.interval, this);
14539     },
14540
14541     // private
14542     click : function(){
14543         this.fireEvent("click", this);
14544         this.timer = this.click.defer(this.getInterval(), this);
14545     },
14546
14547     // private
14548     getInterval: function(){
14549         if(!this.accelerate){
14550             return this.interval;
14551         }
14552         var pressTime = this.mousedownTime.getElapsed();
14553         if(pressTime < 500){
14554             return 400;
14555         }else if(pressTime < 1700){
14556             return 320;
14557         }else if(pressTime < 2600){
14558             return 250;
14559         }else if(pressTime < 3500){
14560             return 180;
14561         }else if(pressTime < 4400){
14562             return 140;
14563         }else if(pressTime < 5300){
14564             return 80;
14565         }else if(pressTime < 6200){
14566             return 50;
14567         }else{
14568             return 10;
14569         }
14570     },
14571
14572     // private
14573     handleMouseOut : function(){
14574         clearTimeout(this.timer);
14575         if(this.pressClass){
14576             this.el.removeClass(this.pressClass);
14577         }
14578         this.el.on("mouseover", this.handleMouseReturn, this);
14579     },
14580
14581     // private
14582     handleMouseReturn : function(){
14583         this.el.un("mouseover", this.handleMouseReturn);
14584         if(this.pressClass){
14585             this.el.addClass(this.pressClass);
14586         }
14587         this.click();
14588     },
14589
14590     // private
14591     handleMouseUp : function(){
14592         clearTimeout(this.timer);
14593         this.el.un("mouseover", this.handleMouseReturn);
14594         this.el.un("mouseout", this.handleMouseOut);
14595         Roo.get(document).un("mouseup", this.handleMouseUp);
14596         this.el.removeClass(this.pressClass);
14597         this.fireEvent("mouseup", this);
14598     }
14599 });/**
14600  * @class Roo.util.Clipboard
14601  * @static
14602  * 
14603  * Clipboard UTILS
14604  * 
14605  **/
14606 Roo.util.Clipboard = {
14607     /**
14608      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
14609      * @param {String} text to copy to clipboard
14610      */
14611     write : function(text) {
14612         // navigator clipboard api needs a secure context (https)
14613         if (navigator.clipboard && window.isSecureContext) {
14614             // navigator clipboard api method'
14615             navigator.clipboard.writeText(text);
14616             return ;
14617         } 
14618         // text area method
14619         var ta = document.createElement("textarea");
14620         ta.value = text;
14621         // make the textarea out of viewport
14622         ta.style.position = "fixed";
14623         ta.style.left = "-999999px";
14624         ta.style.top = "-999999px";
14625         document.body.appendChild(ta);
14626         ta.focus();
14627         ta.select();
14628         document.execCommand('copy');
14629         (function() {
14630             ta.remove();
14631         }).defer(100);
14632         
14633     }
14634         
14635 }
14636     /*
14637  * Based on:
14638  * Ext JS Library 1.1.1
14639  * Copyright(c) 2006-2007, Ext JS, LLC.
14640  *
14641  * Originally Released Under LGPL - original licence link has changed is not relivant.
14642  *
14643  * Fork - LGPL
14644  * <script type="text/javascript">
14645  */
14646
14647  
14648 /**
14649  * @class Roo.KeyNav
14650  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
14651  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
14652  * way to implement custom navigation schemes for any UI component.</p>
14653  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
14654  * pageUp, pageDown, del, home, end.  Usage:</p>
14655  <pre><code>
14656 var nav = new Roo.KeyNav("my-element", {
14657     "left" : function(e){
14658         this.moveLeft(e.ctrlKey);
14659     },
14660     "right" : function(e){
14661         this.moveRight(e.ctrlKey);
14662     },
14663     "enter" : function(e){
14664         this.save();
14665     },
14666     scope : this
14667 });
14668 </code></pre>
14669  * @constructor
14670  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14671  * @param {Object} config The config
14672  */
14673 Roo.KeyNav = function(el, config){
14674     this.el = Roo.get(el);
14675     Roo.apply(this, config);
14676     if(!this.disabled){
14677         this.disabled = true;
14678         this.enable();
14679     }
14680 };
14681
14682 Roo.KeyNav.prototype = {
14683     /**
14684      * @cfg {Boolean} disabled
14685      * True to disable this KeyNav instance (defaults to false)
14686      */
14687     disabled : false,
14688     /**
14689      * @cfg {String} defaultEventAction
14690      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
14691      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
14692      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
14693      */
14694     defaultEventAction: "stopEvent",
14695     /**
14696      * @cfg {Boolean} forceKeyDown
14697      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
14698      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
14699      * handle keydown instead of keypress.
14700      */
14701     forceKeyDown : false,
14702
14703     // private
14704     prepareEvent : function(e){
14705         var k = e.getKey();
14706         var h = this.keyToHandler[k];
14707         //if(h && this[h]){
14708         //    e.stopPropagation();
14709         //}
14710         if(Roo.isSafari && h && k >= 37 && k <= 40){
14711             e.stopEvent();
14712         }
14713     },
14714
14715     // private
14716     relay : function(e){
14717         var k = e.getKey();
14718         var h = this.keyToHandler[k];
14719         if(h && this[h]){
14720             if(this.doRelay(e, this[h], h) !== true){
14721                 e[this.defaultEventAction]();
14722             }
14723         }
14724     },
14725
14726     // private
14727     doRelay : function(e, h, hname){
14728         return h.call(this.scope || this, e);
14729     },
14730
14731     // possible handlers
14732     enter : false,
14733     left : false,
14734     right : false,
14735     up : false,
14736     down : false,
14737     tab : false,
14738     esc : false,
14739     pageUp : false,
14740     pageDown : false,
14741     del : false,
14742     home : false,
14743     end : false,
14744
14745     // quick lookup hash
14746     keyToHandler : {
14747         37 : "left",
14748         39 : "right",
14749         38 : "up",
14750         40 : "down",
14751         33 : "pageUp",
14752         34 : "pageDown",
14753         46 : "del",
14754         36 : "home",
14755         35 : "end",
14756         13 : "enter",
14757         27 : "esc",
14758         9  : "tab"
14759     },
14760
14761         /**
14762          * Enable this KeyNav
14763          */
14764         enable: function(){
14765                 if(this.disabled){
14766             // ie won't do special keys on keypress, no one else will repeat keys with keydown
14767             // the EventObject will normalize Safari automatically
14768             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14769                 this.el.on("keydown", this.relay,  this);
14770             }else{
14771                 this.el.on("keydown", this.prepareEvent,  this);
14772                 this.el.on("keypress", this.relay,  this);
14773             }
14774                     this.disabled = false;
14775                 }
14776         },
14777
14778         /**
14779          * Disable this KeyNav
14780          */
14781         disable: function(){
14782                 if(!this.disabled){
14783                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
14784                 this.el.un("keydown", this.relay);
14785             }else{
14786                 this.el.un("keydown", this.prepareEvent);
14787                 this.el.un("keypress", this.relay);
14788             }
14789                     this.disabled = true;
14790                 }
14791         }
14792 };/*
14793  * Based on:
14794  * Ext JS Library 1.1.1
14795  * Copyright(c) 2006-2007, Ext JS, LLC.
14796  *
14797  * Originally Released Under LGPL - original licence link has changed is not relivant.
14798  *
14799  * Fork - LGPL
14800  * <script type="text/javascript">
14801  */
14802
14803  
14804 /**
14805  * @class Roo.KeyMap
14806  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
14807  * The constructor accepts the same config object as defined by {@link #addBinding}.
14808  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
14809  * combination it will call the function with this signature (if the match is a multi-key
14810  * combination the callback will still be called only once): (String key, Roo.EventObject e)
14811  * A KeyMap can also handle a string representation of keys.<br />
14812  * Usage:
14813  <pre><code>
14814 // map one key by key code
14815 var map = new Roo.KeyMap("my-element", {
14816     key: 13, // or Roo.EventObject.ENTER
14817     fn: myHandler,
14818     scope: myObject
14819 });
14820
14821 // map multiple keys to one action by string
14822 var map = new Roo.KeyMap("my-element", {
14823     key: "a\r\n\t",
14824     fn: myHandler,
14825     scope: myObject
14826 });
14827
14828 // map multiple keys to multiple actions by strings and array of codes
14829 var map = new Roo.KeyMap("my-element", [
14830     {
14831         key: [10,13],
14832         fn: function(){ alert("Return was pressed"); }
14833     }, {
14834         key: "abc",
14835         fn: function(){ alert('a, b or c was pressed'); }
14836     }, {
14837         key: "\t",
14838         ctrl:true,
14839         shift:true,
14840         fn: function(){ alert('Control + shift + tab was pressed.'); }
14841     }
14842 ]);
14843 </code></pre>
14844  * <b>Note: A KeyMap starts enabled</b>
14845  * @constructor
14846  * @param {String/HTMLElement/Roo.Element} el The element to bind to
14847  * @param {Object} config The config (see {@link #addBinding})
14848  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
14849  */
14850 Roo.KeyMap = function(el, config, eventName){
14851     this.el  = Roo.get(el);
14852     this.eventName = eventName || "keydown";
14853     this.bindings = [];
14854     if(config){
14855         this.addBinding(config);
14856     }
14857     this.enable();
14858 };
14859
14860 Roo.KeyMap.prototype = {
14861     /**
14862      * True to stop the event from bubbling and prevent the default browser action if the
14863      * key was handled by the KeyMap (defaults to false)
14864      * @type Boolean
14865      */
14866     stopEvent : false,
14867
14868     /**
14869      * Add a new binding to this KeyMap. The following config object properties are supported:
14870      * <pre>
14871 Property    Type             Description
14872 ----------  ---------------  ----------------------------------------------------------------------
14873 key         String/Array     A single keycode or an array of keycodes to handle
14874 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
14875 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
14876 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
14877 fn          Function         The function to call when KeyMap finds the expected key combination
14878 scope       Object           The scope of the callback function
14879 </pre>
14880      *
14881      * Usage:
14882      * <pre><code>
14883 // Create a KeyMap
14884 var map = new Roo.KeyMap(document, {
14885     key: Roo.EventObject.ENTER,
14886     fn: handleKey,
14887     scope: this
14888 });
14889
14890 //Add a new binding to the existing KeyMap later
14891 map.addBinding({
14892     key: 'abc',
14893     shift: true,
14894     fn: handleKey,
14895     scope: this
14896 });
14897 </code></pre>
14898      * @param {Object/Array} config A single KeyMap config or an array of configs
14899      */
14900         addBinding : function(config){
14901         if(config instanceof Array){
14902             for(var i = 0, len = config.length; i < len; i++){
14903                 this.addBinding(config[i]);
14904             }
14905             return;
14906         }
14907         var keyCode = config.key,
14908             shift = config.shift, 
14909             ctrl = config.ctrl, 
14910             alt = config.alt,
14911             fn = config.fn,
14912             scope = config.scope;
14913         if(typeof keyCode == "string"){
14914             var ks = [];
14915             var keyString = keyCode.toUpperCase();
14916             for(var j = 0, len = keyString.length; j < len; j++){
14917                 ks.push(keyString.charCodeAt(j));
14918             }
14919             keyCode = ks;
14920         }
14921         var keyArray = keyCode instanceof Array;
14922         var handler = function(e){
14923             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
14924                 var k = e.getKey();
14925                 if(keyArray){
14926                     for(var i = 0, len = keyCode.length; i < len; i++){
14927                         if(keyCode[i] == k){
14928                           if(this.stopEvent){
14929                               e.stopEvent();
14930                           }
14931                           fn.call(scope || window, k, e);
14932                           return;
14933                         }
14934                     }
14935                 }else{
14936                     if(k == keyCode){
14937                         if(this.stopEvent){
14938                            e.stopEvent();
14939                         }
14940                         fn.call(scope || window, k, e);
14941                     }
14942                 }
14943             }
14944         };
14945         this.bindings.push(handler);  
14946         },
14947
14948     /**
14949      * Shorthand for adding a single key listener
14950      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
14951      * following options:
14952      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
14953      * @param {Function} fn The function to call
14954      * @param {Object} scope (optional) The scope of the function
14955      */
14956     on : function(key, fn, scope){
14957         var keyCode, shift, ctrl, alt;
14958         if(typeof key == "object" && !(key instanceof Array)){
14959             keyCode = key.key;
14960             shift = key.shift;
14961             ctrl = key.ctrl;
14962             alt = key.alt;
14963         }else{
14964             keyCode = key;
14965         }
14966         this.addBinding({
14967             key: keyCode,
14968             shift: shift,
14969             ctrl: ctrl,
14970             alt: alt,
14971             fn: fn,
14972             scope: scope
14973         })
14974     },
14975
14976     // private
14977     handleKeyDown : function(e){
14978             if(this.enabled){ //just in case
14979             var b = this.bindings;
14980             for(var i = 0, len = b.length; i < len; i++){
14981                 b[i].call(this, e);
14982             }
14983             }
14984         },
14985         
14986         /**
14987          * Returns true if this KeyMap is enabled
14988          * @return {Boolean} 
14989          */
14990         isEnabled : function(){
14991             return this.enabled;  
14992         },
14993         
14994         /**
14995          * Enables this KeyMap
14996          */
14997         enable: function(){
14998                 if(!this.enabled){
14999                     this.el.on(this.eventName, this.handleKeyDown, this);
15000                     this.enabled = true;
15001                 }
15002         },
15003
15004         /**
15005          * Disable this KeyMap
15006          */
15007         disable: function(){
15008                 if(this.enabled){
15009                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15010                     this.enabled = false;
15011                 }
15012         }
15013 };/*
15014  * Based on:
15015  * Ext JS Library 1.1.1
15016  * Copyright(c) 2006-2007, Ext JS, LLC.
15017  *
15018  * Originally Released Under LGPL - original licence link has changed is not relivant.
15019  *
15020  * Fork - LGPL
15021  * <script type="text/javascript">
15022  */
15023
15024  
15025 /**
15026  * @class Roo.util.TextMetrics
15027  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15028  * wide, in pixels, a given block of text will be.
15029  * @singleton
15030  */
15031 Roo.util.TextMetrics = function(){
15032     var shared;
15033     return {
15034         /**
15035          * Measures the size of the specified text
15036          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15037          * that can affect the size of the rendered text
15038          * @param {String} text The text to measure
15039          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15040          * in order to accurately measure the text height
15041          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15042          */
15043         measure : function(el, text, fixedWidth){
15044             if(!shared){
15045                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15046             }
15047             shared.bind(el);
15048             shared.setFixedWidth(fixedWidth || 'auto');
15049             return shared.getSize(text);
15050         },
15051
15052         /**
15053          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15054          * the overhead of multiple calls to initialize the style properties on each measurement.
15055          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15056          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15057          * in order to accurately measure the text height
15058          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15059          */
15060         createInstance : function(el, fixedWidth){
15061             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15062         }
15063     };
15064 }();
15065
15066  
15067
15068 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth){
15069     var ml = new Roo.Element(document.createElement('div'));
15070     document.body.appendChild(ml.dom);
15071     ml.position('absolute');
15072     ml.setLeftTop(-1000, -1000);
15073     ml.hide();
15074
15075     if(fixedWidth){
15076         ml.setWidth(fixedWidth);
15077     }
15078      
15079     var instance = {
15080         /**
15081          * Returns the size of the specified text based on the internal element's style and width properties
15082          * @memberOf Roo.util.TextMetrics.Instance#
15083          * @param {String} text The text to measure
15084          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15085          */
15086         getSize : function(text){
15087             ml.update(text);
15088             var s = ml.getSize();
15089             ml.update('');
15090             return s;
15091         },
15092
15093         /**
15094          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15095          * that can affect the size of the rendered text
15096          * @memberOf Roo.util.TextMetrics.Instance#
15097          * @param {String/HTMLElement} el The element, dom node or id
15098          */
15099         bind : function(el){
15100             ml.setStyle(
15101                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15102             );
15103         },
15104
15105         /**
15106          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15107          * to set a fixed width in order to accurately measure the text height.
15108          * @memberOf Roo.util.TextMetrics.Instance#
15109          * @param {Number} width The width to set on the element
15110          */
15111         setFixedWidth : function(width){
15112             ml.setWidth(width);
15113         },
15114
15115         /**
15116          * Returns the measured width of the specified text
15117          * @memberOf Roo.util.TextMetrics.Instance#
15118          * @param {String} text The text to measure
15119          * @return {Number} width The width in pixels
15120          */
15121         getWidth : function(text){
15122             ml.dom.style.width = 'auto';
15123             return this.getSize(text).width;
15124         },
15125
15126         /**
15127          * Returns the measured height of the specified text.  For multiline text, be sure to call
15128          * {@link #setFixedWidth} if necessary.
15129          * @memberOf Roo.util.TextMetrics.Instance#
15130          * @param {String} text The text to measure
15131          * @return {Number} height The height in pixels
15132          */
15133         getHeight : function(text){
15134             return this.getSize(text).height;
15135         }
15136     };
15137
15138     instance.bind(bindTo);
15139
15140     return instance;
15141 };
15142
15143 // backwards compat
15144 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15145  * Based on:
15146  * Ext JS Library 1.1.1
15147  * Copyright(c) 2006-2007, Ext JS, LLC.
15148  *
15149  * Originally Released Under LGPL - original licence link has changed is not relivant.
15150  *
15151  * Fork - LGPL
15152  * <script type="text/javascript">
15153  */
15154
15155 /**
15156  * @class Roo.state.Provider
15157  * Abstract base class for state provider implementations. This class provides methods
15158  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15159  * Provider interface.
15160  */
15161 Roo.state.Provider = function(){
15162     /**
15163      * @event statechange
15164      * Fires when a state change occurs.
15165      * @param {Provider} this This state provider
15166      * @param {String} key The state key which was changed
15167      * @param {String} value The encoded value for the state
15168      */
15169     this.addEvents({
15170         "statechange": true
15171     });
15172     this.state = {};
15173     Roo.state.Provider.superclass.constructor.call(this);
15174 };
15175 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15176     /**
15177      * Returns the current value for a key
15178      * @param {String} name The key name
15179      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15180      * @return {Mixed} The state data
15181      */
15182     get : function(name, defaultValue){
15183         return typeof this.state[name] == "undefined" ?
15184             defaultValue : this.state[name];
15185     },
15186     
15187     /**
15188      * Clears a value from the state
15189      * @param {String} name The key name
15190      */
15191     clear : function(name){
15192         delete this.state[name];
15193         this.fireEvent("statechange", this, name, null);
15194     },
15195     
15196     /**
15197      * Sets the value for a key
15198      * @param {String} name The key name
15199      * @param {Mixed} value The value to set
15200      */
15201     set : function(name, value){
15202         this.state[name] = value;
15203         this.fireEvent("statechange", this, name, value);
15204     },
15205     
15206     /**
15207      * Decodes a string previously encoded with {@link #encodeValue}.
15208      * @param {String} value The value to decode
15209      * @return {Mixed} The decoded value
15210      */
15211     decodeValue : function(cookie){
15212         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15213         var matches = re.exec(unescape(cookie));
15214         if(!matches || !matches[1]) {
15215             return; // non state cookie
15216         }
15217         var type = matches[1];
15218         var v = matches[2];
15219         switch(type){
15220             case "n":
15221                 return parseFloat(v);
15222             case "d":
15223                 return new Date(Date.parse(v));
15224             case "b":
15225                 return (v == "1");
15226             case "a":
15227                 var all = [];
15228                 var values = v.split("^");
15229                 for(var i = 0, len = values.length; i < len; i++){
15230                     all.push(this.decodeValue(values[i]));
15231                 }
15232                 return all;
15233            case "o":
15234                 var all = {};
15235                 var values = v.split("^");
15236                 for(var i = 0, len = values.length; i < len; i++){
15237                     var kv = values[i].split("=");
15238                     all[kv[0]] = this.decodeValue(kv[1]);
15239                 }
15240                 return all;
15241            default:
15242                 return v;
15243         }
15244     },
15245     
15246     /**
15247      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15248      * @param {Mixed} value The value to encode
15249      * @return {String} The encoded value
15250      */
15251     encodeValue : function(v){
15252         var enc;
15253         if(typeof v == "number"){
15254             enc = "n:" + v;
15255         }else if(typeof v == "boolean"){
15256             enc = "b:" + (v ? "1" : "0");
15257         }else if(v instanceof Date){
15258             enc = "d:" + v.toGMTString();
15259         }else if(v instanceof Array){
15260             var flat = "";
15261             for(var i = 0, len = v.length; i < len; i++){
15262                 flat += this.encodeValue(v[i]);
15263                 if(i != len-1) {
15264                     flat += "^";
15265                 }
15266             }
15267             enc = "a:" + flat;
15268         }else if(typeof v == "object"){
15269             var flat = "";
15270             for(var key in v){
15271                 if(typeof v[key] != "function"){
15272                     flat += key + "=" + this.encodeValue(v[key]) + "^";
15273                 }
15274             }
15275             enc = "o:" + flat.substring(0, flat.length-1);
15276         }else{
15277             enc = "s:" + v;
15278         }
15279         return escape(enc);        
15280     }
15281 });
15282
15283 /*
15284  * Based on:
15285  * Ext JS Library 1.1.1
15286  * Copyright(c) 2006-2007, Ext JS, LLC.
15287  *
15288  * Originally Released Under LGPL - original licence link has changed is not relivant.
15289  *
15290  * Fork - LGPL
15291  * <script type="text/javascript">
15292  */
15293 /**
15294  * @class Roo.state.Manager
15295  * This is the global state manager. By default all components that are "state aware" check this class
15296  * for state information if you don't pass them a custom state provider. In order for this class
15297  * to be useful, it must be initialized with a provider when your application initializes.
15298  <pre><code>
15299 // in your initialization function
15300 init : function(){
15301    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
15302    ...
15303    // supposed you have a {@link Roo.BorderLayout}
15304    var layout = new Roo.BorderLayout(...);
15305    layout.restoreState();
15306    // or a {Roo.BasicDialog}
15307    var dialog = new Roo.BasicDialog(...);
15308    dialog.restoreState();
15309  </code></pre>
15310  * @singleton
15311  */
15312 Roo.state.Manager = function(){
15313     var provider = new Roo.state.Provider();
15314     
15315     return {
15316         /**
15317          * Configures the default state provider for your application
15318          * @param {Provider} stateProvider The state provider to set
15319          */
15320         setProvider : function(stateProvider){
15321             provider = stateProvider;
15322         },
15323         
15324         /**
15325          * Returns the current value for a key
15326          * @param {String} name The key name
15327          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
15328          * @return {Mixed} The state data
15329          */
15330         get : function(key, defaultValue){
15331             return provider.get(key, defaultValue);
15332         },
15333         
15334         /**
15335          * Sets the value for a key
15336          * @param {String} name The key name
15337          * @param {Mixed} value The state data
15338          */
15339          set : function(key, value){
15340             provider.set(key, value);
15341         },
15342         
15343         /**
15344          * Clears a value from the state
15345          * @param {String} name The key name
15346          */
15347         clear : function(key){
15348             provider.clear(key);
15349         },
15350         
15351         /**
15352          * Gets the currently configured state provider
15353          * @return {Provider} The state provider
15354          */
15355         getProvider : function(){
15356             return provider;
15357         }
15358     };
15359 }();
15360 /*
15361  * Based on:
15362  * Ext JS Library 1.1.1
15363  * Copyright(c) 2006-2007, Ext JS, LLC.
15364  *
15365  * Originally Released Under LGPL - original licence link has changed is not relivant.
15366  *
15367  * Fork - LGPL
15368  * <script type="text/javascript">
15369  */
15370 /**
15371  * @class Roo.state.CookieProvider
15372  * @extends Roo.state.Provider
15373  * The default Provider implementation which saves state via cookies.
15374  * <br />Usage:
15375  <pre><code>
15376    var cp = new Roo.state.CookieProvider({
15377        path: "/cgi-bin/",
15378        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
15379        domain: "roojs.com"
15380    })
15381    Roo.state.Manager.setProvider(cp);
15382  </code></pre>
15383  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
15384  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
15385  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
15386  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
15387  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
15388  * domain the page is running on including the 'www' like 'www.roojs.com')
15389  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
15390  * @constructor
15391  * Create a new CookieProvider
15392  * @param {Object} config The configuration object
15393  */
15394 Roo.state.CookieProvider = function(config){
15395     Roo.state.CookieProvider.superclass.constructor.call(this);
15396     this.path = "/";
15397     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
15398     this.domain = null;
15399     this.secure = false;
15400     Roo.apply(this, config);
15401     this.state = this.readCookies();
15402 };
15403
15404 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
15405     // private
15406     set : function(name, value){
15407         if(typeof value == "undefined" || value === null){
15408             this.clear(name);
15409             return;
15410         }
15411         this.setCookie(name, value);
15412         Roo.state.CookieProvider.superclass.set.call(this, name, value);
15413     },
15414
15415     // private
15416     clear : function(name){
15417         this.clearCookie(name);
15418         Roo.state.CookieProvider.superclass.clear.call(this, name);
15419     },
15420
15421     // private
15422     readCookies : function(){
15423         var cookies = {};
15424         var c = document.cookie + ";";
15425         var re = /\s?(.*?)=(.*?);/g;
15426         var matches;
15427         while((matches = re.exec(c)) != null){
15428             var name = matches[1];
15429             var value = matches[2];
15430             if(name && name.substring(0,3) == "ys-"){
15431                 cookies[name.substr(3)] = this.decodeValue(value);
15432             }
15433         }
15434         return cookies;
15435     },
15436
15437     // private
15438     setCookie : function(name, value){
15439         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
15440            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
15441            ((this.path == null) ? "" : ("; path=" + this.path)) +
15442            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15443            ((this.secure == true) ? "; secure" : "");
15444     },
15445
15446     // private
15447     clearCookie : function(name){
15448         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
15449            ((this.path == null) ? "" : ("; path=" + this.path)) +
15450            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
15451            ((this.secure == true) ? "; secure" : "");
15452     }
15453 });/*
15454  * Based on:
15455  * Ext JS Library 1.1.1
15456  * Copyright(c) 2006-2007, Ext JS, LLC.
15457  *
15458  * Originally Released Under LGPL - original licence link has changed is not relivant.
15459  *
15460  * Fork - LGPL
15461  * <script type="text/javascript">
15462  */
15463  
15464
15465 /**
15466  * @class Roo.ComponentMgr
15467  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
15468  * @singleton
15469  */
15470 Roo.ComponentMgr = function(){
15471     var all = new Roo.util.MixedCollection();
15472
15473     return {
15474         /**
15475          * Registers a component.
15476          * @param {Roo.Component} c The component
15477          */
15478         register : function(c){
15479             all.add(c);
15480         },
15481
15482         /**
15483          * Unregisters a component.
15484          * @param {Roo.Component} c The component
15485          */
15486         unregister : function(c){
15487             all.remove(c);
15488         },
15489
15490         /**
15491          * Returns a component by id
15492          * @param {String} id The component id
15493          */
15494         get : function(id){
15495             return all.get(id);
15496         },
15497
15498         /**
15499          * Registers a function that will be called when a specified component is added to ComponentMgr
15500          * @param {String} id The component id
15501          * @param {Funtction} fn The callback function
15502          * @param {Object} scope The scope of the callback
15503          */
15504         onAvailable : function(id, fn, scope){
15505             all.on("add", function(index, o){
15506                 if(o.id == id){
15507                     fn.call(scope || o, o);
15508                     all.un("add", fn, scope);
15509                 }
15510             });
15511         }
15512     };
15513 }();/*
15514  * Based on:
15515  * Ext JS Library 1.1.1
15516  * Copyright(c) 2006-2007, Ext JS, LLC.
15517  *
15518  * Originally Released Under LGPL - original licence link has changed is not relivant.
15519  *
15520  * Fork - LGPL
15521  * <script type="text/javascript">
15522  */
15523  
15524 /**
15525  * @class Roo.Component
15526  * @extends Roo.util.Observable
15527  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
15528  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
15529  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
15530  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
15531  * All visual components (widgets) that require rendering into a layout should subclass Component.
15532  * @constructor
15533  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
15534  * element and its id used as the component id.  If a string is passed, it is assumed to be the id of an existing element
15535  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
15536  */
15537 Roo.Component = function(config){
15538     config = config || {};
15539     if(config.tagName || config.dom || typeof config == "string"){ // element object
15540         config = {el: config, id: config.id || config};
15541     }
15542     this.initialConfig = config;
15543
15544     Roo.apply(this, config);
15545     this.addEvents({
15546         /**
15547          * @event disable
15548          * Fires after the component is disabled.
15549              * @param {Roo.Component} this
15550              */
15551         disable : true,
15552         /**
15553          * @event enable
15554          * Fires after the component is enabled.
15555              * @param {Roo.Component} this
15556              */
15557         enable : true,
15558         /**
15559          * @event beforeshow
15560          * Fires before the component is shown.  Return false to stop the show.
15561              * @param {Roo.Component} this
15562              */
15563         beforeshow : true,
15564         /**
15565          * @event show
15566          * Fires after the component is shown.
15567              * @param {Roo.Component} this
15568              */
15569         show : true,
15570         /**
15571          * @event beforehide
15572          * Fires before the component is hidden. Return false to stop the hide.
15573              * @param {Roo.Component} this
15574              */
15575         beforehide : true,
15576         /**
15577          * @event hide
15578          * Fires after the component is hidden.
15579              * @param {Roo.Component} this
15580              */
15581         hide : true,
15582         /**
15583          * @event beforerender
15584          * Fires before the component is rendered. Return false to stop the render.
15585              * @param {Roo.Component} this
15586              */
15587         beforerender : true,
15588         /**
15589          * @event render
15590          * Fires after the component is rendered.
15591              * @param {Roo.Component} this
15592              */
15593         render : true,
15594         /**
15595          * @event beforedestroy
15596          * Fires before the component is destroyed. Return false to stop the destroy.
15597              * @param {Roo.Component} this
15598              */
15599         beforedestroy : true,
15600         /**
15601          * @event destroy
15602          * Fires after the component is destroyed.
15603              * @param {Roo.Component} this
15604              */
15605         destroy : true
15606     });
15607     if(!this.id){
15608         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
15609     }
15610     Roo.ComponentMgr.register(this);
15611     Roo.Component.superclass.constructor.call(this);
15612     this.initComponent();
15613     if(this.renderTo){ // not supported by all components yet. use at your own risk!
15614         this.render(this.renderTo);
15615         delete this.renderTo;
15616     }
15617 };
15618
15619 /** @private */
15620 Roo.Component.AUTO_ID = 1000;
15621
15622 Roo.extend(Roo.Component, Roo.util.Observable, {
15623     /**
15624      * @scope Roo.Component.prototype
15625      * @type {Boolean}
15626      * true if this component is hidden. Read-only.
15627      */
15628     hidden : false,
15629     /**
15630      * @type {Boolean}
15631      * true if this component is disabled. Read-only.
15632      */
15633     disabled : false,
15634     /**
15635      * @type {Boolean}
15636      * true if this component has been rendered. Read-only.
15637      */
15638     rendered : false,
15639     
15640     /** @cfg {String} disableClass
15641      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
15642      */
15643     disabledClass : "x-item-disabled",
15644         /** @cfg {Boolean} allowDomMove
15645          * Whether the component can move the Dom node when rendering (defaults to true).
15646          */
15647     allowDomMove : true,
15648     /** @cfg {String} hideMode (display|visibility)
15649      * How this component should hidden. Supported values are
15650      * "visibility" (css visibility), "offsets" (negative offset position) and
15651      * "display" (css display) - defaults to "display".
15652      */
15653     hideMode: 'display',
15654
15655     /** @private */
15656     ctype : "Roo.Component",
15657
15658     /**
15659      * @cfg {String} actionMode 
15660      * which property holds the element that used for  hide() / show() / disable() / enable()
15661      * default is 'el' for forms you probably want to set this to fieldEl 
15662      */
15663     actionMode : "el",
15664
15665     /** @private */
15666     getActionEl : function(){
15667         return this[this.actionMode];
15668     },
15669
15670     initComponent : Roo.emptyFn,
15671     /**
15672      * If this is a lazy rendering component, render it to its container element.
15673      * @param {String/HTMLElement/Element} container (optional) The element this component should be rendered into. If it is being applied to existing markup, this should be left off.
15674      */
15675     render : function(container, position){
15676         
15677         if(this.rendered){
15678             return this;
15679         }
15680         
15681         if(this.fireEvent("beforerender", this) === false){
15682             return false;
15683         }
15684         
15685         if(!container && this.el){
15686             this.el = Roo.get(this.el);
15687             container = this.el.dom.parentNode;
15688             this.allowDomMove = false;
15689         }
15690         this.container = Roo.get(container);
15691         this.rendered = true;
15692         if(position !== undefined){
15693             if(typeof position == 'number'){
15694                 position = this.container.dom.childNodes[position];
15695             }else{
15696                 position = Roo.getDom(position);
15697             }
15698         }
15699         this.onRender(this.container, position || null);
15700         if(this.cls){
15701             this.el.addClass(this.cls);
15702             delete this.cls;
15703         }
15704         if(this.style){
15705             this.el.applyStyles(this.style);
15706             delete this.style;
15707         }
15708         this.fireEvent("render", this);
15709         this.afterRender(this.container);
15710         if(this.hidden){
15711             this.hide();
15712         }
15713         if(this.disabled){
15714             this.disable();
15715         }
15716
15717         return this;
15718         
15719     },
15720
15721     /** @private */
15722     // default function is not really useful
15723     onRender : function(ct, position){
15724         if(this.el){
15725             this.el = Roo.get(this.el);
15726             if(this.allowDomMove !== false){
15727                 ct.dom.insertBefore(this.el.dom, position);
15728             }
15729         }
15730     },
15731
15732     /** @private */
15733     getAutoCreate : function(){
15734         var cfg = typeof this.autoCreate == "object" ?
15735                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
15736         if(this.id && !cfg.id){
15737             cfg.id = this.id;
15738         }
15739         return cfg;
15740     },
15741
15742     /** @private */
15743     afterRender : Roo.emptyFn,
15744
15745     /**
15746      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
15747      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
15748      */
15749     destroy : function(){
15750         if(this.fireEvent("beforedestroy", this) !== false){
15751             this.purgeListeners();
15752             this.beforeDestroy();
15753             if(this.rendered){
15754                 this.el.removeAllListeners();
15755                 this.el.remove();
15756                 if(this.actionMode == "container"){
15757                     this.container.remove();
15758                 }
15759             }
15760             this.onDestroy();
15761             Roo.ComponentMgr.unregister(this);
15762             this.fireEvent("destroy", this);
15763         }
15764     },
15765
15766         /** @private */
15767     beforeDestroy : function(){
15768
15769     },
15770
15771         /** @private */
15772         onDestroy : function(){
15773
15774     },
15775
15776     /**
15777      * Returns the underlying {@link Roo.Element}.
15778      * @return {Roo.Element} The element
15779      */
15780     getEl : function(){
15781         return this.el;
15782     },
15783
15784     /**
15785      * Returns the id of this component.
15786      * @return {String}
15787      */
15788     getId : function(){
15789         return this.id;
15790     },
15791
15792     /**
15793      * Try to focus this component.
15794      * @param {Boolean} selectText True to also select the text in this component (if applicable)
15795      * @return {Roo.Component} this
15796      */
15797     focus : function(selectText){
15798         if(this.rendered){
15799             this.el.focus();
15800             if(selectText === true){
15801                 this.el.dom.select();
15802             }
15803         }
15804         return this;
15805     },
15806
15807     /** @private */
15808     blur : function(){
15809         if(this.rendered){
15810             this.el.blur();
15811         }
15812         return this;
15813     },
15814
15815     /**
15816      * Disable this component.
15817      * @return {Roo.Component} this
15818      */
15819     disable : function(){
15820         if(this.rendered){
15821             this.onDisable();
15822         }
15823         this.disabled = true;
15824         this.fireEvent("disable", this);
15825         return this;
15826     },
15827
15828         // private
15829     onDisable : function(){
15830         this.getActionEl().addClass(this.disabledClass);
15831         this.el.dom.disabled = true;
15832     },
15833
15834     /**
15835      * Enable this component.
15836      * @return {Roo.Component} this
15837      */
15838     enable : function(){
15839         if(this.rendered){
15840             this.onEnable();
15841         }
15842         this.disabled = false;
15843         this.fireEvent("enable", this);
15844         return this;
15845     },
15846
15847         // private
15848     onEnable : function(){
15849         this.getActionEl().removeClass(this.disabledClass);
15850         this.el.dom.disabled = false;
15851     },
15852
15853     /**
15854      * Convenience function for setting disabled/enabled by boolean.
15855      * @param {Boolean} disabled
15856      */
15857     setDisabled : function(disabled){
15858         this[disabled ? "disable" : "enable"]();
15859     },
15860
15861     /**
15862      * Show this component.
15863      * @return {Roo.Component} this
15864      */
15865     show: function(){
15866         if(this.fireEvent("beforeshow", this) !== false){
15867             this.hidden = false;
15868             if(this.rendered){
15869                 this.onShow();
15870             }
15871             this.fireEvent("show", this);
15872         }
15873         return this;
15874     },
15875
15876     // private
15877     onShow : function(){
15878         var ae = this.getActionEl();
15879         if(this.hideMode == 'visibility'){
15880             ae.dom.style.visibility = "visible";
15881         }else if(this.hideMode == 'offsets'){
15882             ae.removeClass('x-hidden');
15883         }else{
15884             ae.dom.style.display = "";
15885         }
15886     },
15887
15888     /**
15889      * Hide this component.
15890      * @return {Roo.Component} this
15891      */
15892     hide: function(){
15893         if(this.fireEvent("beforehide", this) !== false){
15894             this.hidden = true;
15895             if(this.rendered){
15896                 this.onHide();
15897             }
15898             this.fireEvent("hide", this);
15899         }
15900         return this;
15901     },
15902
15903     // private
15904     onHide : function(){
15905         var ae = this.getActionEl();
15906         if(this.hideMode == 'visibility'){
15907             ae.dom.style.visibility = "hidden";
15908         }else if(this.hideMode == 'offsets'){
15909             ae.addClass('x-hidden');
15910         }else{
15911             ae.dom.style.display = "none";
15912         }
15913     },
15914
15915     /**
15916      * Convenience function to hide or show this component by boolean.
15917      * @param {Boolean} visible True to show, false to hide
15918      * @return {Roo.Component} this
15919      */
15920     setVisible: function(visible){
15921         if(visible) {
15922             this.show();
15923         }else{
15924             this.hide();
15925         }
15926         return this;
15927     },
15928
15929     /**
15930      * Returns true if this component is visible.
15931      */
15932     isVisible : function(){
15933         return this.getActionEl().isVisible();
15934     },
15935
15936     cloneConfig : function(overrides){
15937         overrides = overrides || {};
15938         var id = overrides.id || Roo.id();
15939         var cfg = Roo.applyIf(overrides, this.initialConfig);
15940         cfg.id = id; // prevent dup id
15941         return new this.constructor(cfg);
15942     }
15943 });/*
15944  * Based on:
15945  * Ext JS Library 1.1.1
15946  * Copyright(c) 2006-2007, Ext JS, LLC.
15947  *
15948  * Originally Released Under LGPL - original licence link has changed is not relivant.
15949  *
15950  * Fork - LGPL
15951  * <script type="text/javascript">
15952  */
15953
15954 /**
15955  * @class Roo.BoxComponent
15956  * @extends Roo.Component
15957  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
15958  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
15959  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
15960  * layout containers.
15961  * @constructor
15962  * @param {Roo.Element/String/Object} config The configuration options.
15963  */
15964 Roo.BoxComponent = function(config){
15965     Roo.Component.call(this, config);
15966     this.addEvents({
15967         /**
15968          * @event resize
15969          * Fires after the component is resized.
15970              * @param {Roo.Component} this
15971              * @param {Number} adjWidth The box-adjusted width that was set
15972              * @param {Number} adjHeight The box-adjusted height that was set
15973              * @param {Number} rawWidth The width that was originally specified
15974              * @param {Number} rawHeight The height that was originally specified
15975              */
15976         resize : true,
15977         /**
15978          * @event move
15979          * Fires after the component is moved.
15980              * @param {Roo.Component} this
15981              * @param {Number} x The new x position
15982              * @param {Number} y The new y position
15983              */
15984         move : true
15985     });
15986 };
15987
15988 Roo.extend(Roo.BoxComponent, Roo.Component, {
15989     // private, set in afterRender to signify that the component has been rendered
15990     boxReady : false,
15991     // private, used to defer height settings to subclasses
15992     deferHeight: false,
15993     /** @cfg {Number} width
15994      * width (optional) size of component
15995      */
15996      /** @cfg {Number} height
15997      * height (optional) size of component
15998      */
15999      
16000     /**
16001      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16002      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16003      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16004      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16005      * @return {Roo.BoxComponent} this
16006      */
16007     setSize : function(w, h){
16008         // support for standard size objects
16009         if(typeof w == 'object'){
16010             h = w.height;
16011             w = w.width;
16012         }
16013         // not rendered
16014         if(!this.boxReady){
16015             this.width = w;
16016             this.height = h;
16017             return this;
16018         }
16019
16020         // prevent recalcs when not needed
16021         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16022             return this;
16023         }
16024         this.lastSize = {width: w, height: h};
16025
16026         var adj = this.adjustSize(w, h);
16027         var aw = adj.width, ah = adj.height;
16028         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16029             var rz = this.getResizeEl();
16030             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16031                 rz.setSize(aw, ah);
16032             }else if(!this.deferHeight && ah !== undefined){
16033                 rz.setHeight(ah);
16034             }else if(aw !== undefined){
16035                 rz.setWidth(aw);
16036             }
16037             this.onResize(aw, ah, w, h);
16038             this.fireEvent('resize', this, aw, ah, w, h);
16039         }
16040         return this;
16041     },
16042
16043     /**
16044      * Gets the current size of the component's underlying element.
16045      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16046      */
16047     getSize : function(){
16048         return this.el.getSize();
16049     },
16050
16051     /**
16052      * Gets the current XY position of the component's underlying element.
16053      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16054      * @return {Array} The XY position of the element (e.g., [100, 200])
16055      */
16056     getPosition : function(local){
16057         if(local === true){
16058             return [this.el.getLeft(true), this.el.getTop(true)];
16059         }
16060         return this.xy || this.el.getXY();
16061     },
16062
16063     /**
16064      * Gets the current box measurements of the component's underlying element.
16065      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16066      * @returns {Object} box An object in the format {x, y, width, height}
16067      */
16068     getBox : function(local){
16069         var s = this.el.getSize();
16070         if(local){
16071             s.x = this.el.getLeft(true);
16072             s.y = this.el.getTop(true);
16073         }else{
16074             var xy = this.xy || this.el.getXY();
16075             s.x = xy[0];
16076             s.y = xy[1];
16077         }
16078         return s;
16079     },
16080
16081     /**
16082      * Sets the current box measurements of the component's underlying element.
16083      * @param {Object} box An object in the format {x, y, width, height}
16084      * @returns {Roo.BoxComponent} this
16085      */
16086     updateBox : function(box){
16087         this.setSize(box.width, box.height);
16088         this.setPagePosition(box.x, box.y);
16089         return this;
16090     },
16091
16092     // protected
16093     getResizeEl : function(){
16094         return this.resizeEl || this.el;
16095     },
16096
16097     // protected
16098     getPositionEl : function(){
16099         return this.positionEl || this.el;
16100     },
16101
16102     /**
16103      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16104      * This method fires the move event.
16105      * @param {Number} left The new left
16106      * @param {Number} top The new top
16107      * @returns {Roo.BoxComponent} this
16108      */
16109     setPosition : function(x, y){
16110         this.x = x;
16111         this.y = y;
16112         if(!this.boxReady){
16113             return this;
16114         }
16115         var adj = this.adjustPosition(x, y);
16116         var ax = adj.x, ay = adj.y;
16117
16118         var el = this.getPositionEl();
16119         if(ax !== undefined || ay !== undefined){
16120             if(ax !== undefined && ay !== undefined){
16121                 el.setLeftTop(ax, ay);
16122             }else if(ax !== undefined){
16123                 el.setLeft(ax);
16124             }else if(ay !== undefined){
16125                 el.setTop(ay);
16126             }
16127             this.onPosition(ax, ay);
16128             this.fireEvent('move', this, ax, ay);
16129         }
16130         return this;
16131     },
16132
16133     /**
16134      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16135      * This method fires the move event.
16136      * @param {Number} x The new x position
16137      * @param {Number} y The new y position
16138      * @returns {Roo.BoxComponent} this
16139      */
16140     setPagePosition : function(x, y){
16141         this.pageX = x;
16142         this.pageY = y;
16143         if(!this.boxReady){
16144             return;
16145         }
16146         if(x === undefined || y === undefined){ // cannot translate undefined points
16147             return;
16148         }
16149         var p = this.el.translatePoints(x, y);
16150         this.setPosition(p.left, p.top);
16151         return this;
16152     },
16153
16154     // private
16155     onRender : function(ct, position){
16156         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16157         if(this.resizeEl){
16158             this.resizeEl = Roo.get(this.resizeEl);
16159         }
16160         if(this.positionEl){
16161             this.positionEl = Roo.get(this.positionEl);
16162         }
16163     },
16164
16165     // private
16166     afterRender : function(){
16167         Roo.BoxComponent.superclass.afterRender.call(this);
16168         this.boxReady = true;
16169         this.setSize(this.width, this.height);
16170         if(this.x || this.y){
16171             this.setPosition(this.x, this.y);
16172         }
16173         if(this.pageX || this.pageY){
16174             this.setPagePosition(this.pageX, this.pageY);
16175         }
16176     },
16177
16178     /**
16179      * Force the component's size to recalculate based on the underlying element's current height and width.
16180      * @returns {Roo.BoxComponent} this
16181      */
16182     syncSize : function(){
16183         delete this.lastSize;
16184         this.setSize(this.el.getWidth(), this.el.getHeight());
16185         return this;
16186     },
16187
16188     /**
16189      * Called after the component is resized, this method is empty by default but can be implemented by any
16190      * subclass that needs to perform custom logic after a resize occurs.
16191      * @param {Number} adjWidth The box-adjusted width that was set
16192      * @param {Number} adjHeight The box-adjusted height that was set
16193      * @param {Number} rawWidth The width that was originally specified
16194      * @param {Number} rawHeight The height that was originally specified
16195      */
16196     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16197
16198     },
16199
16200     /**
16201      * Called after the component is moved, this method is empty by default but can be implemented by any
16202      * subclass that needs to perform custom logic after a move occurs.
16203      * @param {Number} x The new x position
16204      * @param {Number} y The new y position
16205      */
16206     onPosition : function(x, y){
16207
16208     },
16209
16210     // private
16211     adjustSize : function(w, h){
16212         if(this.autoWidth){
16213             w = 'auto';
16214         }
16215         if(this.autoHeight){
16216             h = 'auto';
16217         }
16218         return {width : w, height: h};
16219     },
16220
16221     // private
16222     adjustPosition : function(x, y){
16223         return {x : x, y: y};
16224     }
16225 });/*
16226  * Based on:
16227  * Ext JS Library 1.1.1
16228  * Copyright(c) 2006-2007, Ext JS, LLC.
16229  *
16230  * Originally Released Under LGPL - original licence link has changed is not relivant.
16231  *
16232  * Fork - LGPL
16233  * <script type="text/javascript">
16234  */
16235  (function(){ 
16236 /**
16237  * @class Roo.Layer
16238  * @extends Roo.Element
16239  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16240  * automatic maintaining of shadow/shim positions.
16241  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16242  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16243  * you can pass a string with a CSS class name. False turns off the shadow.
16244  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16245  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16246  * @cfg {String} cls CSS class to add to the element
16247  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16248  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16249  * @constructor
16250  * @param {Object} config An object with config options.
16251  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16252  */
16253
16254 Roo.Layer = function(config, existingEl){
16255     config = config || {};
16256     var dh = Roo.DomHelper;
16257     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16258     if(existingEl){
16259         this.dom = Roo.getDom(existingEl);
16260     }
16261     if(!this.dom){
16262         var o = config.dh || {tag: "div", cls: "x-layer"};
16263         this.dom = dh.append(pel, o);
16264     }
16265     if(config.cls){
16266         this.addClass(config.cls);
16267     }
16268     this.constrain = config.constrain !== false;
16269     this.visibilityMode = Roo.Element.VISIBILITY;
16270     if(config.id){
16271         this.id = this.dom.id = config.id;
16272     }else{
16273         this.id = Roo.id(this.dom);
16274     }
16275     this.zindex = config.zindex || this.getZIndex();
16276     this.position("absolute", this.zindex);
16277     if(config.shadow){
16278         this.shadowOffset = config.shadowOffset || 4;
16279         this.shadow = new Roo.Shadow({
16280             offset : this.shadowOffset,
16281             mode : config.shadow
16282         });
16283     }else{
16284         this.shadowOffset = 0;
16285     }
16286     this.useShim = config.shim !== false && Roo.useShims;
16287     this.useDisplay = config.useDisplay;
16288     this.hide();
16289 };
16290
16291 var supr = Roo.Element.prototype;
16292
16293 // shims are shared among layer to keep from having 100 iframes
16294 var shims = [];
16295
16296 Roo.extend(Roo.Layer, Roo.Element, {
16297
16298     getZIndex : function(){
16299         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
16300     },
16301
16302     getShim : function(){
16303         if(!this.useShim){
16304             return null;
16305         }
16306         if(this.shim){
16307             return this.shim;
16308         }
16309         var shim = shims.shift();
16310         if(!shim){
16311             shim = this.createShim();
16312             shim.enableDisplayMode('block');
16313             shim.dom.style.display = 'none';
16314             shim.dom.style.visibility = 'visible';
16315         }
16316         var pn = this.dom.parentNode;
16317         if(shim.dom.parentNode != pn){
16318             pn.insertBefore(shim.dom, this.dom);
16319         }
16320         shim.setStyle('z-index', this.getZIndex()-2);
16321         this.shim = shim;
16322         return shim;
16323     },
16324
16325     hideShim : function(){
16326         if(this.shim){
16327             this.shim.setDisplayed(false);
16328             shims.push(this.shim);
16329             delete this.shim;
16330         }
16331     },
16332
16333     disableShadow : function(){
16334         if(this.shadow){
16335             this.shadowDisabled = true;
16336             this.shadow.hide();
16337             this.lastShadowOffset = this.shadowOffset;
16338             this.shadowOffset = 0;
16339         }
16340     },
16341
16342     enableShadow : function(show){
16343         if(this.shadow){
16344             this.shadowDisabled = false;
16345             this.shadowOffset = this.lastShadowOffset;
16346             delete this.lastShadowOffset;
16347             if(show){
16348                 this.sync(true);
16349             }
16350         }
16351     },
16352
16353     // private
16354     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
16355     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
16356     sync : function(doShow){
16357         var sw = this.shadow;
16358         if(!this.updating && this.isVisible() && (sw || this.useShim)){
16359             var sh = this.getShim();
16360
16361             var w = this.getWidth(),
16362                 h = this.getHeight();
16363
16364             var l = this.getLeft(true),
16365                 t = this.getTop(true);
16366
16367             if(sw && !this.shadowDisabled){
16368                 if(doShow && !sw.isVisible()){
16369                     sw.show(this);
16370                 }else{
16371                     sw.realign(l, t, w, h);
16372                 }
16373                 if(sh){
16374                     if(doShow){
16375                        sh.show();
16376                     }
16377                     // fit the shim behind the shadow, so it is shimmed too
16378                     var a = sw.adjusts, s = sh.dom.style;
16379                     s.left = (Math.min(l, l+a.l))+"px";
16380                     s.top = (Math.min(t, t+a.t))+"px";
16381                     s.width = (w+a.w)+"px";
16382                     s.height = (h+a.h)+"px";
16383                 }
16384             }else if(sh){
16385                 if(doShow){
16386                    sh.show();
16387                 }
16388                 sh.setSize(w, h);
16389                 sh.setLeftTop(l, t);
16390             }
16391             
16392         }
16393     },
16394
16395     // private
16396     destroy : function(){
16397         this.hideShim();
16398         if(this.shadow){
16399             this.shadow.hide();
16400         }
16401         this.removeAllListeners();
16402         var pn = this.dom.parentNode;
16403         if(pn){
16404             pn.removeChild(this.dom);
16405         }
16406         Roo.Element.uncache(this.id);
16407     },
16408
16409     remove : function(){
16410         this.destroy();
16411     },
16412
16413     // private
16414     beginUpdate : function(){
16415         this.updating = true;
16416     },
16417
16418     // private
16419     endUpdate : function(){
16420         this.updating = false;
16421         this.sync(true);
16422     },
16423
16424     // private
16425     hideUnders : function(negOffset){
16426         if(this.shadow){
16427             this.shadow.hide();
16428         }
16429         this.hideShim();
16430     },
16431
16432     // private
16433     constrainXY : function(){
16434         if(this.constrain){
16435             var vw = Roo.lib.Dom.getViewWidth(),
16436                 vh = Roo.lib.Dom.getViewHeight();
16437             var s = Roo.get(document).getScroll();
16438
16439             var xy = this.getXY();
16440             var x = xy[0], y = xy[1];   
16441             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
16442             // only move it if it needs it
16443             var moved = false;
16444             // first validate right/bottom
16445             if((x + w) > vw+s.left){
16446                 x = vw - w - this.shadowOffset;
16447                 moved = true;
16448             }
16449             if((y + h) > vh+s.top){
16450                 y = vh - h - this.shadowOffset;
16451                 moved = true;
16452             }
16453             // then make sure top/left isn't negative
16454             if(x < s.left){
16455                 x = s.left;
16456                 moved = true;
16457             }
16458             if(y < s.top){
16459                 y = s.top;
16460                 moved = true;
16461             }
16462             if(moved){
16463                 if(this.avoidY){
16464                     var ay = this.avoidY;
16465                     if(y <= ay && (y+h) >= ay){
16466                         y = ay-h-5;   
16467                     }
16468                 }
16469                 xy = [x, y];
16470                 this.storeXY(xy);
16471                 supr.setXY.call(this, xy);
16472                 this.sync();
16473             }
16474         }
16475     },
16476
16477     isVisible : function(){
16478         return this.visible;    
16479     },
16480
16481     // private
16482     showAction : function(){
16483         this.visible = true; // track visibility to prevent getStyle calls
16484         if(this.useDisplay === true){
16485             this.setDisplayed("");
16486         }else if(this.lastXY){
16487             supr.setXY.call(this, this.lastXY);
16488         }else if(this.lastLT){
16489             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
16490         }
16491     },
16492
16493     // private
16494     hideAction : function(){
16495         this.visible = false;
16496         if(this.useDisplay === true){
16497             this.setDisplayed(false);
16498         }else{
16499             this.setLeftTop(-10000,-10000);
16500         }
16501     },
16502
16503     // overridden Element method
16504     setVisible : function(v, a, d, c, e){
16505         if(v){
16506             this.showAction();
16507         }
16508         if(a && v){
16509             var cb = function(){
16510                 this.sync(true);
16511                 if(c){
16512                     c();
16513                 }
16514             }.createDelegate(this);
16515             supr.setVisible.call(this, true, true, d, cb, e);
16516         }else{
16517             if(!v){
16518                 this.hideUnders(true);
16519             }
16520             var cb = c;
16521             if(a){
16522                 cb = function(){
16523                     this.hideAction();
16524                     if(c){
16525                         c();
16526                     }
16527                 }.createDelegate(this);
16528             }
16529             supr.setVisible.call(this, v, a, d, cb, e);
16530             if(v){
16531                 this.sync(true);
16532             }else if(!a){
16533                 this.hideAction();
16534             }
16535         }
16536     },
16537
16538     storeXY : function(xy){
16539         delete this.lastLT;
16540         this.lastXY = xy;
16541     },
16542
16543     storeLeftTop : function(left, top){
16544         delete this.lastXY;
16545         this.lastLT = [left, top];
16546     },
16547
16548     // private
16549     beforeFx : function(){
16550         this.beforeAction();
16551         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
16552     },
16553
16554     // private
16555     afterFx : function(){
16556         Roo.Layer.superclass.afterFx.apply(this, arguments);
16557         this.sync(this.isVisible());
16558     },
16559
16560     // private
16561     beforeAction : function(){
16562         if(!this.updating && this.shadow){
16563             this.shadow.hide();
16564         }
16565     },
16566
16567     // overridden Element method
16568     setLeft : function(left){
16569         this.storeLeftTop(left, this.getTop(true));
16570         supr.setLeft.apply(this, arguments);
16571         this.sync();
16572     },
16573
16574     setTop : function(top){
16575         this.storeLeftTop(this.getLeft(true), top);
16576         supr.setTop.apply(this, arguments);
16577         this.sync();
16578     },
16579
16580     setLeftTop : function(left, top){
16581         this.storeLeftTop(left, top);
16582         supr.setLeftTop.apply(this, arguments);
16583         this.sync();
16584     },
16585
16586     setXY : function(xy, a, d, c, e){
16587         this.fixDisplay();
16588         this.beforeAction();
16589         this.storeXY(xy);
16590         var cb = this.createCB(c);
16591         supr.setXY.call(this, xy, a, d, cb, e);
16592         if(!a){
16593             cb();
16594         }
16595     },
16596
16597     // private
16598     createCB : function(c){
16599         var el = this;
16600         return function(){
16601             el.constrainXY();
16602             el.sync(true);
16603             if(c){
16604                 c();
16605             }
16606         };
16607     },
16608
16609     // overridden Element method
16610     setX : function(x, a, d, c, e){
16611         this.setXY([x, this.getY()], a, d, c, e);
16612     },
16613
16614     // overridden Element method
16615     setY : function(y, a, d, c, e){
16616         this.setXY([this.getX(), y], a, d, c, e);
16617     },
16618
16619     // overridden Element method
16620     setSize : function(w, h, a, d, c, e){
16621         this.beforeAction();
16622         var cb = this.createCB(c);
16623         supr.setSize.call(this, w, h, a, d, cb, e);
16624         if(!a){
16625             cb();
16626         }
16627     },
16628
16629     // overridden Element method
16630     setWidth : function(w, a, d, c, e){
16631         this.beforeAction();
16632         var cb = this.createCB(c);
16633         supr.setWidth.call(this, w, a, d, cb, e);
16634         if(!a){
16635             cb();
16636         }
16637     },
16638
16639     // overridden Element method
16640     setHeight : function(h, a, d, c, e){
16641         this.beforeAction();
16642         var cb = this.createCB(c);
16643         supr.setHeight.call(this, h, a, d, cb, e);
16644         if(!a){
16645             cb();
16646         }
16647     },
16648
16649     // overridden Element method
16650     setBounds : function(x, y, w, h, a, d, c, e){
16651         this.beforeAction();
16652         var cb = this.createCB(c);
16653         if(!a){
16654             this.storeXY([x, y]);
16655             supr.setXY.call(this, [x, y]);
16656             supr.setSize.call(this, w, h, a, d, cb, e);
16657             cb();
16658         }else{
16659             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
16660         }
16661         return this;
16662     },
16663     
16664     /**
16665      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
16666      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
16667      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
16668      * @param {Number} zindex The new z-index to set
16669      * @return {this} The Layer
16670      */
16671     setZIndex : function(zindex){
16672         this.zindex = zindex;
16673         this.setStyle("z-index", zindex + 2);
16674         if(this.shadow){
16675             this.shadow.setZIndex(zindex + 1);
16676         }
16677         if(this.shim){
16678             this.shim.setStyle("z-index", zindex);
16679         }
16680     }
16681 });
16682 })();/*
16683  * Original code for Roojs - LGPL
16684  * <script type="text/javascript">
16685  */
16686  
16687 /**
16688  * @class Roo.XComponent
16689  * A delayed Element creator...
16690  * Or a way to group chunks of interface together.
16691  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
16692  *  used in conjunction with XComponent.build() it will create an instance of each element,
16693  *  then call addxtype() to build the User interface.
16694  * 
16695  * Mypart.xyx = new Roo.XComponent({
16696
16697     parent : 'Mypart.xyz', // empty == document.element.!!
16698     order : '001',
16699     name : 'xxxx'
16700     region : 'xxxx'
16701     disabled : function() {} 
16702      
16703     tree : function() { // return an tree of xtype declared components
16704         var MODULE = this;
16705         return 
16706         {
16707             xtype : 'NestedLayoutPanel',
16708             // technicall
16709         }
16710      ]
16711  *})
16712  *
16713  *
16714  * It can be used to build a big heiracy, with parent etc.
16715  * or you can just use this to render a single compoent to a dom element
16716  * MYPART.render(Roo.Element | String(id) | dom_element )
16717  *
16718  *
16719  * Usage patterns.
16720  *
16721  * Classic Roo
16722  *
16723  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
16724  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
16725  *
16726  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
16727  *
16728  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
16729  * - if mulitple topModules exist, the last one is defined as the top module.
16730  *
16731  * Embeded Roo
16732  * 
16733  * When the top level or multiple modules are to embedded into a existing HTML page,
16734  * the parent element can container '#id' of the element where the module will be drawn.
16735  *
16736  * Bootstrap Roo
16737  *
16738  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
16739  * it relies more on a include mechanism, where sub modules are included into an outer page.
16740  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
16741  * 
16742  * Bootstrap Roo Included elements
16743  *
16744  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
16745  * hence confusing the component builder as it thinks there are multiple top level elements. 
16746  *
16747  * String Over-ride & Translations
16748  *
16749  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
16750  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
16751  * are needed. @see Roo.XComponent.overlayString  
16752  * 
16753  * 
16754  * 
16755  * @extends Roo.util.Observable
16756  * @constructor
16757  * @param cfg {Object} configuration of component
16758  * 
16759  */
16760 Roo.XComponent = function(cfg) {
16761     Roo.apply(this, cfg);
16762     this.addEvents({ 
16763         /**
16764              * @event built
16765              * Fires when this the componnt is built
16766              * @param {Roo.XComponent} c the component
16767              */
16768         'built' : true
16769         
16770     });
16771     this.region = this.region || 'center'; // default..
16772     Roo.XComponent.register(this);
16773     this.modules = false;
16774     this.el = false; // where the layout goes..
16775     
16776     
16777 }
16778 Roo.extend(Roo.XComponent, Roo.util.Observable, {
16779     /**
16780      * @property el
16781      * The created element (with Roo.factory())
16782      * @type {Roo.Layout}
16783      */
16784     el  : false,
16785     
16786     /**
16787      * @property el
16788      * for BC  - use el in new code
16789      * @type {Roo.Layout}
16790      */
16791     panel : false,
16792     
16793     /**
16794      * @property layout
16795      * for BC  - use el in new code
16796      * @type {Roo.Layout}
16797      */
16798     layout : false,
16799     
16800      /**
16801      * @cfg {Function|boolean} disabled
16802      * If this module is disabled by some rule, return true from the funtion
16803      */
16804     disabled : false,
16805     
16806     /**
16807      * @cfg {String} parent 
16808      * Name of parent element which it get xtype added to..
16809      */
16810     parent: false,
16811     
16812     /**
16813      * @cfg {String} order
16814      * Used to set the order in which elements are created (usefull for multiple tabs)
16815      */
16816     
16817     order : false,
16818     /**
16819      * @cfg {String} name
16820      * String to display while loading.
16821      */
16822     name : false,
16823     /**
16824      * @cfg {String} region
16825      * Region to render component to (defaults to center)
16826      */
16827     region : 'center',
16828     
16829     /**
16830      * @cfg {Array} items
16831      * A single item array - the first element is the root of the tree..
16832      * It's done this way to stay compatible with the Xtype system...
16833      */
16834     items : false,
16835     
16836     /**
16837      * @property _tree
16838      * The method that retuns the tree of parts that make up this compoennt 
16839      * @type {function}
16840      */
16841     _tree  : false,
16842     
16843      /**
16844      * render
16845      * render element to dom or tree
16846      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
16847      */
16848     
16849     render : function(el)
16850     {
16851         
16852         el = el || false;
16853         var hp = this.parent ? 1 : 0;
16854         Roo.debug &&  Roo.log(this);
16855         
16856         var tree = this._tree ? this._tree() : this.tree();
16857
16858         
16859         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
16860             // if parent is a '#.....' string, then let's use that..
16861             var ename = this.parent.substr(1);
16862             this.parent = false;
16863             Roo.debug && Roo.log(ename);
16864             switch (ename) {
16865                 case 'bootstrap-body':
16866                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
16867                         // this is the BorderLayout standard?
16868                        this.parent = { el : true };
16869                        break;
16870                     }
16871                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
16872                         // need to insert stuff...
16873                         this.parent =  {
16874                              el : new Roo.bootstrap.layout.Border({
16875                                  el : document.body, 
16876                      
16877                                  center: {
16878                                     titlebar: false,
16879                                     autoScroll:false,
16880                                     closeOnTab: true,
16881                                     tabPosition: 'top',
16882                                       //resizeTabs: true,
16883                                     alwaysShowTabs: true,
16884                                     hideTabs: false
16885                                      //minTabWidth: 140
16886                                  }
16887                              })
16888                         
16889                          };
16890                          break;
16891                     }
16892                          
16893                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
16894                         this.parent = { el :  new  Roo.bootstrap.Body() };
16895                         Roo.debug && Roo.log("setting el to doc body");
16896                          
16897                     } else {
16898                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
16899                     }
16900                     break;
16901                 case 'bootstrap':
16902                     this.parent = { el : true};
16903                     // fall through
16904                 default:
16905                     el = Roo.get(ename);
16906                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
16907                         this.parent = { el : true};
16908                     }
16909                     
16910                     break;
16911             }
16912                 
16913             
16914             if (!el && !this.parent) {
16915                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
16916                 return;
16917             }
16918         }
16919         
16920         Roo.debug && Roo.log("EL:");
16921         Roo.debug && Roo.log(el);
16922         Roo.debug && Roo.log("this.parent.el:");
16923         Roo.debug && Roo.log(this.parent.el);
16924         
16925
16926         // altertive root elements ??? - we need a better way to indicate these.
16927         var is_alt = Roo.XComponent.is_alt ||
16928                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
16929                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
16930                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
16931         
16932         
16933         
16934         if (!this.parent && is_alt) {
16935             //el = Roo.get(document.body);
16936             this.parent = { el : true };
16937         }
16938             
16939             
16940         
16941         if (!this.parent) {
16942             
16943             Roo.debug && Roo.log("no parent - creating one");
16944             
16945             el = el ? Roo.get(el) : false;      
16946             
16947             if (typeof(Roo.BorderLayout) == 'undefined' ) {
16948                 
16949                 this.parent =  {
16950                     el : new Roo.bootstrap.layout.Border({
16951                         el: el || document.body,
16952                     
16953                         center: {
16954                             titlebar: false,
16955                             autoScroll:false,
16956                             closeOnTab: true,
16957                             tabPosition: 'top',
16958                              //resizeTabs: true,
16959                             alwaysShowTabs: false,
16960                             hideTabs: true,
16961                             minTabWidth: 140,
16962                             overflow: 'visible'
16963                          }
16964                      })
16965                 };
16966             } else {
16967             
16968                 // it's a top level one..
16969                 this.parent =  {
16970                     el : new Roo.BorderLayout(el || document.body, {
16971                         center: {
16972                             titlebar: false,
16973                             autoScroll:false,
16974                             closeOnTab: true,
16975                             tabPosition: 'top',
16976                              //resizeTabs: true,
16977                             alwaysShowTabs: el && hp? false :  true,
16978                             hideTabs: el || !hp ? true :  false,
16979                             minTabWidth: 140
16980                          }
16981                     })
16982                 };
16983             }
16984         }
16985         
16986         if (!this.parent.el) {
16987                 // probably an old style ctor, which has been disabled.
16988                 return;
16989
16990         }
16991                 // The 'tree' method is  '_tree now' 
16992             
16993         tree.region = tree.region || this.region;
16994         var is_body = false;
16995         if (this.parent.el === true) {
16996             // bootstrap... - body..
16997             if (el) {
16998                 tree.el = el;
16999             }
17000             this.parent.el = Roo.factory(tree);
17001             is_body = true;
17002         }
17003         
17004         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17005         this.fireEvent('built', this);
17006         
17007         this.panel = this.el;
17008         this.layout = this.panel.layout;
17009         this.parentLayout = this.parent.layout  || false;  
17010          
17011     }
17012     
17013 });
17014
17015 Roo.apply(Roo.XComponent, {
17016     /**
17017      * @property  hideProgress
17018      * true to disable the building progress bar.. usefull on single page renders.
17019      * @type Boolean
17020      */
17021     hideProgress : false,
17022     /**
17023      * @property  buildCompleted
17024      * True when the builder has completed building the interface.
17025      * @type Boolean
17026      */
17027     buildCompleted : false,
17028      
17029     /**
17030      * @property  topModule
17031      * the upper most module - uses document.element as it's constructor.
17032      * @type Object
17033      */
17034      
17035     topModule  : false,
17036       
17037     /**
17038      * @property  modules
17039      * array of modules to be created by registration system.
17040      * @type {Array} of Roo.XComponent
17041      */
17042     
17043     modules : [],
17044     /**
17045      * @property  elmodules
17046      * array of modules to be created by which use #ID 
17047      * @type {Array} of Roo.XComponent
17048      */
17049      
17050     elmodules : [],
17051
17052      /**
17053      * @property  is_alt
17054      * Is an alternative Root - normally used by bootstrap or other systems,
17055      *    where the top element in the tree can wrap 'body' 
17056      * @type {boolean}  (default false)
17057      */
17058      
17059     is_alt : false,
17060     /**
17061      * @property  build_from_html
17062      * Build elements from html - used by bootstrap HTML stuff 
17063      *    - this is cleared after build is completed
17064      * @type {boolean}    (default false)
17065      */
17066      
17067     build_from_html : false,
17068     /**
17069      * Register components to be built later.
17070      *
17071      * This solves the following issues
17072      * - Building is not done on page load, but after an authentication process has occured.
17073      * - Interface elements are registered on page load
17074      * - Parent Interface elements may not be loaded before child, so this handles that..
17075      * 
17076      *
17077      * example:
17078      * 
17079      * MyApp.register({
17080           order : '000001',
17081           module : 'Pman.Tab.projectMgr',
17082           region : 'center',
17083           parent : 'Pman.layout',
17084           disabled : false,  // or use a function..
17085         })
17086      
17087      * * @param {Object} details about module
17088      */
17089     register : function(obj) {
17090                 
17091         Roo.XComponent.event.fireEvent('register', obj);
17092         switch(typeof(obj.disabled) ) {
17093                 
17094             case 'undefined':
17095                 break;
17096             
17097             case 'function':
17098                 if ( obj.disabled() ) {
17099                         return;
17100                 }
17101                 break;
17102             
17103             default:
17104                 if (obj.disabled || obj.region == '#disabled') {
17105                         return;
17106                 }
17107                 break;
17108         }
17109                 
17110         this.modules.push(obj);
17111          
17112     },
17113     /**
17114      * convert a string to an object..
17115      * eg. 'AAA.BBB' -> finds AAA.BBB
17116
17117      */
17118     
17119     toObject : function(str)
17120     {
17121         if (!str || typeof(str) == 'object') {
17122             return str;
17123         }
17124         if (str.substring(0,1) == '#') {
17125             return str;
17126         }
17127
17128         var ar = str.split('.');
17129         var rt, o;
17130         rt = ar.shift();
17131             /** eval:var:o */
17132         try {
17133             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17134         } catch (e) {
17135             throw "Module not found : " + str;
17136         }
17137         
17138         if (o === false) {
17139             throw "Module not found : " + str;
17140         }
17141         Roo.each(ar, function(e) {
17142             if (typeof(o[e]) == 'undefined') {
17143                 throw "Module not found : " + str;
17144             }
17145             o = o[e];
17146         });
17147         
17148         return o;
17149         
17150     },
17151     
17152     
17153     /**
17154      * move modules into their correct place in the tree..
17155      * 
17156      */
17157     preBuild : function ()
17158     {
17159         var _t = this;
17160         Roo.each(this.modules , function (obj)
17161         {
17162             Roo.XComponent.event.fireEvent('beforebuild', obj);
17163             
17164             var opar = obj.parent;
17165             try { 
17166                 obj.parent = this.toObject(opar);
17167             } catch(e) {
17168                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17169                 return;
17170             }
17171             
17172             if (!obj.parent) {
17173                 Roo.debug && Roo.log("GOT top level module");
17174                 Roo.debug && Roo.log(obj);
17175                 obj.modules = new Roo.util.MixedCollection(false, 
17176                     function(o) { return o.order + '' }
17177                 );
17178                 this.topModule = obj;
17179                 return;
17180             }
17181                         // parent is a string (usually a dom element name..)
17182             if (typeof(obj.parent) == 'string') {
17183                 this.elmodules.push(obj);
17184                 return;
17185             }
17186             if (obj.parent.constructor != Roo.XComponent) {
17187                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17188             }
17189             if (!obj.parent.modules) {
17190                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17191                     function(o) { return o.order + '' }
17192                 );
17193             }
17194             if (obj.parent.disabled) {
17195                 obj.disabled = true;
17196             }
17197             obj.parent.modules.add(obj);
17198         }, this);
17199     },
17200     
17201      /**
17202      * make a list of modules to build.
17203      * @return {Array} list of modules. 
17204      */ 
17205     
17206     buildOrder : function()
17207     {
17208         var _this = this;
17209         var cmp = function(a,b) {   
17210             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17211         };
17212         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17213             throw "No top level modules to build";
17214         }
17215         
17216         // make a flat list in order of modules to build.
17217         var mods = this.topModule ? [ this.topModule ] : [];
17218                 
17219         
17220         // elmodules (is a list of DOM based modules )
17221         Roo.each(this.elmodules, function(e) {
17222             mods.push(e);
17223             if (!this.topModule &&
17224                 typeof(e.parent) == 'string' &&
17225                 e.parent.substring(0,1) == '#' &&
17226                 Roo.get(e.parent.substr(1))
17227                ) {
17228                 
17229                 _this.topModule = e;
17230             }
17231             
17232         });
17233
17234         
17235         // add modules to their parents..
17236         var addMod = function(m) {
17237             Roo.debug && Roo.log("build Order: add: " + m.name);
17238                 
17239             mods.push(m);
17240             if (m.modules && !m.disabled) {
17241                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17242                 m.modules.keySort('ASC',  cmp );
17243                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17244     
17245                 m.modules.each(addMod);
17246             } else {
17247                 Roo.debug && Roo.log("build Order: no child modules");
17248             }
17249             // not sure if this is used any more..
17250             if (m.finalize) {
17251                 m.finalize.name = m.name + " (clean up) ";
17252                 mods.push(m.finalize);
17253             }
17254             
17255         }
17256         if (this.topModule && this.topModule.modules) { 
17257             this.topModule.modules.keySort('ASC',  cmp );
17258             this.topModule.modules.each(addMod);
17259         } 
17260         return mods;
17261     },
17262     
17263      /**
17264      * Build the registered modules.
17265      * @param {Object} parent element.
17266      * @param {Function} optional method to call after module has been added.
17267      * 
17268      */ 
17269    
17270     build : function(opts) 
17271     {
17272         
17273         if (typeof(opts) != 'undefined') {
17274             Roo.apply(this,opts);
17275         }
17276         
17277         this.preBuild();
17278         var mods = this.buildOrder();
17279       
17280         //this.allmods = mods;
17281         //Roo.debug && Roo.log(mods);
17282         //return;
17283         if (!mods.length) { // should not happen
17284             throw "NO modules!!!";
17285         }
17286         
17287         
17288         var msg = "Building Interface...";
17289         // flash it up as modal - so we store the mask!?
17290         if (!this.hideProgress && Roo.MessageBox) {
17291             Roo.MessageBox.show({ title: 'loading' });
17292             Roo.MessageBox.show({
17293                title: "Please wait...",
17294                msg: msg,
17295                width:450,
17296                progress:true,
17297                buttons : false,
17298                closable:false,
17299                modal: false
17300               
17301             });
17302         }
17303         var total = mods.length;
17304         
17305         var _this = this;
17306         var progressRun = function() {
17307             if (!mods.length) {
17308                 Roo.debug && Roo.log('hide?');
17309                 if (!this.hideProgress && Roo.MessageBox) {
17310                     Roo.MessageBox.hide();
17311                 }
17312                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
17313                 
17314                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
17315                 
17316                 // THE END...
17317                 return false;   
17318             }
17319             
17320             var m = mods.shift();
17321             
17322             
17323             Roo.debug && Roo.log(m);
17324             // not sure if this is supported any more.. - modules that are are just function
17325             if (typeof(m) == 'function') { 
17326                 m.call(this);
17327                 return progressRun.defer(10, _this);
17328             } 
17329             
17330             
17331             msg = "Building Interface " + (total  - mods.length) + 
17332                     " of " + total + 
17333                     (m.name ? (' - ' + m.name) : '');
17334                         Roo.debug && Roo.log(msg);
17335             if (!_this.hideProgress &&  Roo.MessageBox) { 
17336                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
17337             }
17338             
17339          
17340             // is the module disabled?
17341             var disabled = (typeof(m.disabled) == 'function') ?
17342                 m.disabled.call(m.module.disabled) : m.disabled;    
17343             
17344             
17345             if (disabled) {
17346                 return progressRun(); // we do not update the display!
17347             }
17348             
17349             // now build 
17350             
17351                         
17352                         
17353             m.render();
17354             // it's 10 on top level, and 1 on others??? why...
17355             return progressRun.defer(10, _this);
17356              
17357         }
17358         progressRun.defer(1, _this);
17359      
17360         
17361         
17362     },
17363     /**
17364      * Overlay a set of modified strings onto a component
17365      * This is dependant on our builder exporting the strings and 'named strings' elements.
17366      * 
17367      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
17368      * @param {Object} associative array of 'named' string and it's new value.
17369      * 
17370      */
17371         overlayStrings : function( component, strings )
17372     {
17373         if (typeof(component['_named_strings']) == 'undefined') {
17374             throw "ERROR: component does not have _named_strings";
17375         }
17376         for ( var k in strings ) {
17377             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
17378             if (md !== false) {
17379                 component['_strings'][md] = strings[k];
17380             } else {
17381                 Roo.log('could not find named string: ' + k + ' in');
17382                 Roo.log(component);
17383             }
17384             
17385         }
17386         
17387     },
17388     
17389         
17390         /**
17391          * Event Object.
17392          *
17393          *
17394          */
17395         event: false, 
17396     /**
17397          * wrapper for event.on - aliased later..  
17398          * Typically use to register a event handler for register:
17399          *
17400          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
17401          *
17402          */
17403     on : false
17404    
17405     
17406     
17407 });
17408
17409 Roo.XComponent.event = new Roo.util.Observable({
17410                 events : { 
17411                         /**
17412                          * @event register
17413                          * Fires when an Component is registered,
17414                          * set the disable property on the Component to stop registration.
17415                          * @param {Roo.XComponent} c the component being registerd.
17416                          * 
17417                          */
17418                         'register' : true,
17419             /**
17420                          * @event beforebuild
17421                          * Fires before each Component is built
17422                          * can be used to apply permissions.
17423                          * @param {Roo.XComponent} c the component being registerd.
17424                          * 
17425                          */
17426                         'beforebuild' : true,
17427                         /**
17428                          * @event buildcomplete
17429                          * Fires on the top level element when all elements have been built
17430                          * @param {Roo.XComponent} the top level component.
17431                          */
17432                         'buildcomplete' : true
17433                         
17434                 }
17435 });
17436
17437 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
17438  //
17439  /**
17440  * marked - a markdown parser
17441  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
17442  * https://github.com/chjj/marked
17443  */
17444
17445
17446 /**
17447  *
17448  * Roo.Markdown - is a very crude wrapper around marked..
17449  *
17450  * usage:
17451  * 
17452  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
17453  * 
17454  * Note: move the sample code to the bottom of this
17455  * file before uncommenting it.
17456  *
17457  */
17458
17459 Roo.Markdown = {};
17460 Roo.Markdown.toHtml = function(text) {
17461     
17462     var c = new Roo.Markdown.marked.setOptions({
17463             renderer: new Roo.Markdown.marked.Renderer(),
17464             gfm: true,
17465             tables: true,
17466             breaks: false,
17467             pedantic: false,
17468             sanitize: false,
17469             smartLists: true,
17470             smartypants: false
17471           });
17472     // A FEW HACKS!!?
17473     
17474     text = text.replace(/\\\n/g,' ');
17475     return Roo.Markdown.marked(text);
17476 };
17477 //
17478 // converter
17479 //
17480 // Wraps all "globals" so that the only thing
17481 // exposed is makeHtml().
17482 //
17483 (function() {
17484     
17485      /**
17486          * eval:var:escape
17487          * eval:var:unescape
17488          * eval:var:replace
17489          */
17490       
17491     /**
17492      * Helpers
17493      */
17494     
17495     var escape = function (html, encode) {
17496       return html
17497         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
17498         .replace(/</g, '&lt;')
17499         .replace(/>/g, '&gt;')
17500         .replace(/"/g, '&quot;')
17501         .replace(/'/g, '&#39;');
17502     }
17503     
17504     var unescape = function (html) {
17505         // explicitly match decimal, hex, and named HTML entities 
17506       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
17507         n = n.toLowerCase();
17508         if (n === 'colon') { return ':'; }
17509         if (n.charAt(0) === '#') {
17510           return n.charAt(1) === 'x'
17511             ? String.fromCharCode(parseInt(n.substring(2), 16))
17512             : String.fromCharCode(+n.substring(1));
17513         }
17514         return '';
17515       });
17516     }
17517     
17518     var replace = function (regex, opt) {
17519       regex = regex.source;
17520       opt = opt || '';
17521       return function self(name, val) {
17522         if (!name) { return new RegExp(regex, opt); }
17523         val = val.source || val;
17524         val = val.replace(/(^|[^\[])\^/g, '$1');
17525         regex = regex.replace(name, val);
17526         return self;
17527       };
17528     }
17529
17530
17531          /**
17532          * eval:var:noop
17533     */
17534     var noop = function () {}
17535     noop.exec = noop;
17536     
17537          /**
17538          * eval:var:merge
17539     */
17540     var merge = function (obj) {
17541       var i = 1
17542         , target
17543         , key;
17544     
17545       for (; i < arguments.length; i++) {
17546         target = arguments[i];
17547         for (key in target) {
17548           if (Object.prototype.hasOwnProperty.call(target, key)) {
17549             obj[key] = target[key];
17550           }
17551         }
17552       }
17553     
17554       return obj;
17555     }
17556     
17557     
17558     /**
17559      * Block-Level Grammar
17560      */
17561     
17562     
17563     
17564     
17565     var block = {
17566       newline: /^\n+/,
17567       code: /^( {4}[^\n]+\n*)+/,
17568       fences: noop,
17569       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
17570       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
17571       nptable: noop,
17572       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
17573       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
17574       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
17575       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
17576       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
17577       table: noop,
17578       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
17579       text: /^[^\n]+/
17580     };
17581     
17582     block.bullet = /(?:[*+-]|\d+\.)/;
17583     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
17584     block.item = replace(block.item, 'gm')
17585       (/bull/g, block.bullet)
17586       ();
17587     
17588     block.list = replace(block.list)
17589       (/bull/g, block.bullet)
17590       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
17591       ('def', '\\n+(?=' + block.def.source + ')')
17592       ();
17593     
17594     block.blockquote = replace(block.blockquote)
17595       ('def', block.def)
17596       ();
17597     
17598     block._tag = '(?!(?:'
17599       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
17600       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
17601       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
17602     
17603     block.html = replace(block.html)
17604       ('comment', /<!--[\s\S]*?-->/)
17605       ('closed', /<(tag)[\s\S]+?<\/\1>/)
17606       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
17607       (/tag/g, block._tag)
17608       ();
17609     
17610     block.paragraph = replace(block.paragraph)
17611       ('hr', block.hr)
17612       ('heading', block.heading)
17613       ('lheading', block.lheading)
17614       ('blockquote', block.blockquote)
17615       ('tag', '<' + block._tag)
17616       ('def', block.def)
17617       ();
17618     
17619     /**
17620      * Normal Block Grammar
17621      */
17622     
17623     block.normal = merge({}, block);
17624     
17625     /**
17626      * GFM Block Grammar
17627      */
17628     
17629     block.gfm = merge({}, block.normal, {
17630       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
17631       paragraph: /^/,
17632       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
17633     });
17634     
17635     block.gfm.paragraph = replace(block.paragraph)
17636       ('(?!', '(?!'
17637         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
17638         + block.list.source.replace('\\1', '\\3') + '|')
17639       ();
17640     
17641     /**
17642      * GFM + Tables Block Grammar
17643      */
17644     
17645     block.tables = merge({}, block.gfm, {
17646       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
17647       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
17648     });
17649     
17650     /**
17651      * Block Lexer
17652      */
17653     
17654     var Lexer = function (options) {
17655       this.tokens = [];
17656       this.tokens.links = {};
17657       this.options = options || marked.defaults;
17658       this.rules = block.normal;
17659     
17660       if (this.options.gfm) {
17661         if (this.options.tables) {
17662           this.rules = block.tables;
17663         } else {
17664           this.rules = block.gfm;
17665         }
17666       }
17667     }
17668     
17669     /**
17670      * Expose Block Rules
17671      */
17672     
17673     Lexer.rules = block;
17674     
17675     /**
17676      * Static Lex Method
17677      */
17678     
17679     Lexer.lex = function(src, options) {
17680       var lexer = new Lexer(options);
17681       return lexer.lex(src);
17682     };
17683     
17684     /**
17685      * Preprocessing
17686      */
17687     
17688     Lexer.prototype.lex = function(src) {
17689       src = src
17690         .replace(/\r\n|\r/g, '\n')
17691         .replace(/\t/g, '    ')
17692         .replace(/\u00a0/g, ' ')
17693         .replace(/\u2424/g, '\n');
17694     
17695       return this.token(src, true);
17696     };
17697     
17698     /**
17699      * Lexing
17700      */
17701     
17702     Lexer.prototype.token = function(src, top, bq) {
17703       var src = src.replace(/^ +$/gm, '')
17704         , next
17705         , loose
17706         , cap
17707         , bull
17708         , b
17709         , item
17710         , space
17711         , i
17712         , l;
17713     
17714       while (src) {
17715         // newline
17716         if (cap = this.rules.newline.exec(src)) {
17717           src = src.substring(cap[0].length);
17718           if (cap[0].length > 1) {
17719             this.tokens.push({
17720               type: 'space'
17721             });
17722           }
17723         }
17724     
17725         // code
17726         if (cap = this.rules.code.exec(src)) {
17727           src = src.substring(cap[0].length);
17728           cap = cap[0].replace(/^ {4}/gm, '');
17729           this.tokens.push({
17730             type: 'code',
17731             text: !this.options.pedantic
17732               ? cap.replace(/\n+$/, '')
17733               : cap
17734           });
17735           continue;
17736         }
17737     
17738         // fences (gfm)
17739         if (cap = this.rules.fences.exec(src)) {
17740           src = src.substring(cap[0].length);
17741           this.tokens.push({
17742             type: 'code',
17743             lang: cap[2],
17744             text: cap[3] || ''
17745           });
17746           continue;
17747         }
17748     
17749         // heading
17750         if (cap = this.rules.heading.exec(src)) {
17751           src = src.substring(cap[0].length);
17752           this.tokens.push({
17753             type: 'heading',
17754             depth: cap[1].length,
17755             text: cap[2]
17756           });
17757           continue;
17758         }
17759     
17760         // table no leading pipe (gfm)
17761         if (top && (cap = this.rules.nptable.exec(src))) {
17762           src = src.substring(cap[0].length);
17763     
17764           item = {
17765             type: 'table',
17766             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17767             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17768             cells: cap[3].replace(/\n$/, '').split('\n')
17769           };
17770     
17771           for (i = 0; i < item.align.length; i++) {
17772             if (/^ *-+: *$/.test(item.align[i])) {
17773               item.align[i] = 'right';
17774             } else if (/^ *:-+: *$/.test(item.align[i])) {
17775               item.align[i] = 'center';
17776             } else if (/^ *:-+ *$/.test(item.align[i])) {
17777               item.align[i] = 'left';
17778             } else {
17779               item.align[i] = null;
17780             }
17781           }
17782     
17783           for (i = 0; i < item.cells.length; i++) {
17784             item.cells[i] = item.cells[i].split(/ *\| */);
17785           }
17786     
17787           this.tokens.push(item);
17788     
17789           continue;
17790         }
17791     
17792         // lheading
17793         if (cap = this.rules.lheading.exec(src)) {
17794           src = src.substring(cap[0].length);
17795           this.tokens.push({
17796             type: 'heading',
17797             depth: cap[2] === '=' ? 1 : 2,
17798             text: cap[1]
17799           });
17800           continue;
17801         }
17802     
17803         // hr
17804         if (cap = this.rules.hr.exec(src)) {
17805           src = src.substring(cap[0].length);
17806           this.tokens.push({
17807             type: 'hr'
17808           });
17809           continue;
17810         }
17811     
17812         // blockquote
17813         if (cap = this.rules.blockquote.exec(src)) {
17814           src = src.substring(cap[0].length);
17815     
17816           this.tokens.push({
17817             type: 'blockquote_start'
17818           });
17819     
17820           cap = cap[0].replace(/^ *> ?/gm, '');
17821     
17822           // Pass `top` to keep the current
17823           // "toplevel" state. This is exactly
17824           // how markdown.pl works.
17825           this.token(cap, top, true);
17826     
17827           this.tokens.push({
17828             type: 'blockquote_end'
17829           });
17830     
17831           continue;
17832         }
17833     
17834         // list
17835         if (cap = this.rules.list.exec(src)) {
17836           src = src.substring(cap[0].length);
17837           bull = cap[2];
17838     
17839           this.tokens.push({
17840             type: 'list_start',
17841             ordered: bull.length > 1
17842           });
17843     
17844           // Get each top-level item.
17845           cap = cap[0].match(this.rules.item);
17846     
17847           next = false;
17848           l = cap.length;
17849           i = 0;
17850     
17851           for (; i < l; i++) {
17852             item = cap[i];
17853     
17854             // Remove the list item's bullet
17855             // so it is seen as the next token.
17856             space = item.length;
17857             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
17858     
17859             // Outdent whatever the
17860             // list item contains. Hacky.
17861             if (~item.indexOf('\n ')) {
17862               space -= item.length;
17863               item = !this.options.pedantic
17864                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
17865                 : item.replace(/^ {1,4}/gm, '');
17866             }
17867     
17868             // Determine whether the next list item belongs here.
17869             // Backpedal if it does not belong in this list.
17870             if (this.options.smartLists && i !== l - 1) {
17871               b = block.bullet.exec(cap[i + 1])[0];
17872               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
17873                 src = cap.slice(i + 1).join('\n') + src;
17874                 i = l - 1;
17875               }
17876             }
17877     
17878             // Determine whether item is loose or not.
17879             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
17880             // for discount behavior.
17881             loose = next || /\n\n(?!\s*$)/.test(item);
17882             if (i !== l - 1) {
17883               next = item.charAt(item.length - 1) === '\n';
17884               if (!loose) { loose = next; }
17885             }
17886     
17887             this.tokens.push({
17888               type: loose
17889                 ? 'loose_item_start'
17890                 : 'list_item_start'
17891             });
17892     
17893             // Recurse.
17894             this.token(item, false, bq);
17895     
17896             this.tokens.push({
17897               type: 'list_item_end'
17898             });
17899           }
17900     
17901           this.tokens.push({
17902             type: 'list_end'
17903           });
17904     
17905           continue;
17906         }
17907     
17908         // html
17909         if (cap = this.rules.html.exec(src)) {
17910           src = src.substring(cap[0].length);
17911           this.tokens.push({
17912             type: this.options.sanitize
17913               ? 'paragraph'
17914               : 'html',
17915             pre: !this.options.sanitizer
17916               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
17917             text: cap[0]
17918           });
17919           continue;
17920         }
17921     
17922         // def
17923         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
17924           src = src.substring(cap[0].length);
17925           this.tokens.links[cap[1].toLowerCase()] = {
17926             href: cap[2],
17927             title: cap[3]
17928           };
17929           continue;
17930         }
17931     
17932         // table (gfm)
17933         if (top && (cap = this.rules.table.exec(src))) {
17934           src = src.substring(cap[0].length);
17935     
17936           item = {
17937             type: 'table',
17938             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
17939             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
17940             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
17941           };
17942     
17943           for (i = 0; i < item.align.length; i++) {
17944             if (/^ *-+: *$/.test(item.align[i])) {
17945               item.align[i] = 'right';
17946             } else if (/^ *:-+: *$/.test(item.align[i])) {
17947               item.align[i] = 'center';
17948             } else if (/^ *:-+ *$/.test(item.align[i])) {
17949               item.align[i] = 'left';
17950             } else {
17951               item.align[i] = null;
17952             }
17953           }
17954     
17955           for (i = 0; i < item.cells.length; i++) {
17956             item.cells[i] = item.cells[i]
17957               .replace(/^ *\| *| *\| *$/g, '')
17958               .split(/ *\| */);
17959           }
17960     
17961           this.tokens.push(item);
17962     
17963           continue;
17964         }
17965     
17966         // top-level paragraph
17967         if (top && (cap = this.rules.paragraph.exec(src))) {
17968           src = src.substring(cap[0].length);
17969           this.tokens.push({
17970             type: 'paragraph',
17971             text: cap[1].charAt(cap[1].length - 1) === '\n'
17972               ? cap[1].slice(0, -1)
17973               : cap[1]
17974           });
17975           continue;
17976         }
17977     
17978         // text
17979         if (cap = this.rules.text.exec(src)) {
17980           // Top-level should never reach here.
17981           src = src.substring(cap[0].length);
17982           this.tokens.push({
17983             type: 'text',
17984             text: cap[0]
17985           });
17986           continue;
17987         }
17988     
17989         if (src) {
17990           throw new
17991             Error('Infinite loop on byte: ' + src.charCodeAt(0));
17992         }
17993       }
17994     
17995       return this.tokens;
17996     };
17997     
17998     /**
17999      * Inline-Level Grammar
18000      */
18001     
18002     var inline = {
18003       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18004       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18005       url: noop,
18006       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18007       link: /^!?\[(inside)\]\(href\)/,
18008       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18009       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18010       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18011       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18012       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18013       br: /^ {2,}\n(?!\s*$)/,
18014       del: noop,
18015       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18016     };
18017     
18018     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18019     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18020     
18021     inline.link = replace(inline.link)
18022       ('inside', inline._inside)
18023       ('href', inline._href)
18024       ();
18025     
18026     inline.reflink = replace(inline.reflink)
18027       ('inside', inline._inside)
18028       ();
18029     
18030     /**
18031      * Normal Inline Grammar
18032      */
18033     
18034     inline.normal = merge({}, inline);
18035     
18036     /**
18037      * Pedantic Inline Grammar
18038      */
18039     
18040     inline.pedantic = merge({}, inline.normal, {
18041       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18042       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18043     });
18044     
18045     /**
18046      * GFM Inline Grammar
18047      */
18048     
18049     inline.gfm = merge({}, inline.normal, {
18050       escape: replace(inline.escape)('])', '~|])')(),
18051       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18052       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18053       text: replace(inline.text)
18054         (']|', '~]|')
18055         ('|', '|https?://|')
18056         ()
18057     });
18058     
18059     /**
18060      * GFM + Line Breaks Inline Grammar
18061      */
18062     
18063     inline.breaks = merge({}, inline.gfm, {
18064       br: replace(inline.br)('{2,}', '*')(),
18065       text: replace(inline.gfm.text)('{2,}', '*')()
18066     });
18067     
18068     /**
18069      * Inline Lexer & Compiler
18070      */
18071     
18072     var InlineLexer  = function (links, options) {
18073       this.options = options || marked.defaults;
18074       this.links = links;
18075       this.rules = inline.normal;
18076       this.renderer = this.options.renderer || new Renderer;
18077       this.renderer.options = this.options;
18078     
18079       if (!this.links) {
18080         throw new
18081           Error('Tokens array requires a `links` property.');
18082       }
18083     
18084       if (this.options.gfm) {
18085         if (this.options.breaks) {
18086           this.rules = inline.breaks;
18087         } else {
18088           this.rules = inline.gfm;
18089         }
18090       } else if (this.options.pedantic) {
18091         this.rules = inline.pedantic;
18092       }
18093     }
18094     
18095     /**
18096      * Expose Inline Rules
18097      */
18098     
18099     InlineLexer.rules = inline;
18100     
18101     /**
18102      * Static Lexing/Compiling Method
18103      */
18104     
18105     InlineLexer.output = function(src, links, options) {
18106       var inline = new InlineLexer(links, options);
18107       return inline.output(src);
18108     };
18109     
18110     /**
18111      * Lexing/Compiling
18112      */
18113     
18114     InlineLexer.prototype.output = function(src) {
18115       var out = ''
18116         , link
18117         , text
18118         , href
18119         , cap;
18120     
18121       while (src) {
18122         // escape
18123         if (cap = this.rules.escape.exec(src)) {
18124           src = src.substring(cap[0].length);
18125           out += cap[1];
18126           continue;
18127         }
18128     
18129         // autolink
18130         if (cap = this.rules.autolink.exec(src)) {
18131           src = src.substring(cap[0].length);
18132           if (cap[2] === '@') {
18133             text = cap[1].charAt(6) === ':'
18134               ? this.mangle(cap[1].substring(7))
18135               : this.mangle(cap[1]);
18136             href = this.mangle('mailto:') + text;
18137           } else {
18138             text = escape(cap[1]);
18139             href = text;
18140           }
18141           out += this.renderer.link(href, null, text);
18142           continue;
18143         }
18144     
18145         // url (gfm)
18146         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18147           src = src.substring(cap[0].length);
18148           text = escape(cap[1]);
18149           href = text;
18150           out += this.renderer.link(href, null, text);
18151           continue;
18152         }
18153     
18154         // tag
18155         if (cap = this.rules.tag.exec(src)) {
18156           if (!this.inLink && /^<a /i.test(cap[0])) {
18157             this.inLink = true;
18158           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18159             this.inLink = false;
18160           }
18161           src = src.substring(cap[0].length);
18162           out += this.options.sanitize
18163             ? this.options.sanitizer
18164               ? this.options.sanitizer(cap[0])
18165               : escape(cap[0])
18166             : cap[0];
18167           continue;
18168         }
18169     
18170         // link
18171         if (cap = this.rules.link.exec(src)) {
18172           src = src.substring(cap[0].length);
18173           this.inLink = true;
18174           out += this.outputLink(cap, {
18175             href: cap[2],
18176             title: cap[3]
18177           });
18178           this.inLink = false;
18179           continue;
18180         }
18181     
18182         // reflink, nolink
18183         if ((cap = this.rules.reflink.exec(src))
18184             || (cap = this.rules.nolink.exec(src))) {
18185           src = src.substring(cap[0].length);
18186           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18187           link = this.links[link.toLowerCase()];
18188           if (!link || !link.href) {
18189             out += cap[0].charAt(0);
18190             src = cap[0].substring(1) + src;
18191             continue;
18192           }
18193           this.inLink = true;
18194           out += this.outputLink(cap, link);
18195           this.inLink = false;
18196           continue;
18197         }
18198     
18199         // strong
18200         if (cap = this.rules.strong.exec(src)) {
18201           src = src.substring(cap[0].length);
18202           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18203           continue;
18204         }
18205     
18206         // em
18207         if (cap = this.rules.em.exec(src)) {
18208           src = src.substring(cap[0].length);
18209           out += this.renderer.em(this.output(cap[2] || cap[1]));
18210           continue;
18211         }
18212     
18213         // code
18214         if (cap = this.rules.code.exec(src)) {
18215           src = src.substring(cap[0].length);
18216           out += this.renderer.codespan(escape(cap[2], true));
18217           continue;
18218         }
18219     
18220         // br
18221         if (cap = this.rules.br.exec(src)) {
18222           src = src.substring(cap[0].length);
18223           out += this.renderer.br();
18224           continue;
18225         }
18226     
18227         // del (gfm)
18228         if (cap = this.rules.del.exec(src)) {
18229           src = src.substring(cap[0].length);
18230           out += this.renderer.del(this.output(cap[1]));
18231           continue;
18232         }
18233     
18234         // text
18235         if (cap = this.rules.text.exec(src)) {
18236           src = src.substring(cap[0].length);
18237           out += this.renderer.text(escape(this.smartypants(cap[0])));
18238           continue;
18239         }
18240     
18241         if (src) {
18242           throw new
18243             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18244         }
18245       }
18246     
18247       return out;
18248     };
18249     
18250     /**
18251      * Compile Link
18252      */
18253     
18254     InlineLexer.prototype.outputLink = function(cap, link) {
18255       var href = escape(link.href)
18256         , title = link.title ? escape(link.title) : null;
18257     
18258       return cap[0].charAt(0) !== '!'
18259         ? this.renderer.link(href, title, this.output(cap[1]))
18260         : this.renderer.image(href, title, escape(cap[1]));
18261     };
18262     
18263     /**
18264      * Smartypants Transformations
18265      */
18266     
18267     InlineLexer.prototype.smartypants = function(text) {
18268       if (!this.options.smartypants)  { return text; }
18269       return text
18270         // em-dashes
18271         .replace(/---/g, '\u2014')
18272         // en-dashes
18273         .replace(/--/g, '\u2013')
18274         // opening singles
18275         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
18276         // closing singles & apostrophes
18277         .replace(/'/g, '\u2019')
18278         // opening doubles
18279         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
18280         // closing doubles
18281         .replace(/"/g, '\u201d')
18282         // ellipses
18283         .replace(/\.{3}/g, '\u2026');
18284     };
18285     
18286     /**
18287      * Mangle Links
18288      */
18289     
18290     InlineLexer.prototype.mangle = function(text) {
18291       if (!this.options.mangle) { return text; }
18292       var out = ''
18293         , l = text.length
18294         , i = 0
18295         , ch;
18296     
18297       for (; i < l; i++) {
18298         ch = text.charCodeAt(i);
18299         if (Math.random() > 0.5) {
18300           ch = 'x' + ch.toString(16);
18301         }
18302         out += '&#' + ch + ';';
18303       }
18304     
18305       return out;
18306     };
18307     
18308     /**
18309      * Renderer
18310      */
18311     
18312      /**
18313          * eval:var:Renderer
18314     */
18315     
18316     var Renderer   = function (options) {
18317       this.options = options || {};
18318     }
18319     
18320     Renderer.prototype.code = function(code, lang, escaped) {
18321       if (this.options.highlight) {
18322         var out = this.options.highlight(code, lang);
18323         if (out != null && out !== code) {
18324           escaped = true;
18325           code = out;
18326         }
18327       } else {
18328             // hack!!! - it's already escapeD?
18329             escaped = true;
18330       }
18331     
18332       if (!lang) {
18333         return '<pre><code>'
18334           + (escaped ? code : escape(code, true))
18335           + '\n</code></pre>';
18336       }
18337     
18338       return '<pre><code class="'
18339         + this.options.langPrefix
18340         + escape(lang, true)
18341         + '">'
18342         + (escaped ? code : escape(code, true))
18343         + '\n</code></pre>\n';
18344     };
18345     
18346     Renderer.prototype.blockquote = function(quote) {
18347       return '<blockquote>\n' + quote + '</blockquote>\n';
18348     };
18349     
18350     Renderer.prototype.html = function(html) {
18351       return html;
18352     };
18353     
18354     Renderer.prototype.heading = function(text, level, raw) {
18355       return '<h'
18356         + level
18357         + ' id="'
18358         + this.options.headerPrefix
18359         + raw.toLowerCase().replace(/[^\w]+/g, '-')
18360         + '">'
18361         + text
18362         + '</h'
18363         + level
18364         + '>\n';
18365     };
18366     
18367     Renderer.prototype.hr = function() {
18368       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
18369     };
18370     
18371     Renderer.prototype.list = function(body, ordered) {
18372       var type = ordered ? 'ol' : 'ul';
18373       return '<' + type + '>\n' + body + '</' + type + '>\n';
18374     };
18375     
18376     Renderer.prototype.listitem = function(text) {
18377       return '<li>' + text + '</li>\n';
18378     };
18379     
18380     Renderer.prototype.paragraph = function(text) {
18381       return '<p>' + text + '</p>\n';
18382     };
18383     
18384     Renderer.prototype.table = function(header, body) {
18385       return '<table class="table table-striped">\n'
18386         + '<thead>\n'
18387         + header
18388         + '</thead>\n'
18389         + '<tbody>\n'
18390         + body
18391         + '</tbody>\n'
18392         + '</table>\n';
18393     };
18394     
18395     Renderer.prototype.tablerow = function(content) {
18396       return '<tr>\n' + content + '</tr>\n';
18397     };
18398     
18399     Renderer.prototype.tablecell = function(content, flags) {
18400       var type = flags.header ? 'th' : 'td';
18401       var tag = flags.align
18402         ? '<' + type + ' style="text-align:' + flags.align + '">'
18403         : '<' + type + '>';
18404       return tag + content + '</' + type + '>\n';
18405     };
18406     
18407     // span level renderer
18408     Renderer.prototype.strong = function(text) {
18409       return '<strong>' + text + '</strong>';
18410     };
18411     
18412     Renderer.prototype.em = function(text) {
18413       return '<em>' + text + '</em>';
18414     };
18415     
18416     Renderer.prototype.codespan = function(text) {
18417       return '<code>' + text + '</code>';
18418     };
18419     
18420     Renderer.prototype.br = function() {
18421       return this.options.xhtml ? '<br/>' : '<br>';
18422     };
18423     
18424     Renderer.prototype.del = function(text) {
18425       return '<del>' + text + '</del>';
18426     };
18427     
18428     Renderer.prototype.link = function(href, title, text) {
18429       if (this.options.sanitize) {
18430         try {
18431           var prot = decodeURIComponent(unescape(href))
18432             .replace(/[^\w:]/g, '')
18433             .toLowerCase();
18434         } catch (e) {
18435           return '';
18436         }
18437         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
18438           return '';
18439         }
18440       }
18441       var out = '<a href="' + href + '"';
18442       if (title) {
18443         out += ' title="' + title + '"';
18444       }
18445       out += '>' + text + '</a>';
18446       return out;
18447     };
18448     
18449     Renderer.prototype.image = function(href, title, text) {
18450       var out = '<img src="' + href + '" alt="' + text + '"';
18451       if (title) {
18452         out += ' title="' + title + '"';
18453       }
18454       out += this.options.xhtml ? '/>' : '>';
18455       return out;
18456     };
18457     
18458     Renderer.prototype.text = function(text) {
18459       return text;
18460     };
18461     
18462     /**
18463      * Parsing & Compiling
18464      */
18465          /**
18466          * eval:var:Parser
18467     */
18468     
18469     var Parser= function (options) {
18470       this.tokens = [];
18471       this.token = null;
18472       this.options = options || marked.defaults;
18473       this.options.renderer = this.options.renderer || new Renderer;
18474       this.renderer = this.options.renderer;
18475       this.renderer.options = this.options;
18476     }
18477     
18478     /**
18479      * Static Parse Method
18480      */
18481     
18482     Parser.parse = function(src, options, renderer) {
18483       var parser = new Parser(options, renderer);
18484       return parser.parse(src);
18485     };
18486     
18487     /**
18488      * Parse Loop
18489      */
18490     
18491     Parser.prototype.parse = function(src) {
18492       this.inline = new InlineLexer(src.links, this.options, this.renderer);
18493       this.tokens = src.reverse();
18494     
18495       var out = '';
18496       while (this.next()) {
18497         out += this.tok();
18498       }
18499     
18500       return out;
18501     };
18502     
18503     /**
18504      * Next Token
18505      */
18506     
18507     Parser.prototype.next = function() {
18508       return this.token = this.tokens.pop();
18509     };
18510     
18511     /**
18512      * Preview Next Token
18513      */
18514     
18515     Parser.prototype.peek = function() {
18516       return this.tokens[this.tokens.length - 1] || 0;
18517     };
18518     
18519     /**
18520      * Parse Text Tokens
18521      */
18522     
18523     Parser.prototype.parseText = function() {
18524       var body = this.token.text;
18525     
18526       while (this.peek().type === 'text') {
18527         body += '\n' + this.next().text;
18528       }
18529     
18530       return this.inline.output(body);
18531     };
18532     
18533     /**
18534      * Parse Current Token
18535      */
18536     
18537     Parser.prototype.tok = function() {
18538       switch (this.token.type) {
18539         case 'space': {
18540           return '';
18541         }
18542         case 'hr': {
18543           return this.renderer.hr();
18544         }
18545         case 'heading': {
18546           return this.renderer.heading(
18547             this.inline.output(this.token.text),
18548             this.token.depth,
18549             this.token.text);
18550         }
18551         case 'code': {
18552           return this.renderer.code(this.token.text,
18553             this.token.lang,
18554             this.token.escaped);
18555         }
18556         case 'table': {
18557           var header = ''
18558             , body = ''
18559             , i
18560             , row
18561             , cell
18562             , flags
18563             , j;
18564     
18565           // header
18566           cell = '';
18567           for (i = 0; i < this.token.header.length; i++) {
18568             flags = { header: true, align: this.token.align[i] };
18569             cell += this.renderer.tablecell(
18570               this.inline.output(this.token.header[i]),
18571               { header: true, align: this.token.align[i] }
18572             );
18573           }
18574           header += this.renderer.tablerow(cell);
18575     
18576           for (i = 0; i < this.token.cells.length; i++) {
18577             row = this.token.cells[i];
18578     
18579             cell = '';
18580             for (j = 0; j < row.length; j++) {
18581               cell += this.renderer.tablecell(
18582                 this.inline.output(row[j]),
18583                 { header: false, align: this.token.align[j] }
18584               );
18585             }
18586     
18587             body += this.renderer.tablerow(cell);
18588           }
18589           return this.renderer.table(header, body);
18590         }
18591         case 'blockquote_start': {
18592           var body = '';
18593     
18594           while (this.next().type !== 'blockquote_end') {
18595             body += this.tok();
18596           }
18597     
18598           return this.renderer.blockquote(body);
18599         }
18600         case 'list_start': {
18601           var body = ''
18602             , ordered = this.token.ordered;
18603     
18604           while (this.next().type !== 'list_end') {
18605             body += this.tok();
18606           }
18607     
18608           return this.renderer.list(body, ordered);
18609         }
18610         case 'list_item_start': {
18611           var body = '';
18612     
18613           while (this.next().type !== 'list_item_end') {
18614             body += this.token.type === 'text'
18615               ? this.parseText()
18616               : this.tok();
18617           }
18618     
18619           return this.renderer.listitem(body);
18620         }
18621         case 'loose_item_start': {
18622           var body = '';
18623     
18624           while (this.next().type !== 'list_item_end') {
18625             body += this.tok();
18626           }
18627     
18628           return this.renderer.listitem(body);
18629         }
18630         case 'html': {
18631           var html = !this.token.pre && !this.options.pedantic
18632             ? this.inline.output(this.token.text)
18633             : this.token.text;
18634           return this.renderer.html(html);
18635         }
18636         case 'paragraph': {
18637           return this.renderer.paragraph(this.inline.output(this.token.text));
18638         }
18639         case 'text': {
18640           return this.renderer.paragraph(this.parseText());
18641         }
18642       }
18643     };
18644   
18645     
18646     /**
18647      * Marked
18648      */
18649          /**
18650          * eval:var:marked
18651     */
18652     var marked = function (src, opt, callback) {
18653       if (callback || typeof opt === 'function') {
18654         if (!callback) {
18655           callback = opt;
18656           opt = null;
18657         }
18658     
18659         opt = merge({}, marked.defaults, opt || {});
18660     
18661         var highlight = opt.highlight
18662           , tokens
18663           , pending
18664           , i = 0;
18665     
18666         try {
18667           tokens = Lexer.lex(src, opt)
18668         } catch (e) {
18669           return callback(e);
18670         }
18671     
18672         pending = tokens.length;
18673          /**
18674          * eval:var:done
18675     */
18676         var done = function(err) {
18677           if (err) {
18678             opt.highlight = highlight;
18679             return callback(err);
18680           }
18681     
18682           var out;
18683     
18684           try {
18685             out = Parser.parse(tokens, opt);
18686           } catch (e) {
18687             err = e;
18688           }
18689     
18690           opt.highlight = highlight;
18691     
18692           return err
18693             ? callback(err)
18694             : callback(null, out);
18695         };
18696     
18697         if (!highlight || highlight.length < 3) {
18698           return done();
18699         }
18700     
18701         delete opt.highlight;
18702     
18703         if (!pending) { return done(); }
18704     
18705         for (; i < tokens.length; i++) {
18706           (function(token) {
18707             if (token.type !== 'code') {
18708               return --pending || done();
18709             }
18710             return highlight(token.text, token.lang, function(err, code) {
18711               if (err) { return done(err); }
18712               if (code == null || code === token.text) {
18713                 return --pending || done();
18714               }
18715               token.text = code;
18716               token.escaped = true;
18717               --pending || done();
18718             });
18719           })(tokens[i]);
18720         }
18721     
18722         return;
18723       }
18724       try {
18725         if (opt) { opt = merge({}, marked.defaults, opt); }
18726         return Parser.parse(Lexer.lex(src, opt), opt);
18727       } catch (e) {
18728         e.message += '\nPlease report this to https://github.com/chjj/marked.';
18729         if ((opt || marked.defaults).silent) {
18730           return '<p>An error occured:</p><pre>'
18731             + escape(e.message + '', true)
18732             + '</pre>';
18733         }
18734         throw e;
18735       }
18736     }
18737     
18738     /**
18739      * Options
18740      */
18741     
18742     marked.options =
18743     marked.setOptions = function(opt) {
18744       merge(marked.defaults, opt);
18745       return marked;
18746     };
18747     
18748     marked.defaults = {
18749       gfm: true,
18750       tables: true,
18751       breaks: false,
18752       pedantic: false,
18753       sanitize: false,
18754       sanitizer: null,
18755       mangle: true,
18756       smartLists: false,
18757       silent: false,
18758       highlight: null,
18759       langPrefix: 'lang-',
18760       smartypants: false,
18761       headerPrefix: '',
18762       renderer: new Renderer,
18763       xhtml: false
18764     };
18765     
18766     /**
18767      * Expose
18768      */
18769     
18770     marked.Parser = Parser;
18771     marked.parser = Parser.parse;
18772     
18773     marked.Renderer = Renderer;
18774     
18775     marked.Lexer = Lexer;
18776     marked.lexer = Lexer.lex;
18777     
18778     marked.InlineLexer = InlineLexer;
18779     marked.inlineLexer = InlineLexer.output;
18780     
18781     marked.parse = marked;
18782     
18783     Roo.Markdown.marked = marked;
18784
18785 })();/*
18786  * Based on:
18787  * Ext JS Library 1.1.1
18788  * Copyright(c) 2006-2007, Ext JS, LLC.
18789  *
18790  * Originally Released Under LGPL - original licence link has changed is not relivant.
18791  *
18792  * Fork - LGPL
18793  * <script type="text/javascript">
18794  */
18795
18796
18797
18798 /*
18799  * These classes are derivatives of the similarly named classes in the YUI Library.
18800  * The original license:
18801  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
18802  * Code licensed under the BSD License:
18803  * http://developer.yahoo.net/yui/license.txt
18804  */
18805
18806 (function() {
18807
18808 var Event=Roo.EventManager;
18809 var Dom=Roo.lib.Dom;
18810
18811 /**
18812  * @class Roo.dd.DragDrop
18813  * @extends Roo.util.Observable
18814  * Defines the interface and base operation of items that that can be
18815  * dragged or can be drop targets.  It was designed to be extended, overriding
18816  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
18817  * Up to three html elements can be associated with a DragDrop instance:
18818  * <ul>
18819  * <li>linked element: the element that is passed into the constructor.
18820  * This is the element which defines the boundaries for interaction with
18821  * other DragDrop objects.</li>
18822  * <li>handle element(s): The drag operation only occurs if the element that
18823  * was clicked matches a handle element.  By default this is the linked
18824  * element, but there are times that you will want only a portion of the
18825  * linked element to initiate the drag operation, and the setHandleElId()
18826  * method provides a way to define this.</li>
18827  * <li>drag element: this represents the element that would be moved along
18828  * with the cursor during a drag operation.  By default, this is the linked
18829  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
18830  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
18831  * </li>
18832  * </ul>
18833  * This class should not be instantiated until the onload event to ensure that
18834  * the associated elements are available.
18835  * The following would define a DragDrop obj that would interact with any
18836  * other DragDrop obj in the "group1" group:
18837  * <pre>
18838  *  dd = new Roo.dd.DragDrop("div1", "group1");
18839  * </pre>
18840  * Since none of the event handlers have been implemented, nothing would
18841  * actually happen if you were to run the code above.  Normally you would
18842  * override this class or one of the default implementations, but you can
18843  * also override the methods you want on an instance of the class...
18844  * <pre>
18845  *  dd.onDragDrop = function(e, id) {
18846  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
18847  *  }
18848  * </pre>
18849  * @constructor
18850  * @param {String} id of the element that is linked to this instance
18851  * @param {String} sGroup the group of related DragDrop objects
18852  * @param {object} config an object containing configurable attributes
18853  *                Valid properties for DragDrop:
18854  *                    padding, isTarget, maintainOffset, primaryButtonOnly
18855  */
18856 Roo.dd.DragDrop = function(id, sGroup, config) {
18857     if (id) {
18858         this.init(id, sGroup, config);
18859     }
18860     
18861 };
18862
18863 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
18864
18865     /**
18866      * The id of the element associated with this object.  This is what we
18867      * refer to as the "linked element" because the size and position of
18868      * this element is used to determine when the drag and drop objects have
18869      * interacted.
18870      * @property id
18871      * @type String
18872      */
18873     id: null,
18874
18875     /**
18876      * Configuration attributes passed into the constructor
18877      * @property config
18878      * @type object
18879      */
18880     config: null,
18881
18882     /**
18883      * The id of the element that will be dragged.  By default this is same
18884      * as the linked element , but could be changed to another element. Ex:
18885      * Roo.dd.DDProxy
18886      * @property dragElId
18887      * @type String
18888      * @private
18889      */
18890     dragElId: null,
18891
18892     /**
18893      * the id of the element that initiates the drag operation.  By default
18894      * this is the linked element, but could be changed to be a child of this
18895      * element.  This lets us do things like only starting the drag when the
18896      * header element within the linked html element is clicked.
18897      * @property handleElId
18898      * @type String
18899      * @private
18900      */
18901     handleElId: null,
18902
18903     /**
18904      * An associative array of HTML tags that will be ignored if clicked.
18905      * @property invalidHandleTypes
18906      * @type {string: string}
18907      */
18908     invalidHandleTypes: null,
18909
18910     /**
18911      * An associative array of ids for elements that will be ignored if clicked
18912      * @property invalidHandleIds
18913      * @type {string: string}
18914      */
18915     invalidHandleIds: null,
18916
18917     /**
18918      * An indexted array of css class names for elements that will be ignored
18919      * if clicked.
18920      * @property invalidHandleClasses
18921      * @type string[]
18922      */
18923     invalidHandleClasses: null,
18924
18925     /**
18926      * The linked element's absolute X position at the time the drag was
18927      * started
18928      * @property startPageX
18929      * @type int
18930      * @private
18931      */
18932     startPageX: 0,
18933
18934     /**
18935      * The linked element's absolute X position at the time the drag was
18936      * started
18937      * @property startPageY
18938      * @type int
18939      * @private
18940      */
18941     startPageY: 0,
18942
18943     /**
18944      * The group defines a logical collection of DragDrop objects that are
18945      * related.  Instances only get events when interacting with other
18946      * DragDrop object in the same group.  This lets us define multiple
18947      * groups using a single DragDrop subclass if we want.
18948      * @property groups
18949      * @type {string: string}
18950      */
18951     groups: null,
18952
18953     /**
18954      * Individual drag/drop instances can be locked.  This will prevent
18955      * onmousedown start drag.
18956      * @property locked
18957      * @type boolean
18958      * @private
18959      */
18960     locked: false,
18961
18962     /**
18963      * Lock this instance
18964      * @method lock
18965      */
18966     lock: function() { this.locked = true; },
18967
18968     /**
18969      * Unlock this instace
18970      * @method unlock
18971      */
18972     unlock: function() { this.locked = false; },
18973
18974     /**
18975      * By default, all insances can be a drop target.  This can be disabled by
18976      * setting isTarget to false.
18977      * @method isTarget
18978      * @type boolean
18979      */
18980     isTarget: true,
18981
18982     /**
18983      * The padding configured for this drag and drop object for calculating
18984      * the drop zone intersection with this object.
18985      * @method padding
18986      * @type int[]
18987      */
18988     padding: null,
18989
18990     /**
18991      * Cached reference to the linked element
18992      * @property _domRef
18993      * @private
18994      */
18995     _domRef: null,
18996
18997     /**
18998      * Internal typeof flag
18999      * @property __ygDragDrop
19000      * @private
19001      */
19002     __ygDragDrop: true,
19003
19004     /**
19005      * Set to true when horizontal contraints are applied
19006      * @property constrainX
19007      * @type boolean
19008      * @private
19009      */
19010     constrainX: false,
19011
19012     /**
19013      * Set to true when vertical contraints are applied
19014      * @property constrainY
19015      * @type boolean
19016      * @private
19017      */
19018     constrainY: false,
19019
19020     /**
19021      * The left constraint
19022      * @property minX
19023      * @type int
19024      * @private
19025      */
19026     minX: 0,
19027
19028     /**
19029      * The right constraint
19030      * @property maxX
19031      * @type int
19032      * @private
19033      */
19034     maxX: 0,
19035
19036     /**
19037      * The up constraint
19038      * @property minY
19039      * @type int
19040      * @type int
19041      * @private
19042      */
19043     minY: 0,
19044
19045     /**
19046      * The down constraint
19047      * @property maxY
19048      * @type int
19049      * @private
19050      */
19051     maxY: 0,
19052
19053     /**
19054      * Maintain offsets when we resetconstraints.  Set to true when you want
19055      * the position of the element relative to its parent to stay the same
19056      * when the page changes
19057      *
19058      * @property maintainOffset
19059      * @type boolean
19060      */
19061     maintainOffset: false,
19062
19063     /**
19064      * Array of pixel locations the element will snap to if we specified a
19065      * horizontal graduation/interval.  This array is generated automatically
19066      * when you define a tick interval.
19067      * @property xTicks
19068      * @type int[]
19069      */
19070     xTicks: null,
19071
19072     /**
19073      * Array of pixel locations the element will snap to if we specified a
19074      * vertical graduation/interval.  This array is generated automatically
19075      * when you define a tick interval.
19076      * @property yTicks
19077      * @type int[]
19078      */
19079     yTicks: null,
19080
19081     /**
19082      * By default the drag and drop instance will only respond to the primary
19083      * button click (left button for a right-handed mouse).  Set to true to
19084      * allow drag and drop to start with any mouse click that is propogated
19085      * by the browser
19086      * @property primaryButtonOnly
19087      * @type boolean
19088      */
19089     primaryButtonOnly: true,
19090
19091     /**
19092      * The availabe property is false until the linked dom element is accessible.
19093      * @property available
19094      * @type boolean
19095      */
19096     available: false,
19097
19098     /**
19099      * By default, drags can only be initiated if the mousedown occurs in the
19100      * region the linked element is.  This is done in part to work around a
19101      * bug in some browsers that mis-report the mousedown if the previous
19102      * mouseup happened outside of the window.  This property is set to true
19103      * if outer handles are defined.
19104      *
19105      * @property hasOuterHandles
19106      * @type boolean
19107      * @default false
19108      */
19109     hasOuterHandles: false,
19110
19111     /**
19112      * Code that executes immediately before the startDrag event
19113      * @method b4StartDrag
19114      * @private
19115      */
19116     b4StartDrag: function(x, y) { },
19117
19118     /**
19119      * Abstract method called after a drag/drop object is clicked
19120      * and the drag or mousedown time thresholds have beeen met.
19121      * @method startDrag
19122      * @param {int} X click location
19123      * @param {int} Y click location
19124      */
19125     startDrag: function(x, y) { /* override this */ },
19126
19127     /**
19128      * Code that executes immediately before the onDrag event
19129      * @method b4Drag
19130      * @private
19131      */
19132     b4Drag: function(e) { },
19133
19134     /**
19135      * Abstract method called during the onMouseMove event while dragging an
19136      * object.
19137      * @method onDrag
19138      * @param {Event} e the mousemove event
19139      */
19140     onDrag: function(e) { /* override this */ },
19141
19142     /**
19143      * Abstract method called when this element fist begins hovering over
19144      * another DragDrop obj
19145      * @method onDragEnter
19146      * @param {Event} e the mousemove event
19147      * @param {String|DragDrop[]} id In POINT mode, the element
19148      * id this is hovering over.  In INTERSECT mode, an array of one or more
19149      * dragdrop items being hovered over.
19150      */
19151     onDragEnter: function(e, id) { /* override this */ },
19152
19153     /**
19154      * Code that executes immediately before the onDragOver event
19155      * @method b4DragOver
19156      * @private
19157      */
19158     b4DragOver: function(e) { },
19159
19160     /**
19161      * Abstract method called when this element is hovering over another
19162      * DragDrop obj
19163      * @method onDragOver
19164      * @param {Event} e the mousemove event
19165      * @param {String|DragDrop[]} id In POINT mode, the element
19166      * id this is hovering over.  In INTERSECT mode, an array of dd items
19167      * being hovered over.
19168      */
19169     onDragOver: function(e, id) { /* override this */ },
19170
19171     /**
19172      * Code that executes immediately before the onDragOut event
19173      * @method b4DragOut
19174      * @private
19175      */
19176     b4DragOut: function(e) { },
19177
19178     /**
19179      * Abstract method called when we are no longer hovering over an element
19180      * @method onDragOut
19181      * @param {Event} e the mousemove event
19182      * @param {String|DragDrop[]} id In POINT mode, the element
19183      * id this was hovering over.  In INTERSECT mode, an array of dd items
19184      * that the mouse is no longer over.
19185      */
19186     onDragOut: function(e, id) { /* override this */ },
19187
19188     /**
19189      * Code that executes immediately before the onDragDrop event
19190      * @method b4DragDrop
19191      * @private
19192      */
19193     b4DragDrop: function(e) { },
19194
19195     /**
19196      * Abstract method called when this item is dropped on another DragDrop
19197      * obj
19198      * @method onDragDrop
19199      * @param {Event} e the mouseup event
19200      * @param {String|DragDrop[]} id In POINT mode, the element
19201      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19202      * was dropped on.
19203      */
19204     onDragDrop: function(e, id) { /* override this */ },
19205
19206     /**
19207      * Abstract method called when this item is dropped on an area with no
19208      * drop target
19209      * @method onInvalidDrop
19210      * @param {Event} e the mouseup event
19211      */
19212     onInvalidDrop: function(e) { /* override this */ },
19213
19214     /**
19215      * Code that executes immediately before the endDrag event
19216      * @method b4EndDrag
19217      * @private
19218      */
19219     b4EndDrag: function(e) { },
19220
19221     /**
19222      * Fired when we are done dragging the object
19223      * @method endDrag
19224      * @param {Event} e the mouseup event
19225      */
19226     endDrag: function(e) { /* override this */ },
19227
19228     /**
19229      * Code executed immediately before the onMouseDown event
19230      * @method b4MouseDown
19231      * @param {Event} e the mousedown event
19232      * @private
19233      */
19234     b4MouseDown: function(e) {  },
19235
19236     /**
19237      * Event handler that fires when a drag/drop obj gets a mousedown
19238      * @method onMouseDown
19239      * @param {Event} e the mousedown event
19240      */
19241     onMouseDown: function(e) { /* override this */ },
19242
19243     /**
19244      * Event handler that fires when a drag/drop obj gets a mouseup
19245      * @method onMouseUp
19246      * @param {Event} e the mouseup event
19247      */
19248     onMouseUp: function(e) { /* override this */ },
19249
19250     /**
19251      * Override the onAvailable method to do what is needed after the initial
19252      * position was determined.
19253      * @method onAvailable
19254      */
19255     onAvailable: function () {
19256     },
19257
19258     /*
19259      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19260      * @type Object
19261      */
19262     defaultPadding : {left:0, right:0, top:0, bottom:0},
19263
19264     /*
19265      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19266  *
19267  * Usage:
19268  <pre><code>
19269  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19270                 { dragElId: "existingProxyDiv" });
19271  dd.startDrag = function(){
19272      this.constrainTo("parent-id");
19273  };
19274  </code></pre>
19275  * Or you can initalize it using the {@link Roo.Element} object:
19276  <pre><code>
19277  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
19278      startDrag : function(){
19279          this.constrainTo("parent-id");
19280      }
19281  });
19282  </code></pre>
19283      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
19284      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
19285      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
19286      * an object containing the sides to pad. For example: {right:10, bottom:10}
19287      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
19288      */
19289     constrainTo : function(constrainTo, pad, inContent){
19290         if(typeof pad == "number"){
19291             pad = {left: pad, right:pad, top:pad, bottom:pad};
19292         }
19293         pad = pad || this.defaultPadding;
19294         var b = Roo.get(this.getEl()).getBox();
19295         var ce = Roo.get(constrainTo);
19296         var s = ce.getScroll();
19297         var c, cd = ce.dom;
19298         if(cd == document.body){
19299             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
19300         }else{
19301             xy = ce.getXY();
19302             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
19303         }
19304
19305
19306         var topSpace = b.y - c.y;
19307         var leftSpace = b.x - c.x;
19308
19309         this.resetConstraints();
19310         this.setXConstraint(leftSpace - (pad.left||0), // left
19311                 c.width - leftSpace - b.width - (pad.right||0) //right
19312         );
19313         this.setYConstraint(topSpace - (pad.top||0), //top
19314                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
19315         );
19316     },
19317
19318     /**
19319      * Returns a reference to the linked element
19320      * @method getEl
19321      * @return {HTMLElement} the html element
19322      */
19323     getEl: function() {
19324         if (!this._domRef) {
19325             this._domRef = Roo.getDom(this.id);
19326         }
19327
19328         return this._domRef;
19329     },
19330
19331     /**
19332      * Returns a reference to the actual element to drag.  By default this is
19333      * the same as the html element, but it can be assigned to another
19334      * element. An example of this can be found in Roo.dd.DDProxy
19335      * @method getDragEl
19336      * @return {HTMLElement} the html element
19337      */
19338     getDragEl: function() {
19339         return Roo.getDom(this.dragElId);
19340     },
19341
19342     /**
19343      * Sets up the DragDrop object.  Must be called in the constructor of any
19344      * Roo.dd.DragDrop subclass
19345      * @method init
19346      * @param id the id of the linked element
19347      * @param {String} sGroup the group of related items
19348      * @param {object} config configuration attributes
19349      */
19350     init: function(id, sGroup, config) {
19351         this.initTarget(id, sGroup, config);
19352         if (!Roo.isTouch) {
19353             Event.on(this.id, "mousedown", this.handleMouseDown, this);
19354         }
19355         Event.on(this.id, "touchstart", this.handleMouseDown, this);
19356         // Event.on(this.id, "selectstart", Event.preventDefault);
19357     },
19358
19359     /**
19360      * Initializes Targeting functionality only... the object does not
19361      * get a mousedown handler.
19362      * @method initTarget
19363      * @param id the id of the linked element
19364      * @param {String} sGroup the group of related items
19365      * @param {object} config configuration attributes
19366      */
19367     initTarget: function(id, sGroup, config) {
19368
19369         // configuration attributes
19370         this.config = config || {};
19371
19372         // create a local reference to the drag and drop manager
19373         this.DDM = Roo.dd.DDM;
19374         // initialize the groups array
19375         this.groups = {};
19376
19377         // assume that we have an element reference instead of an id if the
19378         // parameter is not a string
19379         if (typeof id !== "string") {
19380             id = Roo.id(id);
19381         }
19382
19383         // set the id
19384         this.id = id;
19385
19386         // add to an interaction group
19387         this.addToGroup((sGroup) ? sGroup : "default");
19388
19389         // We don't want to register this as the handle with the manager
19390         // so we just set the id rather than calling the setter.
19391         this.handleElId = id;
19392
19393         // the linked element is the element that gets dragged by default
19394         this.setDragElId(id);
19395
19396         // by default, clicked anchors will not start drag operations.
19397         this.invalidHandleTypes = { A: "A" };
19398         this.invalidHandleIds = {};
19399         this.invalidHandleClasses = [];
19400
19401         this.applyConfig();
19402
19403         this.handleOnAvailable();
19404     },
19405
19406     /**
19407      * Applies the configuration parameters that were passed into the constructor.
19408      * This is supposed to happen at each level through the inheritance chain.  So
19409      * a DDProxy implentation will execute apply config on DDProxy, DD, and
19410      * DragDrop in order to get all of the parameters that are available in
19411      * each object.
19412      * @method applyConfig
19413      */
19414     applyConfig: function() {
19415
19416         // configurable properties:
19417         //    padding, isTarget, maintainOffset, primaryButtonOnly
19418         this.padding           = this.config.padding || [0, 0, 0, 0];
19419         this.isTarget          = (this.config.isTarget !== false);
19420         this.maintainOffset    = (this.config.maintainOffset);
19421         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
19422
19423     },
19424
19425     /**
19426      * Executed when the linked element is available
19427      * @method handleOnAvailable
19428      * @private
19429      */
19430     handleOnAvailable: function() {
19431         this.available = true;
19432         this.resetConstraints();
19433         this.onAvailable();
19434     },
19435
19436      /**
19437      * Configures the padding for the target zone in px.  Effectively expands
19438      * (or reduces) the virtual object size for targeting calculations.
19439      * Supports css-style shorthand; if only one parameter is passed, all sides
19440      * will have that padding, and if only two are passed, the top and bottom
19441      * will have the first param, the left and right the second.
19442      * @method setPadding
19443      * @param {int} iTop    Top pad
19444      * @param {int} iRight  Right pad
19445      * @param {int} iBot    Bot pad
19446      * @param {int} iLeft   Left pad
19447      */
19448     setPadding: function(iTop, iRight, iBot, iLeft) {
19449         // this.padding = [iLeft, iRight, iTop, iBot];
19450         if (!iRight && 0 !== iRight) {
19451             this.padding = [iTop, iTop, iTop, iTop];
19452         } else if (!iBot && 0 !== iBot) {
19453             this.padding = [iTop, iRight, iTop, iRight];
19454         } else {
19455             this.padding = [iTop, iRight, iBot, iLeft];
19456         }
19457     },
19458
19459     /**
19460      * Stores the initial placement of the linked element.
19461      * @method setInitialPosition
19462      * @param {int} diffX   the X offset, default 0
19463      * @param {int} diffY   the Y offset, default 0
19464      */
19465     setInitPosition: function(diffX, diffY) {
19466         var el = this.getEl();
19467
19468         if (!this.DDM.verifyEl(el)) {
19469             return;
19470         }
19471
19472         var dx = diffX || 0;
19473         var dy = diffY || 0;
19474
19475         var p = Dom.getXY( el );
19476
19477         this.initPageX = p[0] - dx;
19478         this.initPageY = p[1] - dy;
19479
19480         this.lastPageX = p[0];
19481         this.lastPageY = p[1];
19482
19483
19484         this.setStartPosition(p);
19485     },
19486
19487     /**
19488      * Sets the start position of the element.  This is set when the obj
19489      * is initialized, the reset when a drag is started.
19490      * @method setStartPosition
19491      * @param pos current position (from previous lookup)
19492      * @private
19493      */
19494     setStartPosition: function(pos) {
19495         var p = pos || Dom.getXY( this.getEl() );
19496         this.deltaSetXY = null;
19497
19498         this.startPageX = p[0];
19499         this.startPageY = p[1];
19500     },
19501
19502     /**
19503      * Add this instance to a group of related drag/drop objects.  All
19504      * instances belong to at least one group, and can belong to as many
19505      * groups as needed.
19506      * @method addToGroup
19507      * @param sGroup {string} the name of the group
19508      */
19509     addToGroup: function(sGroup) {
19510         this.groups[sGroup] = true;
19511         this.DDM.regDragDrop(this, sGroup);
19512     },
19513
19514     /**
19515      * Remove's this instance from the supplied interaction group
19516      * @method removeFromGroup
19517      * @param {string}  sGroup  The group to drop
19518      */
19519     removeFromGroup: function(sGroup) {
19520         if (this.groups[sGroup]) {
19521             delete this.groups[sGroup];
19522         }
19523
19524         this.DDM.removeDDFromGroup(this, sGroup);
19525     },
19526
19527     /**
19528      * Allows you to specify that an element other than the linked element
19529      * will be moved with the cursor during a drag
19530      * @method setDragElId
19531      * @param id {string} the id of the element that will be used to initiate the drag
19532      */
19533     setDragElId: function(id) {
19534         this.dragElId = id;
19535     },
19536
19537     /**
19538      * Allows you to specify a child of the linked element that should be
19539      * used to initiate the drag operation.  An example of this would be if
19540      * you have a content div with text and links.  Clicking anywhere in the
19541      * content area would normally start the drag operation.  Use this method
19542      * to specify that an element inside of the content div is the element
19543      * that starts the drag operation.
19544      * @method setHandleElId
19545      * @param id {string} the id of the element that will be used to
19546      * initiate the drag.
19547      */
19548     setHandleElId: function(id) {
19549         if (typeof id !== "string") {
19550             id = Roo.id(id);
19551         }
19552         this.handleElId = id;
19553         this.DDM.regHandle(this.id, id);
19554     },
19555
19556     /**
19557      * Allows you to set an element outside of the linked element as a drag
19558      * handle
19559      * @method setOuterHandleElId
19560      * @param id the id of the element that will be used to initiate the drag
19561      */
19562     setOuterHandleElId: function(id) {
19563         if (typeof id !== "string") {
19564             id = Roo.id(id);
19565         }
19566         Event.on(id, "mousedown",
19567                 this.handleMouseDown, this);
19568         this.setHandleElId(id);
19569
19570         this.hasOuterHandles = true;
19571     },
19572
19573     /**
19574      * Remove all drag and drop hooks for this element
19575      * @method unreg
19576      */
19577     unreg: function() {
19578         Event.un(this.id, "mousedown",
19579                 this.handleMouseDown);
19580         Event.un(this.id, "touchstart",
19581                 this.handleMouseDown);
19582         this._domRef = null;
19583         this.DDM._remove(this);
19584     },
19585
19586     destroy : function(){
19587         this.unreg();
19588     },
19589
19590     /**
19591      * Returns true if this instance is locked, or the drag drop mgr is locked
19592      * (meaning that all drag/drop is disabled on the page.)
19593      * @method isLocked
19594      * @return {boolean} true if this obj or all drag/drop is locked, else
19595      * false
19596      */
19597     isLocked: function() {
19598         return (this.DDM.isLocked() || this.locked);
19599     },
19600
19601     /**
19602      * Fired when this object is clicked
19603      * @method handleMouseDown
19604      * @param {Event} e
19605      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
19606      * @private
19607      */
19608     handleMouseDown: function(e, oDD){
19609      
19610         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
19611             //Roo.log('not touch/ button !=0');
19612             return;
19613         }
19614         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
19615             return; // double touch..
19616         }
19617         
19618
19619         if (this.isLocked()) {
19620             //Roo.log('locked');
19621             return;
19622         }
19623
19624         this.DDM.refreshCache(this.groups);
19625 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
19626         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
19627         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
19628             //Roo.log('no outer handes or not over target');
19629                 // do nothing.
19630         } else {
19631 //            Roo.log('check validator');
19632             if (this.clickValidator(e)) {
19633 //                Roo.log('validate success');
19634                 // set the initial element position
19635                 this.setStartPosition();
19636
19637
19638                 this.b4MouseDown(e);
19639                 this.onMouseDown(e);
19640
19641                 this.DDM.handleMouseDown(e, this);
19642
19643                 this.DDM.stopEvent(e);
19644             } else {
19645
19646
19647             }
19648         }
19649     },
19650
19651     clickValidator: function(e) {
19652         var target = e.getTarget();
19653         return ( this.isValidHandleChild(target) &&
19654                     (this.id == this.handleElId ||
19655                         this.DDM.handleWasClicked(target, this.id)) );
19656     },
19657
19658     /**
19659      * Allows you to specify a tag name that should not start a drag operation
19660      * when clicked.  This is designed to facilitate embedding links within a
19661      * drag handle that do something other than start the drag.
19662      * @method addInvalidHandleType
19663      * @param {string} tagName the type of element to exclude
19664      */
19665     addInvalidHandleType: function(tagName) {
19666         var type = tagName.toUpperCase();
19667         this.invalidHandleTypes[type] = type;
19668     },
19669
19670     /**
19671      * Lets you to specify an element id for a child of a drag handle
19672      * that should not initiate a drag
19673      * @method addInvalidHandleId
19674      * @param {string} id the element id of the element you wish to ignore
19675      */
19676     addInvalidHandleId: function(id) {
19677         if (typeof id !== "string") {
19678             id = Roo.id(id);
19679         }
19680         this.invalidHandleIds[id] = id;
19681     },
19682
19683     /**
19684      * Lets you specify a css class of elements that will not initiate a drag
19685      * @method addInvalidHandleClass
19686      * @param {string} cssClass the class of the elements you wish to ignore
19687      */
19688     addInvalidHandleClass: function(cssClass) {
19689         this.invalidHandleClasses.push(cssClass);
19690     },
19691
19692     /**
19693      * Unsets an excluded tag name set by addInvalidHandleType
19694      * @method removeInvalidHandleType
19695      * @param {string} tagName the type of element to unexclude
19696      */
19697     removeInvalidHandleType: function(tagName) {
19698         var type = tagName.toUpperCase();
19699         // this.invalidHandleTypes[type] = null;
19700         delete this.invalidHandleTypes[type];
19701     },
19702
19703     /**
19704      * Unsets an invalid handle id
19705      * @method removeInvalidHandleId
19706      * @param {string} id the id of the element to re-enable
19707      */
19708     removeInvalidHandleId: function(id) {
19709         if (typeof id !== "string") {
19710             id = Roo.id(id);
19711         }
19712         delete this.invalidHandleIds[id];
19713     },
19714
19715     /**
19716      * Unsets an invalid css class
19717      * @method removeInvalidHandleClass
19718      * @param {string} cssClass the class of the element(s) you wish to
19719      * re-enable
19720      */
19721     removeInvalidHandleClass: function(cssClass) {
19722         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
19723             if (this.invalidHandleClasses[i] == cssClass) {
19724                 delete this.invalidHandleClasses[i];
19725             }
19726         }
19727     },
19728
19729     /**
19730      * Checks the tag exclusion list to see if this click should be ignored
19731      * @method isValidHandleChild
19732      * @param {HTMLElement} node the HTMLElement to evaluate
19733      * @return {boolean} true if this is a valid tag type, false if not
19734      */
19735     isValidHandleChild: function(node) {
19736
19737         var valid = true;
19738         // var n = (node.nodeName == "#text") ? node.parentNode : node;
19739         var nodeName;
19740         try {
19741             nodeName = node.nodeName.toUpperCase();
19742         } catch(e) {
19743             nodeName = node.nodeName;
19744         }
19745         valid = valid && !this.invalidHandleTypes[nodeName];
19746         valid = valid && !this.invalidHandleIds[node.id];
19747
19748         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
19749             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
19750         }
19751
19752
19753         return valid;
19754
19755     },
19756
19757     /**
19758      * Create the array of horizontal tick marks if an interval was specified
19759      * in setXConstraint().
19760      * @method setXTicks
19761      * @private
19762      */
19763     setXTicks: function(iStartX, iTickSize) {
19764         this.xTicks = [];
19765         this.xTickSize = iTickSize;
19766
19767         var tickMap = {};
19768
19769         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
19770             if (!tickMap[i]) {
19771                 this.xTicks[this.xTicks.length] = i;
19772                 tickMap[i] = true;
19773             }
19774         }
19775
19776         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
19777             if (!tickMap[i]) {
19778                 this.xTicks[this.xTicks.length] = i;
19779                 tickMap[i] = true;
19780             }
19781         }
19782
19783         this.xTicks.sort(this.DDM.numericSort) ;
19784     },
19785
19786     /**
19787      * Create the array of vertical tick marks if an interval was specified in
19788      * setYConstraint().
19789      * @method setYTicks
19790      * @private
19791      */
19792     setYTicks: function(iStartY, iTickSize) {
19793         this.yTicks = [];
19794         this.yTickSize = iTickSize;
19795
19796         var tickMap = {};
19797
19798         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
19799             if (!tickMap[i]) {
19800                 this.yTicks[this.yTicks.length] = i;
19801                 tickMap[i] = true;
19802             }
19803         }
19804
19805         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
19806             if (!tickMap[i]) {
19807                 this.yTicks[this.yTicks.length] = i;
19808                 tickMap[i] = true;
19809             }
19810         }
19811
19812         this.yTicks.sort(this.DDM.numericSort) ;
19813     },
19814
19815     /**
19816      * By default, the element can be dragged any place on the screen.  Use
19817      * this method to limit the horizontal travel of the element.  Pass in
19818      * 0,0 for the parameters if you want to lock the drag to the y axis.
19819      * @method setXConstraint
19820      * @param {int} iLeft the number of pixels the element can move to the left
19821      * @param {int} iRight the number of pixels the element can move to the
19822      * right
19823      * @param {int} iTickSize optional parameter for specifying that the
19824      * element
19825      * should move iTickSize pixels at a time.
19826      */
19827     setXConstraint: function(iLeft, iRight, iTickSize) {
19828         this.leftConstraint = iLeft;
19829         this.rightConstraint = iRight;
19830
19831         this.minX = this.initPageX - iLeft;
19832         this.maxX = this.initPageX + iRight;
19833         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
19834
19835         this.constrainX = true;
19836     },
19837
19838     /**
19839      * Clears any constraints applied to this instance.  Also clears ticks
19840      * since they can't exist independent of a constraint at this time.
19841      * @method clearConstraints
19842      */
19843     clearConstraints: function() {
19844         this.constrainX = false;
19845         this.constrainY = false;
19846         this.clearTicks();
19847     },
19848
19849     /**
19850      * Clears any tick interval defined for this instance
19851      * @method clearTicks
19852      */
19853     clearTicks: function() {
19854         this.xTicks = null;
19855         this.yTicks = null;
19856         this.xTickSize = 0;
19857         this.yTickSize = 0;
19858     },
19859
19860     /**
19861      * By default, the element can be dragged any place on the screen.  Set
19862      * this to limit the vertical travel of the element.  Pass in 0,0 for the
19863      * parameters if you want to lock the drag to the x axis.
19864      * @method setYConstraint
19865      * @param {int} iUp the number of pixels the element can move up
19866      * @param {int} iDown the number of pixels the element can move down
19867      * @param {int} iTickSize optional parameter for specifying that the
19868      * element should move iTickSize pixels at a time.
19869      */
19870     setYConstraint: function(iUp, iDown, iTickSize) {
19871         this.topConstraint = iUp;
19872         this.bottomConstraint = iDown;
19873
19874         this.minY = this.initPageY - iUp;
19875         this.maxY = this.initPageY + iDown;
19876         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
19877
19878         this.constrainY = true;
19879
19880     },
19881
19882     /**
19883      * resetConstraints must be called if you manually reposition a dd element.
19884      * @method resetConstraints
19885      * @param {boolean} maintainOffset
19886      */
19887     resetConstraints: function() {
19888
19889
19890         // Maintain offsets if necessary
19891         if (this.initPageX || this.initPageX === 0) {
19892             // figure out how much this thing has moved
19893             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
19894             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
19895
19896             this.setInitPosition(dx, dy);
19897
19898         // This is the first time we have detected the element's position
19899         } else {
19900             this.setInitPosition();
19901         }
19902
19903         if (this.constrainX) {
19904             this.setXConstraint( this.leftConstraint,
19905                                  this.rightConstraint,
19906                                  this.xTickSize        );
19907         }
19908
19909         if (this.constrainY) {
19910             this.setYConstraint( this.topConstraint,
19911                                  this.bottomConstraint,
19912                                  this.yTickSize         );
19913         }
19914     },
19915
19916     /**
19917      * Normally the drag element is moved pixel by pixel, but we can specify
19918      * that it move a number of pixels at a time.  This method resolves the
19919      * location when we have it set up like this.
19920      * @method getTick
19921      * @param {int} val where we want to place the object
19922      * @param {int[]} tickArray sorted array of valid points
19923      * @return {int} the closest tick
19924      * @private
19925      */
19926     getTick: function(val, tickArray) {
19927
19928         if (!tickArray) {
19929             // If tick interval is not defined, it is effectively 1 pixel,
19930             // so we return the value passed to us.
19931             return val;
19932         } else if (tickArray[0] >= val) {
19933             // The value is lower than the first tick, so we return the first
19934             // tick.
19935             return tickArray[0];
19936         } else {
19937             for (var i=0, len=tickArray.length; i<len; ++i) {
19938                 var next = i + 1;
19939                 if (tickArray[next] && tickArray[next] >= val) {
19940                     var diff1 = val - tickArray[i];
19941                     var diff2 = tickArray[next] - val;
19942                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
19943                 }
19944             }
19945
19946             // The value is larger than the last tick, so we return the last
19947             // tick.
19948             return tickArray[tickArray.length - 1];
19949         }
19950     },
19951
19952     /**
19953      * toString method
19954      * @method toString
19955      * @return {string} string representation of the dd obj
19956      */
19957     toString: function() {
19958         return ("DragDrop " + this.id);
19959     }
19960
19961 });
19962
19963 })();
19964 /*
19965  * Based on:
19966  * Ext JS Library 1.1.1
19967  * Copyright(c) 2006-2007, Ext JS, LLC.
19968  *
19969  * Originally Released Under LGPL - original licence link has changed is not relivant.
19970  *
19971  * Fork - LGPL
19972  * <script type="text/javascript">
19973  */
19974
19975
19976 /**
19977  * The drag and drop utility provides a framework for building drag and drop
19978  * applications.  In addition to enabling drag and drop for specific elements,
19979  * the drag and drop elements are tracked by the manager class, and the
19980  * interactions between the various elements are tracked during the drag and
19981  * the implementing code is notified about these important moments.
19982  */
19983
19984 // Only load the library once.  Rewriting the manager class would orphan
19985 // existing drag and drop instances.
19986 if (!Roo.dd.DragDropMgr) {
19987
19988 /**
19989  * @class Roo.dd.DragDropMgr
19990  * DragDropMgr is a singleton that tracks the element interaction for
19991  * all DragDrop items in the window.  Generally, you will not call
19992  * this class directly, but it does have helper methods that could
19993  * be useful in your DragDrop implementations.
19994  * @singleton
19995  */
19996 Roo.dd.DragDropMgr = function() {
19997
19998     var Event = Roo.EventManager;
19999
20000     return {
20001
20002         /**
20003          * Two dimensional Array of registered DragDrop objects.  The first
20004          * dimension is the DragDrop item group, the second the DragDrop
20005          * object.
20006          * @property ids
20007          * @type {string: string}
20008          * @private
20009          * @static
20010          */
20011         ids: {},
20012
20013         /**
20014          * Array of element ids defined as drag handles.  Used to determine
20015          * if the element that generated the mousedown event is actually the
20016          * handle and not the html element itself.
20017          * @property handleIds
20018          * @type {string: string}
20019          * @private
20020          * @static
20021          */
20022         handleIds: {},
20023
20024         /**
20025          * the DragDrop object that is currently being dragged
20026          * @property dragCurrent
20027          * @type DragDrop
20028          * @private
20029          * @static
20030          **/
20031         dragCurrent: null,
20032
20033         /**
20034          * the DragDrop object(s) that are being hovered over
20035          * @property dragOvers
20036          * @type Array
20037          * @private
20038          * @static
20039          */
20040         dragOvers: {},
20041
20042         /**
20043          * the X distance between the cursor and the object being dragged
20044          * @property deltaX
20045          * @type int
20046          * @private
20047          * @static
20048          */
20049         deltaX: 0,
20050
20051         /**
20052          * the Y distance between the cursor and the object being dragged
20053          * @property deltaY
20054          * @type int
20055          * @private
20056          * @static
20057          */
20058         deltaY: 0,
20059
20060         /**
20061          * Flag to determine if we should prevent the default behavior of the
20062          * events we define. By default this is true, but this can be set to
20063          * false if you need the default behavior (not recommended)
20064          * @property preventDefault
20065          * @type boolean
20066          * @static
20067          */
20068         preventDefault: true,
20069
20070         /**
20071          * Flag to determine if we should stop the propagation of the events
20072          * we generate. This is true by default but you may want to set it to
20073          * false if the html element contains other features that require the
20074          * mouse click.
20075          * @property stopPropagation
20076          * @type boolean
20077          * @static
20078          */
20079         stopPropagation: true,
20080
20081         /**
20082          * Internal flag that is set to true when drag and drop has been
20083          * intialized
20084          * @property initialized
20085          * @private
20086          * @static
20087          */
20088         initalized: false,
20089
20090         /**
20091          * All drag and drop can be disabled.
20092          * @property locked
20093          * @private
20094          * @static
20095          */
20096         locked: false,
20097
20098         /**
20099          * Called the first time an element is registered.
20100          * @method init
20101          * @private
20102          * @static
20103          */
20104         init: function() {
20105             this.initialized = true;
20106         },
20107
20108         /**
20109          * In point mode, drag and drop interaction is defined by the
20110          * location of the cursor during the drag/drop
20111          * @property POINT
20112          * @type int
20113          * @static
20114          */
20115         POINT: 0,
20116
20117         /**
20118          * In intersect mode, drag and drop interactio nis defined by the
20119          * overlap of two or more drag and drop objects.
20120          * @property INTERSECT
20121          * @type int
20122          * @static
20123          */
20124         INTERSECT: 1,
20125
20126         /**
20127          * The current drag and drop mode.  Default: POINT
20128          * @property mode
20129          * @type int
20130          * @static
20131          */
20132         mode: 0,
20133
20134         /**
20135          * Runs method on all drag and drop objects
20136          * @method _execOnAll
20137          * @private
20138          * @static
20139          */
20140         _execOnAll: function(sMethod, args) {
20141             for (var i in this.ids) {
20142                 for (var j in this.ids[i]) {
20143                     var oDD = this.ids[i][j];
20144                     if (! this.isTypeOfDD(oDD)) {
20145                         continue;
20146                     }
20147                     oDD[sMethod].apply(oDD, args);
20148                 }
20149             }
20150         },
20151
20152         /**
20153          * Drag and drop initialization.  Sets up the global event handlers
20154          * @method _onLoad
20155          * @private
20156          * @static
20157          */
20158         _onLoad: function() {
20159
20160             this.init();
20161
20162             if (!Roo.isTouch) {
20163                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20164                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20165             }
20166             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20167             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20168             
20169             Event.on(window,   "unload",    this._onUnload, this, true);
20170             Event.on(window,   "resize",    this._onResize, this, true);
20171             // Event.on(window,   "mouseout",    this._test);
20172
20173         },
20174
20175         /**
20176          * Reset constraints on all drag and drop objs
20177          * @method _onResize
20178          * @private
20179          * @static
20180          */
20181         _onResize: function(e) {
20182             this._execOnAll("resetConstraints", []);
20183         },
20184
20185         /**
20186          * Lock all drag and drop functionality
20187          * @method lock
20188          * @static
20189          */
20190         lock: function() { this.locked = true; },
20191
20192         /**
20193          * Unlock all drag and drop functionality
20194          * @method unlock
20195          * @static
20196          */
20197         unlock: function() { this.locked = false; },
20198
20199         /**
20200          * Is drag and drop locked?
20201          * @method isLocked
20202          * @return {boolean} True if drag and drop is locked, false otherwise.
20203          * @static
20204          */
20205         isLocked: function() { return this.locked; },
20206
20207         /**
20208          * Location cache that is set for all drag drop objects when a drag is
20209          * initiated, cleared when the drag is finished.
20210          * @property locationCache
20211          * @private
20212          * @static
20213          */
20214         locationCache: {},
20215
20216         /**
20217          * Set useCache to false if you want to force object the lookup of each
20218          * drag and drop linked element constantly during a drag.
20219          * @property useCache
20220          * @type boolean
20221          * @static
20222          */
20223         useCache: true,
20224
20225         /**
20226          * The number of pixels that the mouse needs to move after the
20227          * mousedown before the drag is initiated.  Default=3;
20228          * @property clickPixelThresh
20229          * @type int
20230          * @static
20231          */
20232         clickPixelThresh: 3,
20233
20234         /**
20235          * The number of milliseconds after the mousedown event to initiate the
20236          * drag if we don't get a mouseup event. Default=1000
20237          * @property clickTimeThresh
20238          * @type int
20239          * @static
20240          */
20241         clickTimeThresh: 350,
20242
20243         /**
20244          * Flag that indicates that either the drag pixel threshold or the
20245          * mousdown time threshold has been met
20246          * @property dragThreshMet
20247          * @type boolean
20248          * @private
20249          * @static
20250          */
20251         dragThreshMet: false,
20252
20253         /**
20254          * Timeout used for the click time threshold
20255          * @property clickTimeout
20256          * @type Object
20257          * @private
20258          * @static
20259          */
20260         clickTimeout: null,
20261
20262         /**
20263          * The X position of the mousedown event stored for later use when a
20264          * drag threshold is met.
20265          * @property startX
20266          * @type int
20267          * @private
20268          * @static
20269          */
20270         startX: 0,
20271
20272         /**
20273          * The Y position of the mousedown event stored for later use when a
20274          * drag threshold is met.
20275          * @property startY
20276          * @type int
20277          * @private
20278          * @static
20279          */
20280         startY: 0,
20281
20282         /**
20283          * Each DragDrop instance must be registered with the DragDropMgr.
20284          * This is executed in DragDrop.init()
20285          * @method regDragDrop
20286          * @param {DragDrop} oDD the DragDrop object to register
20287          * @param {String} sGroup the name of the group this element belongs to
20288          * @static
20289          */
20290         regDragDrop: function(oDD, sGroup) {
20291             if (!this.initialized) { this.init(); }
20292
20293             if (!this.ids[sGroup]) {
20294                 this.ids[sGroup] = {};
20295             }
20296             this.ids[sGroup][oDD.id] = oDD;
20297         },
20298
20299         /**
20300          * Removes the supplied dd instance from the supplied group. Executed
20301          * by DragDrop.removeFromGroup, so don't call this function directly.
20302          * @method removeDDFromGroup
20303          * @private
20304          * @static
20305          */
20306         removeDDFromGroup: function(oDD, sGroup) {
20307             if (!this.ids[sGroup]) {
20308                 this.ids[sGroup] = {};
20309             }
20310
20311             var obj = this.ids[sGroup];
20312             if (obj && obj[oDD.id]) {
20313                 delete obj[oDD.id];
20314             }
20315         },
20316
20317         /**
20318          * Unregisters a drag and drop item.  This is executed in
20319          * DragDrop.unreg, use that method instead of calling this directly.
20320          * @method _remove
20321          * @private
20322          * @static
20323          */
20324         _remove: function(oDD) {
20325             for (var g in oDD.groups) {
20326                 if (g && this.ids[g][oDD.id]) {
20327                     delete this.ids[g][oDD.id];
20328                 }
20329             }
20330             delete this.handleIds[oDD.id];
20331         },
20332
20333         /**
20334          * Each DragDrop handle element must be registered.  This is done
20335          * automatically when executing DragDrop.setHandleElId()
20336          * @method regHandle
20337          * @param {String} sDDId the DragDrop id this element is a handle for
20338          * @param {String} sHandleId the id of the element that is the drag
20339          * handle
20340          * @static
20341          */
20342         regHandle: function(sDDId, sHandleId) {
20343             if (!this.handleIds[sDDId]) {
20344                 this.handleIds[sDDId] = {};
20345             }
20346             this.handleIds[sDDId][sHandleId] = sHandleId;
20347         },
20348
20349         /**
20350          * Utility function to determine if a given element has been
20351          * registered as a drag drop item.
20352          * @method isDragDrop
20353          * @param {String} id the element id to check
20354          * @return {boolean} true if this element is a DragDrop item,
20355          * false otherwise
20356          * @static
20357          */
20358         isDragDrop: function(id) {
20359             return ( this.getDDById(id) ) ? true : false;
20360         },
20361
20362         /**
20363          * Returns the drag and drop instances that are in all groups the
20364          * passed in instance belongs to.
20365          * @method getRelated
20366          * @param {DragDrop} p_oDD the obj to get related data for
20367          * @param {boolean} bTargetsOnly if true, only return targetable objs
20368          * @return {DragDrop[]} the related instances
20369          * @static
20370          */
20371         getRelated: function(p_oDD, bTargetsOnly) {
20372             var oDDs = [];
20373             for (var i in p_oDD.groups) {
20374                 for (j in this.ids[i]) {
20375                     var dd = this.ids[i][j];
20376                     if (! this.isTypeOfDD(dd)) {
20377                         continue;
20378                     }
20379                     if (!bTargetsOnly || dd.isTarget) {
20380                         oDDs[oDDs.length] = dd;
20381                     }
20382                 }
20383             }
20384
20385             return oDDs;
20386         },
20387
20388         /**
20389          * Returns true if the specified dd target is a legal target for
20390          * the specifice drag obj
20391          * @method isLegalTarget
20392          * @param {DragDrop} the drag obj
20393          * @param {DragDrop} the target
20394          * @return {boolean} true if the target is a legal target for the
20395          * dd obj
20396          * @static
20397          */
20398         isLegalTarget: function (oDD, oTargetDD) {
20399             var targets = this.getRelated(oDD, true);
20400             for (var i=0, len=targets.length;i<len;++i) {
20401                 if (targets[i].id == oTargetDD.id) {
20402                     return true;
20403                 }
20404             }
20405
20406             return false;
20407         },
20408
20409         /**
20410          * My goal is to be able to transparently determine if an object is
20411          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
20412          * returns "object", oDD.constructor.toString() always returns
20413          * "DragDrop" and not the name of the subclass.  So for now it just
20414          * evaluates a well-known variable in DragDrop.
20415          * @method isTypeOfDD
20416          * @param {Object} the object to evaluate
20417          * @return {boolean} true if typeof oDD = DragDrop
20418          * @static
20419          */
20420         isTypeOfDD: function (oDD) {
20421             return (oDD && oDD.__ygDragDrop);
20422         },
20423
20424         /**
20425          * Utility function to determine if a given element has been
20426          * registered as a drag drop handle for the given Drag Drop object.
20427          * @method isHandle
20428          * @param {String} id the element id to check
20429          * @return {boolean} true if this element is a DragDrop handle, false
20430          * otherwise
20431          * @static
20432          */
20433         isHandle: function(sDDId, sHandleId) {
20434             return ( this.handleIds[sDDId] &&
20435                             this.handleIds[sDDId][sHandleId] );
20436         },
20437
20438         /**
20439          * Returns the DragDrop instance for a given id
20440          * @method getDDById
20441          * @param {String} id the id of the DragDrop object
20442          * @return {DragDrop} the drag drop object, null if it is not found
20443          * @static
20444          */
20445         getDDById: function(id) {
20446             for (var i in this.ids) {
20447                 if (this.ids[i][id]) {
20448                     return this.ids[i][id];
20449                 }
20450             }
20451             return null;
20452         },
20453
20454         /**
20455          * Fired after a registered DragDrop object gets the mousedown event.
20456          * Sets up the events required to track the object being dragged
20457          * @method handleMouseDown
20458          * @param {Event} e the event
20459          * @param oDD the DragDrop object being dragged
20460          * @private
20461          * @static
20462          */
20463         handleMouseDown: function(e, oDD) {
20464             if(Roo.QuickTips){
20465                 Roo.QuickTips.disable();
20466             }
20467             this.currentTarget = e.getTarget();
20468
20469             this.dragCurrent = oDD;
20470
20471             var el = oDD.getEl();
20472
20473             // track start position
20474             this.startX = e.getPageX();
20475             this.startY = e.getPageY();
20476
20477             this.deltaX = this.startX - el.offsetLeft;
20478             this.deltaY = this.startY - el.offsetTop;
20479
20480             this.dragThreshMet = false;
20481
20482             this.clickTimeout = setTimeout(
20483                     function() {
20484                         var DDM = Roo.dd.DDM;
20485                         DDM.startDrag(DDM.startX, DDM.startY);
20486                     },
20487                     this.clickTimeThresh );
20488         },
20489
20490         /**
20491          * Fired when either the drag pixel threshol or the mousedown hold
20492          * time threshold has been met.
20493          * @method startDrag
20494          * @param x {int} the X position of the original mousedown
20495          * @param y {int} the Y position of the original mousedown
20496          * @static
20497          */
20498         startDrag: function(x, y) {
20499             clearTimeout(this.clickTimeout);
20500             if (this.dragCurrent) {
20501                 this.dragCurrent.b4StartDrag(x, y);
20502                 this.dragCurrent.startDrag(x, y);
20503             }
20504             this.dragThreshMet = true;
20505         },
20506
20507         /**
20508          * Internal function to handle the mouseup event.  Will be invoked
20509          * from the context of the document.
20510          * @method handleMouseUp
20511          * @param {Event} e the event
20512          * @private
20513          * @static
20514          */
20515         handleMouseUp: function(e) {
20516
20517             if(Roo.QuickTips){
20518                 Roo.QuickTips.enable();
20519             }
20520             if (! this.dragCurrent) {
20521                 return;
20522             }
20523
20524             clearTimeout(this.clickTimeout);
20525
20526             if (this.dragThreshMet) {
20527                 this.fireEvents(e, true);
20528             } else {
20529             }
20530
20531             this.stopDrag(e);
20532
20533             this.stopEvent(e);
20534         },
20535
20536         /**
20537          * Utility to stop event propagation and event default, if these
20538          * features are turned on.
20539          * @method stopEvent
20540          * @param {Event} e the event as returned by this.getEvent()
20541          * @static
20542          */
20543         stopEvent: function(e){
20544             if(this.stopPropagation) {
20545                 e.stopPropagation();
20546             }
20547
20548             if (this.preventDefault) {
20549                 e.preventDefault();
20550             }
20551         },
20552
20553         /**
20554          * Internal function to clean up event handlers after the drag
20555          * operation is complete
20556          * @method stopDrag
20557          * @param {Event} e the event
20558          * @private
20559          * @static
20560          */
20561         stopDrag: function(e) {
20562             // Fire the drag end event for the item that was dragged
20563             if (this.dragCurrent) {
20564                 if (this.dragThreshMet) {
20565                     this.dragCurrent.b4EndDrag(e);
20566                     this.dragCurrent.endDrag(e);
20567                 }
20568
20569                 this.dragCurrent.onMouseUp(e);
20570             }
20571
20572             this.dragCurrent = null;
20573             this.dragOvers = {};
20574         },
20575
20576         /**
20577          * Internal function to handle the mousemove event.  Will be invoked
20578          * from the context of the html element.
20579          *
20580          * @TODO figure out what we can do about mouse events lost when the
20581          * user drags objects beyond the window boundary.  Currently we can
20582          * detect this in internet explorer by verifying that the mouse is
20583          * down during the mousemove event.  Firefox doesn't give us the
20584          * button state on the mousemove event.
20585          * @method handleMouseMove
20586          * @param {Event} e the event
20587          * @private
20588          * @static
20589          */
20590         handleMouseMove: function(e) {
20591             if (! this.dragCurrent) {
20592                 return true;
20593             }
20594
20595             // var button = e.which || e.button;
20596
20597             // check for IE mouseup outside of page boundary
20598             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
20599                 this.stopEvent(e);
20600                 return this.handleMouseUp(e);
20601             }
20602
20603             if (!this.dragThreshMet) {
20604                 var diffX = Math.abs(this.startX - e.getPageX());
20605                 var diffY = Math.abs(this.startY - e.getPageY());
20606                 if (diffX > this.clickPixelThresh ||
20607                             diffY > this.clickPixelThresh) {
20608                     this.startDrag(this.startX, this.startY);
20609                 }
20610             }
20611
20612             if (this.dragThreshMet) {
20613                 this.dragCurrent.b4Drag(e);
20614                 this.dragCurrent.onDrag(e);
20615                 if(!this.dragCurrent.moveOnly){
20616                     this.fireEvents(e, false);
20617                 }
20618             }
20619
20620             this.stopEvent(e);
20621
20622             return true;
20623         },
20624
20625         /**
20626          * Iterates over all of the DragDrop elements to find ones we are
20627          * hovering over or dropping on
20628          * @method fireEvents
20629          * @param {Event} e the event
20630          * @param {boolean} isDrop is this a drop op or a mouseover op?
20631          * @private
20632          * @static
20633          */
20634         fireEvents: function(e, isDrop) {
20635             var dc = this.dragCurrent;
20636
20637             // If the user did the mouse up outside of the window, we could
20638             // get here even though we have ended the drag.
20639             if (!dc || dc.isLocked()) {
20640                 return;
20641             }
20642
20643             var pt = e.getPoint();
20644
20645             // cache the previous dragOver array
20646             var oldOvers = [];
20647
20648             var outEvts   = [];
20649             var overEvts  = [];
20650             var dropEvts  = [];
20651             var enterEvts = [];
20652
20653             // Check to see if the object(s) we were hovering over is no longer
20654             // being hovered over so we can fire the onDragOut event
20655             for (var i in this.dragOvers) {
20656
20657                 var ddo = this.dragOvers[i];
20658
20659                 if (! this.isTypeOfDD(ddo)) {
20660                     continue;
20661                 }
20662
20663                 if (! this.isOverTarget(pt, ddo, this.mode)) {
20664                     outEvts.push( ddo );
20665                 }
20666
20667                 oldOvers[i] = true;
20668                 delete this.dragOvers[i];
20669             }
20670
20671             for (var sGroup in dc.groups) {
20672
20673                 if ("string" != typeof sGroup) {
20674                     continue;
20675                 }
20676
20677                 for (i in this.ids[sGroup]) {
20678                     var oDD = this.ids[sGroup][i];
20679                     if (! this.isTypeOfDD(oDD)) {
20680                         continue;
20681                     }
20682
20683                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
20684                         if (this.isOverTarget(pt, oDD, this.mode)) {
20685                             // look for drop interactions
20686                             if (isDrop) {
20687                                 dropEvts.push( oDD );
20688                             // look for drag enter and drag over interactions
20689                             } else {
20690
20691                                 // initial drag over: dragEnter fires
20692                                 if (!oldOvers[oDD.id]) {
20693                                     enterEvts.push( oDD );
20694                                 // subsequent drag overs: dragOver fires
20695                                 } else {
20696                                     overEvts.push( oDD );
20697                                 }
20698
20699                                 this.dragOvers[oDD.id] = oDD;
20700                             }
20701                         }
20702                     }
20703                 }
20704             }
20705
20706             if (this.mode) {
20707                 if (outEvts.length) {
20708                     dc.b4DragOut(e, outEvts);
20709                     dc.onDragOut(e, outEvts);
20710                 }
20711
20712                 if (enterEvts.length) {
20713                     dc.onDragEnter(e, enterEvts);
20714                 }
20715
20716                 if (overEvts.length) {
20717                     dc.b4DragOver(e, overEvts);
20718                     dc.onDragOver(e, overEvts);
20719                 }
20720
20721                 if (dropEvts.length) {
20722                     dc.b4DragDrop(e, dropEvts);
20723                     dc.onDragDrop(e, dropEvts);
20724                 }
20725
20726             } else {
20727                 // fire dragout events
20728                 var len = 0;
20729                 for (i=0, len=outEvts.length; i<len; ++i) {
20730                     dc.b4DragOut(e, outEvts[i].id);
20731                     dc.onDragOut(e, outEvts[i].id);
20732                 }
20733
20734                 // fire enter events
20735                 for (i=0,len=enterEvts.length; i<len; ++i) {
20736                     // dc.b4DragEnter(e, oDD.id);
20737                     dc.onDragEnter(e, enterEvts[i].id);
20738                 }
20739
20740                 // fire over events
20741                 for (i=0,len=overEvts.length; i<len; ++i) {
20742                     dc.b4DragOver(e, overEvts[i].id);
20743                     dc.onDragOver(e, overEvts[i].id);
20744                 }
20745
20746                 // fire drop events
20747                 for (i=0, len=dropEvts.length; i<len; ++i) {
20748                     dc.b4DragDrop(e, dropEvts[i].id);
20749                     dc.onDragDrop(e, dropEvts[i].id);
20750                 }
20751
20752             }
20753
20754             // notify about a drop that did not find a target
20755             if (isDrop && !dropEvts.length) {
20756                 dc.onInvalidDrop(e);
20757             }
20758
20759         },
20760
20761         /**
20762          * Helper function for getting the best match from the list of drag
20763          * and drop objects returned by the drag and drop events when we are
20764          * in INTERSECT mode.  It returns either the first object that the
20765          * cursor is over, or the object that has the greatest overlap with
20766          * the dragged element.
20767          * @method getBestMatch
20768          * @param  {DragDrop[]} dds The array of drag and drop objects
20769          * targeted
20770          * @return {DragDrop}       The best single match
20771          * @static
20772          */
20773         getBestMatch: function(dds) {
20774             var winner = null;
20775             // Return null if the input is not what we expect
20776             //if (!dds || !dds.length || dds.length == 0) {
20777                // winner = null;
20778             // If there is only one item, it wins
20779             //} else if (dds.length == 1) {
20780
20781             var len = dds.length;
20782
20783             if (len == 1) {
20784                 winner = dds[0];
20785             } else {
20786                 // Loop through the targeted items
20787                 for (var i=0; i<len; ++i) {
20788                     var dd = dds[i];
20789                     // If the cursor is over the object, it wins.  If the
20790                     // cursor is over multiple matches, the first one we come
20791                     // to wins.
20792                     if (dd.cursorIsOver) {
20793                         winner = dd;
20794                         break;
20795                     // Otherwise the object with the most overlap wins
20796                     } else {
20797                         if (!winner ||
20798                             winner.overlap.getArea() < dd.overlap.getArea()) {
20799                             winner = dd;
20800                         }
20801                     }
20802                 }
20803             }
20804
20805             return winner;
20806         },
20807
20808         /**
20809          * Refreshes the cache of the top-left and bottom-right points of the
20810          * drag and drop objects in the specified group(s).  This is in the
20811          * format that is stored in the drag and drop instance, so typical
20812          * usage is:
20813          * <code>
20814          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
20815          * </code>
20816          * Alternatively:
20817          * <code>
20818          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
20819          * </code>
20820          * @TODO this really should be an indexed array.  Alternatively this
20821          * method could accept both.
20822          * @method refreshCache
20823          * @param {Object} groups an associative array of groups to refresh
20824          * @static
20825          */
20826         refreshCache: function(groups) {
20827             for (var sGroup in groups) {
20828                 if ("string" != typeof sGroup) {
20829                     continue;
20830                 }
20831                 for (var i in this.ids[sGroup]) {
20832                     var oDD = this.ids[sGroup][i];
20833
20834                     if (this.isTypeOfDD(oDD)) {
20835                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
20836                         var loc = this.getLocation(oDD);
20837                         if (loc) {
20838                             this.locationCache[oDD.id] = loc;
20839                         } else {
20840                             delete this.locationCache[oDD.id];
20841                             // this will unregister the drag and drop object if
20842                             // the element is not in a usable state
20843                             // oDD.unreg();
20844                         }
20845                     }
20846                 }
20847             }
20848         },
20849
20850         /**
20851          * This checks to make sure an element exists and is in the DOM.  The
20852          * main purpose is to handle cases where innerHTML is used to remove
20853          * drag and drop objects from the DOM.  IE provides an 'unspecified
20854          * error' when trying to access the offsetParent of such an element
20855          * @method verifyEl
20856          * @param {HTMLElement} el the element to check
20857          * @return {boolean} true if the element looks usable
20858          * @static
20859          */
20860         verifyEl: function(el) {
20861             if (el) {
20862                 var parent;
20863                 if(Roo.isIE){
20864                     try{
20865                         parent = el.offsetParent;
20866                     }catch(e){}
20867                 }else{
20868                     parent = el.offsetParent;
20869                 }
20870                 if (parent) {
20871                     return true;
20872                 }
20873             }
20874
20875             return false;
20876         },
20877
20878         /**
20879          * Returns a Region object containing the drag and drop element's position
20880          * and size, including the padding configured for it
20881          * @method getLocation
20882          * @param {DragDrop} oDD the drag and drop object to get the
20883          *                       location for
20884          * @return {Roo.lib.Region} a Region object representing the total area
20885          *                             the element occupies, including any padding
20886          *                             the instance is configured for.
20887          * @static
20888          */
20889         getLocation: function(oDD) {
20890             if (! this.isTypeOfDD(oDD)) {
20891                 return null;
20892             }
20893
20894             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
20895
20896             try {
20897                 pos= Roo.lib.Dom.getXY(el);
20898             } catch (e) { }
20899
20900             if (!pos) {
20901                 return null;
20902             }
20903
20904             x1 = pos[0];
20905             x2 = x1 + el.offsetWidth;
20906             y1 = pos[1];
20907             y2 = y1 + el.offsetHeight;
20908
20909             t = y1 - oDD.padding[0];
20910             r = x2 + oDD.padding[1];
20911             b = y2 + oDD.padding[2];
20912             l = x1 - oDD.padding[3];
20913
20914             return new Roo.lib.Region( t, r, b, l );
20915         },
20916
20917         /**
20918          * Checks the cursor location to see if it over the target
20919          * @method isOverTarget
20920          * @param {Roo.lib.Point} pt The point to evaluate
20921          * @param {DragDrop} oTarget the DragDrop object we are inspecting
20922          * @return {boolean} true if the mouse is over the target
20923          * @private
20924          * @static
20925          */
20926         isOverTarget: function(pt, oTarget, intersect) {
20927             // use cache if available
20928             var loc = this.locationCache[oTarget.id];
20929             if (!loc || !this.useCache) {
20930                 loc = this.getLocation(oTarget);
20931                 this.locationCache[oTarget.id] = loc;
20932
20933             }
20934
20935             if (!loc) {
20936                 return false;
20937             }
20938
20939             oTarget.cursorIsOver = loc.contains( pt );
20940
20941             // DragDrop is using this as a sanity check for the initial mousedown
20942             // in this case we are done.  In POINT mode, if the drag obj has no
20943             // contraints, we are also done. Otherwise we need to evaluate the
20944             // location of the target as related to the actual location of the
20945             // dragged element.
20946             var dc = this.dragCurrent;
20947             if (!dc || !dc.getTargetCoord ||
20948                     (!intersect && !dc.constrainX && !dc.constrainY)) {
20949                 return oTarget.cursorIsOver;
20950             }
20951
20952             oTarget.overlap = null;
20953
20954             // Get the current location of the drag element, this is the
20955             // location of the mouse event less the delta that represents
20956             // where the original mousedown happened on the element.  We
20957             // need to consider constraints and ticks as well.
20958             var pos = dc.getTargetCoord(pt.x, pt.y);
20959
20960             var el = dc.getDragEl();
20961             var curRegion = new Roo.lib.Region( pos.y,
20962                                                    pos.x + el.offsetWidth,
20963                                                    pos.y + el.offsetHeight,
20964                                                    pos.x );
20965
20966             var overlap = curRegion.intersect(loc);
20967
20968             if (overlap) {
20969                 oTarget.overlap = overlap;
20970                 return (intersect) ? true : oTarget.cursorIsOver;
20971             } else {
20972                 return false;
20973             }
20974         },
20975
20976         /**
20977          * unload event handler
20978          * @method _onUnload
20979          * @private
20980          * @static
20981          */
20982         _onUnload: function(e, me) {
20983             Roo.dd.DragDropMgr.unregAll();
20984         },
20985
20986         /**
20987          * Cleans up the drag and drop events and objects.
20988          * @method unregAll
20989          * @private
20990          * @static
20991          */
20992         unregAll: function() {
20993
20994             if (this.dragCurrent) {
20995                 this.stopDrag();
20996                 this.dragCurrent = null;
20997             }
20998
20999             this._execOnAll("unreg", []);
21000
21001             for (i in this.elementCache) {
21002                 delete this.elementCache[i];
21003             }
21004
21005             this.elementCache = {};
21006             this.ids = {};
21007         },
21008
21009         /**
21010          * A cache of DOM elements
21011          * @property elementCache
21012          * @private
21013          * @static
21014          */
21015         elementCache: {},
21016
21017         /**
21018          * Get the wrapper for the DOM element specified
21019          * @method getElWrapper
21020          * @param {String} id the id of the element to get
21021          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21022          * @private
21023          * @deprecated This wrapper isn't that useful
21024          * @static
21025          */
21026         getElWrapper: function(id) {
21027             var oWrapper = this.elementCache[id];
21028             if (!oWrapper || !oWrapper.el) {
21029                 oWrapper = this.elementCache[id] =
21030                     new this.ElementWrapper(Roo.getDom(id));
21031             }
21032             return oWrapper;
21033         },
21034
21035         /**
21036          * Returns the actual DOM element
21037          * @method getElement
21038          * @param {String} id the id of the elment to get
21039          * @return {Object} The element
21040          * @deprecated use Roo.getDom instead
21041          * @static
21042          */
21043         getElement: function(id) {
21044             return Roo.getDom(id);
21045         },
21046
21047         /**
21048          * Returns the style property for the DOM element (i.e.,
21049          * document.getElById(id).style)
21050          * @method getCss
21051          * @param {String} id the id of the elment to get
21052          * @return {Object} The style property of the element
21053          * @deprecated use Roo.getDom instead
21054          * @static
21055          */
21056         getCss: function(id) {
21057             var el = Roo.getDom(id);
21058             return (el) ? el.style : null;
21059         },
21060
21061         /**
21062          * Inner class for cached elements
21063          * @class DragDropMgr.ElementWrapper
21064          * @for DragDropMgr
21065          * @private
21066          * @deprecated
21067          */
21068         ElementWrapper: function(el) {
21069                 /**
21070                  * The element
21071                  * @property el
21072                  */
21073                 this.el = el || null;
21074                 /**
21075                  * The element id
21076                  * @property id
21077                  */
21078                 this.id = this.el && el.id;
21079                 /**
21080                  * A reference to the style property
21081                  * @property css
21082                  */
21083                 this.css = this.el && el.style;
21084             },
21085
21086         /**
21087          * Returns the X position of an html element
21088          * @method getPosX
21089          * @param el the element for which to get the position
21090          * @return {int} the X coordinate
21091          * @for DragDropMgr
21092          * @deprecated use Roo.lib.Dom.getX instead
21093          * @static
21094          */
21095         getPosX: function(el) {
21096             return Roo.lib.Dom.getX(el);
21097         },
21098
21099         /**
21100          * Returns the Y position of an html element
21101          * @method getPosY
21102          * @param el the element for which to get the position
21103          * @return {int} the Y coordinate
21104          * @deprecated use Roo.lib.Dom.getY instead
21105          * @static
21106          */
21107         getPosY: function(el) {
21108             return Roo.lib.Dom.getY(el);
21109         },
21110
21111         /**
21112          * Swap two nodes.  In IE, we use the native method, for others we
21113          * emulate the IE behavior
21114          * @method swapNode
21115          * @param n1 the first node to swap
21116          * @param n2 the other node to swap
21117          * @static
21118          */
21119         swapNode: function(n1, n2) {
21120             if (n1.swapNode) {
21121                 n1.swapNode(n2);
21122             } else {
21123                 var p = n2.parentNode;
21124                 var s = n2.nextSibling;
21125
21126                 if (s == n1) {
21127                     p.insertBefore(n1, n2);
21128                 } else if (n2 == n1.nextSibling) {
21129                     p.insertBefore(n2, n1);
21130                 } else {
21131                     n1.parentNode.replaceChild(n2, n1);
21132                     p.insertBefore(n1, s);
21133                 }
21134             }
21135         },
21136
21137         /**
21138          * Returns the current scroll position
21139          * @method getScroll
21140          * @private
21141          * @static
21142          */
21143         getScroll: function () {
21144             var t, l, dde=document.documentElement, db=document.body;
21145             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21146                 t = dde.scrollTop;
21147                 l = dde.scrollLeft;
21148             } else if (db) {
21149                 t = db.scrollTop;
21150                 l = db.scrollLeft;
21151             } else {
21152
21153             }
21154             return { top: t, left: l };
21155         },
21156
21157         /**
21158          * Returns the specified element style property
21159          * @method getStyle
21160          * @param {HTMLElement} el          the element
21161          * @param {string}      styleProp   the style property
21162          * @return {string} The value of the style property
21163          * @deprecated use Roo.lib.Dom.getStyle
21164          * @static
21165          */
21166         getStyle: function(el, styleProp) {
21167             return Roo.fly(el).getStyle(styleProp);
21168         },
21169
21170         /**
21171          * Gets the scrollTop
21172          * @method getScrollTop
21173          * @return {int} the document's scrollTop
21174          * @static
21175          */
21176         getScrollTop: function () { return this.getScroll().top; },
21177
21178         /**
21179          * Gets the scrollLeft
21180          * @method getScrollLeft
21181          * @return {int} the document's scrollTop
21182          * @static
21183          */
21184         getScrollLeft: function () { return this.getScroll().left; },
21185
21186         /**
21187          * Sets the x/y position of an element to the location of the
21188          * target element.
21189          * @method moveToEl
21190          * @param {HTMLElement} moveEl      The element to move
21191          * @param {HTMLElement} targetEl    The position reference element
21192          * @static
21193          */
21194         moveToEl: function (moveEl, targetEl) {
21195             var aCoord = Roo.lib.Dom.getXY(targetEl);
21196             Roo.lib.Dom.setXY(moveEl, aCoord);
21197         },
21198
21199         /**
21200          * Numeric array sort function
21201          * @method numericSort
21202          * @static
21203          */
21204         numericSort: function(a, b) { return (a - b); },
21205
21206         /**
21207          * Internal counter
21208          * @property _timeoutCount
21209          * @private
21210          * @static
21211          */
21212         _timeoutCount: 0,
21213
21214         /**
21215          * Trying to make the load order less important.  Without this we get
21216          * an error if this file is loaded before the Event Utility.
21217          * @method _addListeners
21218          * @private
21219          * @static
21220          */
21221         _addListeners: function() {
21222             var DDM = Roo.dd.DDM;
21223             if ( Roo.lib.Event && document ) {
21224                 DDM._onLoad();
21225             } else {
21226                 if (DDM._timeoutCount > 2000) {
21227                 } else {
21228                     setTimeout(DDM._addListeners, 10);
21229                     if (document && document.body) {
21230                         DDM._timeoutCount += 1;
21231                     }
21232                 }
21233             }
21234         },
21235
21236         /**
21237          * Recursively searches the immediate parent and all child nodes for
21238          * the handle element in order to determine wheter or not it was
21239          * clicked.
21240          * @method handleWasClicked
21241          * @param node the html element to inspect
21242          * @static
21243          */
21244         handleWasClicked: function(node, id) {
21245             if (this.isHandle(id, node.id)) {
21246                 return true;
21247             } else {
21248                 // check to see if this is a text node child of the one we want
21249                 var p = node.parentNode;
21250
21251                 while (p) {
21252                     if (this.isHandle(id, p.id)) {
21253                         return true;
21254                     } else {
21255                         p = p.parentNode;
21256                     }
21257                 }
21258             }
21259
21260             return false;
21261         }
21262
21263     };
21264
21265 }();
21266
21267 // shorter alias, save a few bytes
21268 Roo.dd.DDM = Roo.dd.DragDropMgr;
21269 Roo.dd.DDM._addListeners();
21270
21271 }/*
21272  * Based on:
21273  * Ext JS Library 1.1.1
21274  * Copyright(c) 2006-2007, Ext JS, LLC.
21275  *
21276  * Originally Released Under LGPL - original licence link has changed is not relivant.
21277  *
21278  * Fork - LGPL
21279  * <script type="text/javascript">
21280  */
21281
21282 /**
21283  * @class Roo.dd.DD
21284  * A DragDrop implementation where the linked element follows the
21285  * mouse cursor during a drag.
21286  * @extends Roo.dd.DragDrop
21287  * @constructor
21288  * @param {String} id the id of the linked element
21289  * @param {String} sGroup the group of related DragDrop items
21290  * @param {object} config an object containing configurable attributes
21291  *                Valid properties for DD:
21292  *                    scroll
21293  */
21294 Roo.dd.DD = function(id, sGroup, config) {
21295     if (id) {
21296         this.init(id, sGroup, config);
21297     }
21298 };
21299
21300 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
21301
21302     /**
21303      * When set to true, the utility automatically tries to scroll the browser
21304      * window wehn a drag and drop element is dragged near the viewport boundary.
21305      * Defaults to true.
21306      * @property scroll
21307      * @type boolean
21308      */
21309     scroll: true,
21310
21311     /**
21312      * Sets the pointer offset to the distance between the linked element's top
21313      * left corner and the location the element was clicked
21314      * @method autoOffset
21315      * @param {int} iPageX the X coordinate of the click
21316      * @param {int} iPageY the Y coordinate of the click
21317      */
21318     autoOffset: function(iPageX, iPageY) {
21319         var x = iPageX - this.startPageX;
21320         var y = iPageY - this.startPageY;
21321         this.setDelta(x, y);
21322     },
21323
21324     /**
21325      * Sets the pointer offset.  You can call this directly to force the
21326      * offset to be in a particular location (e.g., pass in 0,0 to set it
21327      * to the center of the object)
21328      * @method setDelta
21329      * @param {int} iDeltaX the distance from the left
21330      * @param {int} iDeltaY the distance from the top
21331      */
21332     setDelta: function(iDeltaX, iDeltaY) {
21333         this.deltaX = iDeltaX;
21334         this.deltaY = iDeltaY;
21335     },
21336
21337     /**
21338      * Sets the drag element to the location of the mousedown or click event,
21339      * maintaining the cursor location relative to the location on the element
21340      * that was clicked.  Override this if you want to place the element in a
21341      * location other than where the cursor is.
21342      * @method setDragElPos
21343      * @param {int} iPageX the X coordinate of the mousedown or drag event
21344      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21345      */
21346     setDragElPos: function(iPageX, iPageY) {
21347         // the first time we do this, we are going to check to make sure
21348         // the element has css positioning
21349
21350         var el = this.getDragEl();
21351         this.alignElWithMouse(el, iPageX, iPageY);
21352     },
21353
21354     /**
21355      * Sets the element to the location of the mousedown or click event,
21356      * maintaining the cursor location relative to the location on the element
21357      * that was clicked.  Override this if you want to place the element in a
21358      * location other than where the cursor is.
21359      * @method alignElWithMouse
21360      * @param {HTMLElement} el the element to move
21361      * @param {int} iPageX the X coordinate of the mousedown or drag event
21362      * @param {int} iPageY the Y coordinate of the mousedown or drag event
21363      */
21364     alignElWithMouse: function(el, iPageX, iPageY) {
21365         var oCoord = this.getTargetCoord(iPageX, iPageY);
21366         var fly = el.dom ? el : Roo.fly(el);
21367         if (!this.deltaSetXY) {
21368             var aCoord = [oCoord.x, oCoord.y];
21369             fly.setXY(aCoord);
21370             var newLeft = fly.getLeft(true);
21371             var newTop  = fly.getTop(true);
21372             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
21373         } else {
21374             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
21375         }
21376
21377         this.cachePosition(oCoord.x, oCoord.y);
21378         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
21379         return oCoord;
21380     },
21381
21382     /**
21383      * Saves the most recent position so that we can reset the constraints and
21384      * tick marks on-demand.  We need to know this so that we can calculate the
21385      * number of pixels the element is offset from its original position.
21386      * @method cachePosition
21387      * @param iPageX the current x position (optional, this just makes it so we
21388      * don't have to look it up again)
21389      * @param iPageY the current y position (optional, this just makes it so we
21390      * don't have to look it up again)
21391      */
21392     cachePosition: function(iPageX, iPageY) {
21393         if (iPageX) {
21394             this.lastPageX = iPageX;
21395             this.lastPageY = iPageY;
21396         } else {
21397             var aCoord = Roo.lib.Dom.getXY(this.getEl());
21398             this.lastPageX = aCoord[0];
21399             this.lastPageY = aCoord[1];
21400         }
21401     },
21402
21403     /**
21404      * Auto-scroll the window if the dragged object has been moved beyond the
21405      * visible window boundary.
21406      * @method autoScroll
21407      * @param {int} x the drag element's x position
21408      * @param {int} y the drag element's y position
21409      * @param {int} h the height of the drag element
21410      * @param {int} w the width of the drag element
21411      * @private
21412      */
21413     autoScroll: function(x, y, h, w) {
21414
21415         if (this.scroll) {
21416             // The client height
21417             var clientH = Roo.lib.Dom.getViewWidth();
21418
21419             // The client width
21420             var clientW = Roo.lib.Dom.getViewHeight();
21421
21422             // The amt scrolled down
21423             var st = this.DDM.getScrollTop();
21424
21425             // The amt scrolled right
21426             var sl = this.DDM.getScrollLeft();
21427
21428             // Location of the bottom of the element
21429             var bot = h + y;
21430
21431             // Location of the right of the element
21432             var right = w + x;
21433
21434             // The distance from the cursor to the bottom of the visible area,
21435             // adjusted so that we don't scroll if the cursor is beyond the
21436             // element drag constraints
21437             var toBot = (clientH + st - y - this.deltaY);
21438
21439             // The distance from the cursor to the right of the visible area
21440             var toRight = (clientW + sl - x - this.deltaX);
21441
21442
21443             // How close to the edge the cursor must be before we scroll
21444             // var thresh = (document.all) ? 100 : 40;
21445             var thresh = 40;
21446
21447             // How many pixels to scroll per autoscroll op.  This helps to reduce
21448             // clunky scrolling. IE is more sensitive about this ... it needs this
21449             // value to be higher.
21450             var scrAmt = (document.all) ? 80 : 30;
21451
21452             // Scroll down if we are near the bottom of the visible page and the
21453             // obj extends below the crease
21454             if ( bot > clientH && toBot < thresh ) {
21455                 window.scrollTo(sl, st + scrAmt);
21456             }
21457
21458             // Scroll up if the window is scrolled down and the top of the object
21459             // goes above the top border
21460             if ( y < st && st > 0 && y - st < thresh ) {
21461                 window.scrollTo(sl, st - scrAmt);
21462             }
21463
21464             // Scroll right if the obj is beyond the right border and the cursor is
21465             // near the border.
21466             if ( right > clientW && toRight < thresh ) {
21467                 window.scrollTo(sl + scrAmt, st);
21468             }
21469
21470             // Scroll left if the window has been scrolled to the right and the obj
21471             // extends past the left border
21472             if ( x < sl && sl > 0 && x - sl < thresh ) {
21473                 window.scrollTo(sl - scrAmt, st);
21474             }
21475         }
21476     },
21477
21478     /**
21479      * Finds the location the element should be placed if we want to move
21480      * it to where the mouse location less the click offset would place us.
21481      * @method getTargetCoord
21482      * @param {int} iPageX the X coordinate of the click
21483      * @param {int} iPageY the Y coordinate of the click
21484      * @return an object that contains the coordinates (Object.x and Object.y)
21485      * @private
21486      */
21487     getTargetCoord: function(iPageX, iPageY) {
21488
21489
21490         var x = iPageX - this.deltaX;
21491         var y = iPageY - this.deltaY;
21492
21493         if (this.constrainX) {
21494             if (x < this.minX) { x = this.minX; }
21495             if (x > this.maxX) { x = this.maxX; }
21496         }
21497
21498         if (this.constrainY) {
21499             if (y < this.minY) { y = this.minY; }
21500             if (y > this.maxY) { y = this.maxY; }
21501         }
21502
21503         x = this.getTick(x, this.xTicks);
21504         y = this.getTick(y, this.yTicks);
21505
21506
21507         return {x:x, y:y};
21508     },
21509
21510     /*
21511      * Sets up config options specific to this class. Overrides
21512      * Roo.dd.DragDrop, but all versions of this method through the
21513      * inheritance chain are called
21514      */
21515     applyConfig: function() {
21516         Roo.dd.DD.superclass.applyConfig.call(this);
21517         this.scroll = (this.config.scroll !== false);
21518     },
21519
21520     /*
21521      * Event that fires prior to the onMouseDown event.  Overrides
21522      * Roo.dd.DragDrop.
21523      */
21524     b4MouseDown: function(e) {
21525         // this.resetConstraints();
21526         this.autoOffset(e.getPageX(),
21527                             e.getPageY());
21528     },
21529
21530     /*
21531      * Event that fires prior to the onDrag event.  Overrides
21532      * Roo.dd.DragDrop.
21533      */
21534     b4Drag: function(e) {
21535         this.setDragElPos(e.getPageX(),
21536                             e.getPageY());
21537     },
21538
21539     toString: function() {
21540         return ("DD " + this.id);
21541     }
21542
21543     //////////////////////////////////////////////////////////////////////////
21544     // Debugging ygDragDrop events that can be overridden
21545     //////////////////////////////////////////////////////////////////////////
21546     /*
21547     startDrag: function(x, y) {
21548     },
21549
21550     onDrag: function(e) {
21551     },
21552
21553     onDragEnter: function(e, id) {
21554     },
21555
21556     onDragOver: function(e, id) {
21557     },
21558
21559     onDragOut: function(e, id) {
21560     },
21561
21562     onDragDrop: function(e, id) {
21563     },
21564
21565     endDrag: function(e) {
21566     }
21567
21568     */
21569
21570 });/*
21571  * Based on:
21572  * Ext JS Library 1.1.1
21573  * Copyright(c) 2006-2007, Ext JS, LLC.
21574  *
21575  * Originally Released Under LGPL - original licence link has changed is not relivant.
21576  *
21577  * Fork - LGPL
21578  * <script type="text/javascript">
21579  */
21580
21581 /**
21582  * @class Roo.dd.DDProxy
21583  * A DragDrop implementation that inserts an empty, bordered div into
21584  * the document that follows the cursor during drag operations.  At the time of
21585  * the click, the frame div is resized to the dimensions of the linked html
21586  * element, and moved to the exact location of the linked element.
21587  *
21588  * References to the "frame" element refer to the single proxy element that
21589  * was created to be dragged in place of all DDProxy elements on the
21590  * page.
21591  *
21592  * @extends Roo.dd.DD
21593  * @constructor
21594  * @param {String} id the id of the linked html element
21595  * @param {String} sGroup the group of related DragDrop objects
21596  * @param {object} config an object containing configurable attributes
21597  *                Valid properties for DDProxy in addition to those in DragDrop:
21598  *                   resizeFrame, centerFrame, dragElId
21599  */
21600 Roo.dd.DDProxy = function(id, sGroup, config) {
21601     if (id) {
21602         this.init(id, sGroup, config);
21603         this.initFrame();
21604     }
21605 };
21606
21607 /**
21608  * The default drag frame div id
21609  * @property Roo.dd.DDProxy.dragElId
21610  * @type String
21611  * @static
21612  */
21613 Roo.dd.DDProxy.dragElId = "ygddfdiv";
21614
21615 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
21616
21617     /**
21618      * By default we resize the drag frame to be the same size as the element
21619      * we want to drag (this is to get the frame effect).  We can turn it off
21620      * if we want a different behavior.
21621      * @property resizeFrame
21622      * @type boolean
21623      */
21624     resizeFrame: true,
21625
21626     /**
21627      * By default the frame is positioned exactly where the drag element is, so
21628      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
21629      * you do not have constraints on the obj is to have the drag frame centered
21630      * around the cursor.  Set centerFrame to true for this effect.
21631      * @property centerFrame
21632      * @type boolean
21633      */
21634     centerFrame: false,
21635
21636     /**
21637      * Creates the proxy element if it does not yet exist
21638      * @method createFrame
21639      */
21640     createFrame: function() {
21641         var self = this;
21642         var body = document.body;
21643
21644         if (!body || !body.firstChild) {
21645             setTimeout( function() { self.createFrame(); }, 50 );
21646             return;
21647         }
21648
21649         var div = this.getDragEl();
21650
21651         if (!div) {
21652             div    = document.createElement("div");
21653             div.id = this.dragElId;
21654             var s  = div.style;
21655
21656             s.position   = "absolute";
21657             s.visibility = "hidden";
21658             s.cursor     = "move";
21659             s.border     = "2px solid #aaa";
21660             s.zIndex     = 999;
21661
21662             // appendChild can blow up IE if invoked prior to the window load event
21663             // while rendering a table.  It is possible there are other scenarios
21664             // that would cause this to happen as well.
21665             body.insertBefore(div, body.firstChild);
21666         }
21667     },
21668
21669     /**
21670      * Initialization for the drag frame element.  Must be called in the
21671      * constructor of all subclasses
21672      * @method initFrame
21673      */
21674     initFrame: function() {
21675         this.createFrame();
21676     },
21677
21678     applyConfig: function() {
21679         Roo.dd.DDProxy.superclass.applyConfig.call(this);
21680
21681         this.resizeFrame = (this.config.resizeFrame !== false);
21682         this.centerFrame = (this.config.centerFrame);
21683         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
21684     },
21685
21686     /**
21687      * Resizes the drag frame to the dimensions of the clicked object, positions
21688      * it over the object, and finally displays it
21689      * @method showFrame
21690      * @param {int} iPageX X click position
21691      * @param {int} iPageY Y click position
21692      * @private
21693      */
21694     showFrame: function(iPageX, iPageY) {
21695         var el = this.getEl();
21696         var dragEl = this.getDragEl();
21697         var s = dragEl.style;
21698
21699         this._resizeProxy();
21700
21701         if (this.centerFrame) {
21702             this.setDelta( Math.round(parseInt(s.width,  10)/2),
21703                            Math.round(parseInt(s.height, 10)/2) );
21704         }
21705
21706         this.setDragElPos(iPageX, iPageY);
21707
21708         Roo.fly(dragEl).show();
21709     },
21710
21711     /**
21712      * The proxy is automatically resized to the dimensions of the linked
21713      * element when a drag is initiated, unless resizeFrame is set to false
21714      * @method _resizeProxy
21715      * @private
21716      */
21717     _resizeProxy: function() {
21718         if (this.resizeFrame) {
21719             var el = this.getEl();
21720             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
21721         }
21722     },
21723
21724     // overrides Roo.dd.DragDrop
21725     b4MouseDown: function(e) {
21726         var x = e.getPageX();
21727         var y = e.getPageY();
21728         this.autoOffset(x, y);
21729         this.setDragElPos(x, y);
21730     },
21731
21732     // overrides Roo.dd.DragDrop
21733     b4StartDrag: function(x, y) {
21734         // show the drag frame
21735         this.showFrame(x, y);
21736     },
21737
21738     // overrides Roo.dd.DragDrop
21739     b4EndDrag: function(e) {
21740         Roo.fly(this.getDragEl()).hide();
21741     },
21742
21743     // overrides Roo.dd.DragDrop
21744     // By default we try to move the element to the last location of the frame.
21745     // This is so that the default behavior mirrors that of Roo.dd.DD.
21746     endDrag: function(e) {
21747
21748         var lel = this.getEl();
21749         var del = this.getDragEl();
21750
21751         // Show the drag frame briefly so we can get its position
21752         del.style.visibility = "";
21753
21754         this.beforeMove();
21755         // Hide the linked element before the move to get around a Safari
21756         // rendering bug.
21757         lel.style.visibility = "hidden";
21758         Roo.dd.DDM.moveToEl(lel, del);
21759         del.style.visibility = "hidden";
21760         lel.style.visibility = "";
21761
21762         this.afterDrag();
21763     },
21764
21765     beforeMove : function(){
21766
21767     },
21768
21769     afterDrag : function(){
21770
21771     },
21772
21773     toString: function() {
21774         return ("DDProxy " + this.id);
21775     }
21776
21777 });
21778 /*
21779  * Based on:
21780  * Ext JS Library 1.1.1
21781  * Copyright(c) 2006-2007, Ext JS, LLC.
21782  *
21783  * Originally Released Under LGPL - original licence link has changed is not relivant.
21784  *
21785  * Fork - LGPL
21786  * <script type="text/javascript">
21787  */
21788
21789  /**
21790  * @class Roo.dd.DDTarget
21791  * A DragDrop implementation that does not move, but can be a drop
21792  * target.  You would get the same result by simply omitting implementation
21793  * for the event callbacks, but this way we reduce the processing cost of the
21794  * event listener and the callbacks.
21795  * @extends Roo.dd.DragDrop
21796  * @constructor
21797  * @param {String} id the id of the element that is a drop target
21798  * @param {String} sGroup the group of related DragDrop objects
21799  * @param {object} config an object containing configurable attributes
21800  *                 Valid properties for DDTarget in addition to those in
21801  *                 DragDrop:
21802  *                    none
21803  */
21804 Roo.dd.DDTarget = function(id, sGroup, config) {
21805     if (id) {
21806         this.initTarget(id, sGroup, config);
21807     }
21808     if (config && (config.listeners || config.events)) { 
21809         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
21810             listeners : config.listeners || {}, 
21811             events : config.events || {} 
21812         });    
21813     }
21814 };
21815
21816 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
21817 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
21818     toString: function() {
21819         return ("DDTarget " + this.id);
21820     }
21821 });
21822 /*
21823  * Based on:
21824  * Ext JS Library 1.1.1
21825  * Copyright(c) 2006-2007, Ext JS, LLC.
21826  *
21827  * Originally Released Under LGPL - original licence link has changed is not relivant.
21828  *
21829  * Fork - LGPL
21830  * <script type="text/javascript">
21831  */
21832  
21833
21834 /**
21835  * @class Roo.dd.ScrollManager
21836  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
21837  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
21838  * @singleton
21839  */
21840 Roo.dd.ScrollManager = function(){
21841     var ddm = Roo.dd.DragDropMgr;
21842     var els = {};
21843     var dragEl = null;
21844     var proc = {};
21845     
21846     
21847     
21848     var onStop = function(e){
21849         dragEl = null;
21850         clearProc();
21851     };
21852     
21853     var triggerRefresh = function(){
21854         if(ddm.dragCurrent){
21855              ddm.refreshCache(ddm.dragCurrent.groups);
21856         }
21857     };
21858     
21859     var doScroll = function(){
21860         if(ddm.dragCurrent){
21861             var dds = Roo.dd.ScrollManager;
21862             if(!dds.animate){
21863                 if(proc.el.scroll(proc.dir, dds.increment)){
21864                     triggerRefresh();
21865                 }
21866             }else{
21867                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
21868             }
21869         }
21870     };
21871     
21872     var clearProc = function(){
21873         if(proc.id){
21874             clearInterval(proc.id);
21875         }
21876         proc.id = 0;
21877         proc.el = null;
21878         proc.dir = "";
21879     };
21880     
21881     var startProc = function(el, dir){
21882          Roo.log('scroll startproc');
21883         clearProc();
21884         proc.el = el;
21885         proc.dir = dir;
21886         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
21887     };
21888     
21889     var onFire = function(e, isDrop){
21890        
21891         if(isDrop || !ddm.dragCurrent){ return; }
21892         var dds = Roo.dd.ScrollManager;
21893         if(!dragEl || dragEl != ddm.dragCurrent){
21894             dragEl = ddm.dragCurrent;
21895             // refresh regions on drag start
21896             dds.refreshCache();
21897         }
21898         
21899         var xy = Roo.lib.Event.getXY(e);
21900         var pt = new Roo.lib.Point(xy[0], xy[1]);
21901         for(var id in els){
21902             var el = els[id], r = el._region;
21903             if(r && r.contains(pt) && el.isScrollable()){
21904                 if(r.bottom - pt.y <= dds.thresh){
21905                     if(proc.el != el){
21906                         startProc(el, "down");
21907                     }
21908                     return;
21909                 }else if(r.right - pt.x <= dds.thresh){
21910                     if(proc.el != el){
21911                         startProc(el, "left");
21912                     }
21913                     return;
21914                 }else if(pt.y - r.top <= dds.thresh){
21915                     if(proc.el != el){
21916                         startProc(el, "up");
21917                     }
21918                     return;
21919                 }else if(pt.x - r.left <= dds.thresh){
21920                     if(proc.el != el){
21921                         startProc(el, "right");
21922                     }
21923                     return;
21924                 }
21925             }
21926         }
21927         clearProc();
21928     };
21929     
21930     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
21931     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
21932     
21933     return {
21934         /**
21935          * Registers new overflow element(s) to auto scroll
21936          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
21937          */
21938         register : function(el){
21939             if(el instanceof Array){
21940                 for(var i = 0, len = el.length; i < len; i++) {
21941                         this.register(el[i]);
21942                 }
21943             }else{
21944                 el = Roo.get(el);
21945                 els[el.id] = el;
21946             }
21947             Roo.dd.ScrollManager.els = els;
21948         },
21949         
21950         /**
21951          * Unregisters overflow element(s) so they are no longer scrolled
21952          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
21953          */
21954         unregister : function(el){
21955             if(el instanceof Array){
21956                 for(var i = 0, len = el.length; i < len; i++) {
21957                         this.unregister(el[i]);
21958                 }
21959             }else{
21960                 el = Roo.get(el);
21961                 delete els[el.id];
21962             }
21963         },
21964         
21965         /**
21966          * The number of pixels from the edge of a container the pointer needs to be to 
21967          * trigger scrolling (defaults to 25)
21968          * @type Number
21969          */
21970         thresh : 25,
21971         
21972         /**
21973          * The number of pixels to scroll in each scroll increment (defaults to 50)
21974          * @type Number
21975          */
21976         increment : 100,
21977         
21978         /**
21979          * The frequency of scrolls in milliseconds (defaults to 500)
21980          * @type Number
21981          */
21982         frequency : 500,
21983         
21984         /**
21985          * True to animate the scroll (defaults to true)
21986          * @type Boolean
21987          */
21988         animate: true,
21989         
21990         /**
21991          * The animation duration in seconds - 
21992          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
21993          * @type Number
21994          */
21995         animDuration: .4,
21996         
21997         /**
21998          * Manually trigger a cache refresh.
21999          */
22000         refreshCache : function(){
22001             for(var id in els){
22002                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22003                     els[id]._region = els[id].getRegion();
22004                 }
22005             }
22006         }
22007     };
22008 }();/*
22009  * Based on:
22010  * Ext JS Library 1.1.1
22011  * Copyright(c) 2006-2007, Ext JS, LLC.
22012  *
22013  * Originally Released Under LGPL - original licence link has changed is not relivant.
22014  *
22015  * Fork - LGPL
22016  * <script type="text/javascript">
22017  */
22018  
22019
22020 /**
22021  * @class Roo.dd.Registry
22022  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22023  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22024  * @singleton
22025  */
22026 Roo.dd.Registry = function(){
22027     var elements = {}; 
22028     var handles = {}; 
22029     var autoIdSeed = 0;
22030
22031     var getId = function(el, autogen){
22032         if(typeof el == "string"){
22033             return el;
22034         }
22035         var id = el.id;
22036         if(!id && autogen !== false){
22037             id = "roodd-" + (++autoIdSeed);
22038             el.id = id;
22039         }
22040         return id;
22041     };
22042     
22043     return {
22044     /**
22045      * Register a drag drop element
22046      * @param {String|HTMLElement} element The id or DOM node to register
22047      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22048      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22049      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22050      * populated in the data object (if applicable):
22051      * <pre>
22052 Value      Description<br />
22053 ---------  ------------------------------------------<br />
22054 handles    Array of DOM nodes that trigger dragging<br />
22055            for the element being registered<br />
22056 isHandle   True if the element passed in triggers<br />
22057            dragging itself, else false
22058 </pre>
22059      */
22060         register : function(el, data){
22061             data = data || {};
22062             if(typeof el == "string"){
22063                 el = document.getElementById(el);
22064             }
22065             data.ddel = el;
22066             elements[getId(el)] = data;
22067             if(data.isHandle !== false){
22068                 handles[data.ddel.id] = data;
22069             }
22070             if(data.handles){
22071                 var hs = data.handles;
22072                 for(var i = 0, len = hs.length; i < len; i++){
22073                         handles[getId(hs[i])] = data;
22074                 }
22075             }
22076         },
22077
22078     /**
22079      * Unregister a drag drop element
22080      * @param {String|HTMLElement}  element The id or DOM node to unregister
22081      */
22082         unregister : function(el){
22083             var id = getId(el, false);
22084             var data = elements[id];
22085             if(data){
22086                 delete elements[id];
22087                 if(data.handles){
22088                     var hs = data.handles;
22089                     for(var i = 0, len = hs.length; i < len; i++){
22090                         delete handles[getId(hs[i], false)];
22091                     }
22092                 }
22093             }
22094         },
22095
22096     /**
22097      * Returns the handle registered for a DOM Node by id
22098      * @param {String|HTMLElement} id The DOM node or id to look up
22099      * @return {Object} handle The custom handle data
22100      */
22101         getHandle : function(id){
22102             if(typeof id != "string"){ // must be element?
22103                 id = id.id;
22104             }
22105             return handles[id];
22106         },
22107
22108     /**
22109      * Returns the handle that is registered for the DOM node that is the target of the event
22110      * @param {Event} e The event
22111      * @return {Object} handle The custom handle data
22112      */
22113         getHandleFromEvent : function(e){
22114             var t = Roo.lib.Event.getTarget(e);
22115             return t ? handles[t.id] : null;
22116         },
22117
22118     /**
22119      * Returns a custom data object that is registered for a DOM node by id
22120      * @param {String|HTMLElement} id The DOM node or id to look up
22121      * @return {Object} data The custom data
22122      */
22123         getTarget : function(id){
22124             if(typeof id != "string"){ // must be element?
22125                 id = id.id;
22126             }
22127             return elements[id];
22128         },
22129
22130     /**
22131      * Returns a custom data object that is registered for the DOM node that is the target of the event
22132      * @param {Event} e The event
22133      * @return {Object} data The custom data
22134      */
22135         getTargetFromEvent : function(e){
22136             var t = Roo.lib.Event.getTarget(e);
22137             return t ? elements[t.id] || handles[t.id] : null;
22138         }
22139     };
22140 }();/*
22141  * Based on:
22142  * Ext JS Library 1.1.1
22143  * Copyright(c) 2006-2007, Ext JS, LLC.
22144  *
22145  * Originally Released Under LGPL - original licence link has changed is not relivant.
22146  *
22147  * Fork - LGPL
22148  * <script type="text/javascript">
22149  */
22150  
22151
22152 /**
22153  * @class Roo.dd.StatusProxy
22154  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22155  * default drag proxy used by all Roo.dd components.
22156  * @constructor
22157  * @param {Object} config
22158  */
22159 Roo.dd.StatusProxy = function(config){
22160     Roo.apply(this, config);
22161     this.id = this.id || Roo.id();
22162     this.el = new Roo.Layer({
22163         dh: {
22164             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22165                 {tag: "div", cls: "x-dd-drop-icon"},
22166                 {tag: "div", cls: "x-dd-drag-ghost"}
22167             ]
22168         }, 
22169         shadow: !config || config.shadow !== false
22170     });
22171     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22172     this.dropStatus = this.dropNotAllowed;
22173 };
22174
22175 Roo.dd.StatusProxy.prototype = {
22176     /**
22177      * @cfg {String} dropAllowed
22178      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22179      */
22180     dropAllowed : "x-dd-drop-ok",
22181     /**
22182      * @cfg {String} dropNotAllowed
22183      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22184      */
22185     dropNotAllowed : "x-dd-drop-nodrop",
22186
22187     /**
22188      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22189      * over the current target element.
22190      * @param {String} cssClass The css class for the new drop status indicator image
22191      */
22192     setStatus : function(cssClass){
22193         cssClass = cssClass || this.dropNotAllowed;
22194         if(this.dropStatus != cssClass){
22195             this.el.replaceClass(this.dropStatus, cssClass);
22196             this.dropStatus = cssClass;
22197         }
22198     },
22199
22200     /**
22201      * Resets the status indicator to the default dropNotAllowed value
22202      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22203      */
22204     reset : function(clearGhost){
22205         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22206         this.dropStatus = this.dropNotAllowed;
22207         if(clearGhost){
22208             this.ghost.update("");
22209         }
22210     },
22211
22212     /**
22213      * Updates the contents of the ghost element
22214      * @param {String} html The html that will replace the current innerHTML of the ghost element
22215      */
22216     update : function(html){
22217         if(typeof html == "string"){
22218             this.ghost.update(html);
22219         }else{
22220             this.ghost.update("");
22221             html.style.margin = "0";
22222             this.ghost.dom.appendChild(html);
22223         }
22224         // ensure float = none set?? cant remember why though.
22225         var el = this.ghost.dom.firstChild;
22226                 if(el){
22227                         Roo.fly(el).setStyle('float', 'none');
22228                 }
22229     },
22230     
22231     /**
22232      * Returns the underlying proxy {@link Roo.Layer}
22233      * @return {Roo.Layer} el
22234     */
22235     getEl : function(){
22236         return this.el;
22237     },
22238
22239     /**
22240      * Returns the ghost element
22241      * @return {Roo.Element} el
22242      */
22243     getGhost : function(){
22244         return this.ghost;
22245     },
22246
22247     /**
22248      * Hides the proxy
22249      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22250      */
22251     hide : function(clear){
22252         this.el.hide();
22253         if(clear){
22254             this.reset(true);
22255         }
22256     },
22257
22258     /**
22259      * Stops the repair animation if it's currently running
22260      */
22261     stop : function(){
22262         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22263             this.anim.stop();
22264         }
22265     },
22266
22267     /**
22268      * Displays this proxy
22269      */
22270     show : function(){
22271         this.el.show();
22272     },
22273
22274     /**
22275      * Force the Layer to sync its shadow and shim positions to the element
22276      */
22277     sync : function(){
22278         this.el.sync();
22279     },
22280
22281     /**
22282      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
22283      * invalid drop operation by the item being dragged.
22284      * @param {Array} xy The XY position of the element ([x, y])
22285      * @param {Function} callback The function to call after the repair is complete
22286      * @param {Object} scope The scope in which to execute the callback
22287      */
22288     repair : function(xy, callback, scope){
22289         this.callback = callback;
22290         this.scope = scope;
22291         if(xy && this.animRepair !== false){
22292             this.el.addClass("x-dd-drag-repair");
22293             this.el.hideUnders(true);
22294             this.anim = this.el.shift({
22295                 duration: this.repairDuration || .5,
22296                 easing: 'easeOut',
22297                 xy: xy,
22298                 stopFx: true,
22299                 callback: this.afterRepair,
22300                 scope: this
22301             });
22302         }else{
22303             this.afterRepair();
22304         }
22305     },
22306
22307     // private
22308     afterRepair : function(){
22309         this.hide(true);
22310         if(typeof this.callback == "function"){
22311             this.callback.call(this.scope || this);
22312         }
22313         this.callback = null;
22314         this.scope = null;
22315     }
22316 };/*
22317  * Based on:
22318  * Ext JS Library 1.1.1
22319  * Copyright(c) 2006-2007, Ext JS, LLC.
22320  *
22321  * Originally Released Under LGPL - original licence link has changed is not relivant.
22322  *
22323  * Fork - LGPL
22324  * <script type="text/javascript">
22325  */
22326
22327 /**
22328  * @class Roo.dd.DragSource
22329  * @extends Roo.dd.DDProxy
22330  * A simple class that provides the basic implementation needed to make any element draggable.
22331  * @constructor
22332  * @param {String/HTMLElement/Element} el The container element
22333  * @param {Object} config
22334  */
22335 Roo.dd.DragSource = function(el, config){
22336     this.el = Roo.get(el);
22337     this.dragData = {};
22338     
22339     Roo.apply(this, config);
22340     
22341     if(!this.proxy){
22342         this.proxy = new Roo.dd.StatusProxy();
22343     }
22344
22345     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
22346           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
22347     
22348     this.dragging = false;
22349 };
22350
22351 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
22352     /**
22353      * @cfg {String} dropAllowed
22354      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22355      */
22356     dropAllowed : "x-dd-drop-ok",
22357     /**
22358      * @cfg {String} dropNotAllowed
22359      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22360      */
22361     dropNotAllowed : "x-dd-drop-nodrop",
22362
22363     /**
22364      * Returns the data object associated with this drag source
22365      * @return {Object} data An object containing arbitrary data
22366      */
22367     getDragData : function(e){
22368         return this.dragData;
22369     },
22370
22371     // private
22372     onDragEnter : function(e, id){
22373         var target = Roo.dd.DragDropMgr.getDDById(id);
22374         this.cachedTarget = target;
22375         if(this.beforeDragEnter(target, e, id) !== false){
22376             if(target.isNotifyTarget){
22377                 var status = target.notifyEnter(this, e, this.dragData);
22378                 this.proxy.setStatus(status);
22379             }else{
22380                 this.proxy.setStatus(this.dropAllowed);
22381             }
22382             
22383             if(this.afterDragEnter){
22384                 /**
22385                  * An empty function by default, but provided so that you can perform a custom action
22386                  * when the dragged item enters the drop target by providing an implementation.
22387                  * @param {Roo.dd.DragDrop} target The drop target
22388                  * @param {Event} e The event object
22389                  * @param {String} id The id of the dragged element
22390                  * @method afterDragEnter
22391                  */
22392                 this.afterDragEnter(target, e, id);
22393             }
22394         }
22395     },
22396
22397     /**
22398      * An empty function by default, but provided so that you can perform a custom action
22399      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
22400      * @param {Roo.dd.DragDrop} target The drop target
22401      * @param {Event} e The event object
22402      * @param {String} id The id of the dragged element
22403      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22404      */
22405     beforeDragEnter : function(target, e, id){
22406         return true;
22407     },
22408
22409     // private
22410     alignElWithMouse: function() {
22411         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
22412         this.proxy.sync();
22413     },
22414
22415     // private
22416     onDragOver : function(e, id){
22417         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22418         if(this.beforeDragOver(target, e, id) !== false){
22419             if(target.isNotifyTarget){
22420                 var status = target.notifyOver(this, e, this.dragData);
22421                 this.proxy.setStatus(status);
22422             }
22423
22424             if(this.afterDragOver){
22425                 /**
22426                  * An empty function by default, but provided so that you can perform a custom action
22427                  * while the dragged item is over the drop target by providing an implementation.
22428                  * @param {Roo.dd.DragDrop} target The drop target
22429                  * @param {Event} e The event object
22430                  * @param {String} id The id of the dragged element
22431                  * @method afterDragOver
22432                  */
22433                 this.afterDragOver(target, e, id);
22434             }
22435         }
22436     },
22437
22438     /**
22439      * An empty function by default, but provided so that you can perform a custom action
22440      * while the dragged item is over the drop target and optionally cancel the onDragOver.
22441      * @param {Roo.dd.DragDrop} target The drop target
22442      * @param {Event} e The event object
22443      * @param {String} id The id of the dragged element
22444      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22445      */
22446     beforeDragOver : function(target, e, id){
22447         return true;
22448     },
22449
22450     // private
22451     onDragOut : function(e, id){
22452         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22453         if(this.beforeDragOut(target, e, id) !== false){
22454             if(target.isNotifyTarget){
22455                 target.notifyOut(this, e, this.dragData);
22456             }
22457             this.proxy.reset();
22458             if(this.afterDragOut){
22459                 /**
22460                  * An empty function by default, but provided so that you can perform a custom action
22461                  * after the dragged item is dragged out of the target without dropping.
22462                  * @param {Roo.dd.DragDrop} target The drop target
22463                  * @param {Event} e The event object
22464                  * @param {String} id The id of the dragged element
22465                  * @method afterDragOut
22466                  */
22467                 this.afterDragOut(target, e, id);
22468             }
22469         }
22470         this.cachedTarget = null;
22471     },
22472
22473     /**
22474      * An empty function by default, but provided so that you can perform a custom action before the dragged
22475      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
22476      * @param {Roo.dd.DragDrop} target The drop target
22477      * @param {Event} e The event object
22478      * @param {String} id The id of the dragged element
22479      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22480      */
22481     beforeDragOut : function(target, e, id){
22482         return true;
22483     },
22484     
22485     // private
22486     onDragDrop : function(e, id){
22487         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
22488         if(this.beforeDragDrop(target, e, id) !== false){
22489             if(target.isNotifyTarget){
22490                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
22491                     this.onValidDrop(target, e, id);
22492                 }else{
22493                     this.onInvalidDrop(target, e, id);
22494                 }
22495             }else{
22496                 this.onValidDrop(target, e, id);
22497             }
22498             
22499             if(this.afterDragDrop){
22500                 /**
22501                  * An empty function by default, but provided so that you can perform a custom action
22502                  * after a valid drag drop has occurred by providing an implementation.
22503                  * @param {Roo.dd.DragDrop} target The drop target
22504                  * @param {Event} e The event object
22505                  * @param {String} id The id of the dropped element
22506                  * @method afterDragDrop
22507                  */
22508                 this.afterDragDrop(target, e, id);
22509             }
22510         }
22511         delete this.cachedTarget;
22512     },
22513
22514     /**
22515      * An empty function by default, but provided so that you can perform a custom action before the dragged
22516      * item is dropped onto the target and optionally cancel the onDragDrop.
22517      * @param {Roo.dd.DragDrop} target The drop target
22518      * @param {Event} e The event object
22519      * @param {String} id The id of the dragged element
22520      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
22521      */
22522     beforeDragDrop : function(target, e, id){
22523         return true;
22524     },
22525
22526     // private
22527     onValidDrop : function(target, e, id){
22528         this.hideProxy();
22529         if(this.afterValidDrop){
22530             /**
22531              * An empty function by default, but provided so that you can perform a custom action
22532              * after a valid drop has occurred by providing an implementation.
22533              * @param {Object} target The target DD 
22534              * @param {Event} e The event object
22535              * @param {String} id The id of the dropped element
22536              * @method afterInvalidDrop
22537              */
22538             this.afterValidDrop(target, e, id);
22539         }
22540     },
22541
22542     // private
22543     getRepairXY : function(e, data){
22544         return this.el.getXY();  
22545     },
22546
22547     // private
22548     onInvalidDrop : function(target, e, id){
22549         this.beforeInvalidDrop(target, e, id);
22550         if(this.cachedTarget){
22551             if(this.cachedTarget.isNotifyTarget){
22552                 this.cachedTarget.notifyOut(this, e, this.dragData);
22553             }
22554             this.cacheTarget = null;
22555         }
22556         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
22557
22558         if(this.afterInvalidDrop){
22559             /**
22560              * An empty function by default, but provided so that you can perform a custom action
22561              * after an invalid drop has occurred by providing an implementation.
22562              * @param {Event} e The event object
22563              * @param {String} id The id of the dropped element
22564              * @method afterInvalidDrop
22565              */
22566             this.afterInvalidDrop(e, id);
22567         }
22568     },
22569
22570     // private
22571     afterRepair : function(){
22572         if(Roo.enableFx){
22573             this.el.highlight(this.hlColor || "c3daf9");
22574         }
22575         this.dragging = false;
22576     },
22577
22578     /**
22579      * An empty function by default, but provided so that you can perform a custom action after an invalid
22580      * drop has occurred.
22581      * @param {Roo.dd.DragDrop} target The drop target
22582      * @param {Event} e The event object
22583      * @param {String} id The id of the dragged element
22584      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
22585      */
22586     beforeInvalidDrop : function(target, e, id){
22587         return true;
22588     },
22589
22590     // private
22591     handleMouseDown : function(e){
22592         if(this.dragging) {
22593             return;
22594         }
22595         var data = this.getDragData(e);
22596         if(data && this.onBeforeDrag(data, e) !== false){
22597             this.dragData = data;
22598             this.proxy.stop();
22599             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
22600         } 
22601     },
22602
22603     /**
22604      * An empty function by default, but provided so that you can perform a custom action before the initial
22605      * drag event begins and optionally cancel it.
22606      * @param {Object} data An object containing arbitrary data to be shared with drop targets
22607      * @param {Event} e The event object
22608      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
22609      */
22610     onBeforeDrag : function(data, e){
22611         return true;
22612     },
22613
22614     /**
22615      * An empty function by default, but provided so that you can perform a custom action once the initial
22616      * drag event has begun.  The drag cannot be canceled from this function.
22617      * @param {Number} x The x position of the click on the dragged object
22618      * @param {Number} y The y position of the click on the dragged object
22619      */
22620     onStartDrag : Roo.emptyFn,
22621
22622     // private - YUI override
22623     startDrag : function(x, y){
22624         this.proxy.reset();
22625         this.dragging = true;
22626         this.proxy.update("");
22627         this.onInitDrag(x, y);
22628         this.proxy.show();
22629     },
22630
22631     // private
22632     onInitDrag : function(x, y){
22633         var clone = this.el.dom.cloneNode(true);
22634         clone.id = Roo.id(); // prevent duplicate ids
22635         this.proxy.update(clone);
22636         this.onStartDrag(x, y);
22637         return true;
22638     },
22639
22640     /**
22641      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
22642      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
22643      */
22644     getProxy : function(){
22645         return this.proxy;  
22646     },
22647
22648     /**
22649      * Hides the drag source's {@link Roo.dd.StatusProxy}
22650      */
22651     hideProxy : function(){
22652         this.proxy.hide();  
22653         this.proxy.reset(true);
22654         this.dragging = false;
22655     },
22656
22657     // private
22658     triggerCacheRefresh : function(){
22659         Roo.dd.DDM.refreshCache(this.groups);
22660     },
22661
22662     // private - override to prevent hiding
22663     b4EndDrag: function(e) {
22664     },
22665
22666     // private - override to prevent moving
22667     endDrag : function(e){
22668         this.onEndDrag(this.dragData, e);
22669     },
22670
22671     // private
22672     onEndDrag : function(data, e){
22673     },
22674     
22675     // private - pin to cursor
22676     autoOffset : function(x, y) {
22677         this.setDelta(-12, -20);
22678     }    
22679 });/*
22680  * Based on:
22681  * Ext JS Library 1.1.1
22682  * Copyright(c) 2006-2007, Ext JS, LLC.
22683  *
22684  * Originally Released Under LGPL - original licence link has changed is not relivant.
22685  *
22686  * Fork - LGPL
22687  * <script type="text/javascript">
22688  */
22689
22690
22691 /**
22692  * @class Roo.dd.DropTarget
22693  * @extends Roo.dd.DDTarget
22694  * A simple class that provides the basic implementation needed to make any element a drop target that can have
22695  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
22696  * @constructor
22697  * @param {String/HTMLElement/Element} el The container element
22698  * @param {Object} config
22699  */
22700 Roo.dd.DropTarget = function(el, config){
22701     this.el = Roo.get(el);
22702     
22703     var listeners = false; ;
22704     if (config && config.listeners) {
22705         listeners= config.listeners;
22706         delete config.listeners;
22707     }
22708     Roo.apply(this, config);
22709     
22710     if(this.containerScroll){
22711         Roo.dd.ScrollManager.register(this.el);
22712     }
22713     this.addEvents( {
22714          /**
22715          * @scope Roo.dd.DropTarget
22716          */
22717          
22718          /**
22719          * @event enter
22720          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
22721          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
22722          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
22723          * 
22724          * IMPORTANT : it should set  this.valid to true|false
22725          * 
22726          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22727          * @param {Event} e The event
22728          * @param {Object} data An object containing arbitrary data supplied by the drag source
22729          */
22730         "enter" : true,
22731         
22732          /**
22733          * @event over
22734          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
22735          * This method will be called on every mouse movement while the drag source is over the drop target.
22736          * This default implementation simply returns the dropAllowed config value.
22737          * 
22738          * IMPORTANT : it should set  this.valid to true|false
22739          * 
22740          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22741          * @param {Event} e The event
22742          * @param {Object} data An object containing arbitrary data supplied by the drag source
22743          
22744          */
22745         "over" : true,
22746         /**
22747          * @event out
22748          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
22749          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
22750          * overClass (if any) from the drop element.
22751          * 
22752          * 
22753          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22754          * @param {Event} e The event
22755          * @param {Object} data An object containing arbitrary data supplied by the drag source
22756          */
22757          "out" : true,
22758          
22759         /**
22760          * @event drop
22761          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
22762          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
22763          * implementation that does something to process the drop event and returns true so that the drag source's
22764          * repair action does not run.
22765          * 
22766          * IMPORTANT : it should set this.success
22767          * 
22768          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
22769          * @param {Event} e The event
22770          * @param {Object} data An object containing arbitrary data supplied by the drag source
22771         */
22772          "drop" : true
22773     });
22774             
22775      
22776     Roo.dd.DropTarget.superclass.constructor.call(  this, 
22777         this.el.dom, 
22778         this.ddGroup || this.group,
22779         {
22780             isTarget: true,
22781             listeners : listeners || {} 
22782            
22783         
22784         }
22785     );
22786
22787 };
22788
22789 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
22790     /**
22791      * @cfg {String} overClass
22792      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
22793      */
22794      /**
22795      * @cfg {String} ddGroup
22796      * The drag drop group to handle drop events for
22797      */
22798      
22799     /**
22800      * @cfg {String} dropAllowed
22801      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
22802      */
22803     dropAllowed : "x-dd-drop-ok",
22804     /**
22805      * @cfg {String} dropNotAllowed
22806      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
22807      */
22808     dropNotAllowed : "x-dd-drop-nodrop",
22809     /**
22810      * @cfg {boolean} success
22811      * set this after drop listener.. 
22812      */
22813     success : false,
22814     /**
22815      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
22816      * if the drop point is valid for over/enter..
22817      */
22818     valid : false,
22819     // private
22820     isTarget : true,
22821
22822     // private
22823     isNotifyTarget : true,
22824     
22825     /**
22826      * @hide
22827      */
22828     notifyEnter : function(dd, e, data)
22829     {
22830         this.valid = true;
22831         this.fireEvent('enter', dd, e, data);
22832         if(this.overClass){
22833             this.el.addClass(this.overClass);
22834         }
22835         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22836             this.valid ? this.dropAllowed : this.dropNotAllowed
22837         );
22838     },
22839
22840     /**
22841      * @hide
22842      */
22843     notifyOver : function(dd, e, data)
22844     {
22845         this.valid = true;
22846         this.fireEvent('over', dd, e, data);
22847         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
22848             this.valid ? this.dropAllowed : this.dropNotAllowed
22849         );
22850     },
22851
22852     /**
22853      * @hide
22854      */
22855     notifyOut : function(dd, e, data)
22856     {
22857         this.fireEvent('out', dd, e, data);
22858         if(this.overClass){
22859             this.el.removeClass(this.overClass);
22860         }
22861     },
22862
22863     /**
22864      * @hide
22865      */
22866     notifyDrop : function(dd, e, data)
22867     {
22868         this.success = false;
22869         this.fireEvent('drop', dd, e, data);
22870         return this.success;
22871     }
22872 });/*
22873  * Based on:
22874  * Ext JS Library 1.1.1
22875  * Copyright(c) 2006-2007, Ext JS, LLC.
22876  *
22877  * Originally Released Under LGPL - original licence link has changed is not relivant.
22878  *
22879  * Fork - LGPL
22880  * <script type="text/javascript">
22881  */
22882
22883
22884 /**
22885  * @class Roo.dd.DragZone
22886  * @extends Roo.dd.DragSource
22887  * This class provides a container DD instance that proxies for multiple child node sources.<br />
22888  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
22889  * @constructor
22890  * @param {String/HTMLElement/Element} el The container element
22891  * @param {Object} config
22892  */
22893 Roo.dd.DragZone = function(el, config){
22894     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
22895     if(this.containerScroll){
22896         Roo.dd.ScrollManager.register(this.el);
22897     }
22898 };
22899
22900 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
22901     /**
22902      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
22903      * for auto scrolling during drag operations.
22904      */
22905     /**
22906      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
22907      * method after a failed drop (defaults to "c3daf9" - light blue)
22908      */
22909
22910     /**
22911      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
22912      * for a valid target to drag based on the mouse down. Override this method
22913      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
22914      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
22915      * @param {EventObject} e The mouse down event
22916      * @return {Object} The dragData
22917      */
22918     getDragData : function(e){
22919         return Roo.dd.Registry.getHandleFromEvent(e);
22920     },
22921     
22922     /**
22923      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
22924      * this.dragData.ddel
22925      * @param {Number} x The x position of the click on the dragged object
22926      * @param {Number} y The y position of the click on the dragged object
22927      * @return {Boolean} true to continue the drag, false to cancel
22928      */
22929     onInitDrag : function(x, y){
22930         this.proxy.update(this.dragData.ddel.cloneNode(true));
22931         this.onStartDrag(x, y);
22932         return true;
22933     },
22934     
22935     /**
22936      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
22937      */
22938     afterRepair : function(){
22939         if(Roo.enableFx){
22940             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
22941         }
22942         this.dragging = false;
22943     },
22944
22945     /**
22946      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
22947      * the XY of this.dragData.ddel
22948      * @param {EventObject} e The mouse up event
22949      * @return {Array} The xy location (e.g. [100, 200])
22950      */
22951     getRepairXY : function(e){
22952         return Roo.Element.fly(this.dragData.ddel).getXY();  
22953     }
22954 });/*
22955  * Based on:
22956  * Ext JS Library 1.1.1
22957  * Copyright(c) 2006-2007, Ext JS, LLC.
22958  *
22959  * Originally Released Under LGPL - original licence link has changed is not relivant.
22960  *
22961  * Fork - LGPL
22962  * <script type="text/javascript">
22963  */
22964 /**
22965  * @class Roo.dd.DropZone
22966  * @extends Roo.dd.DropTarget
22967  * This class provides a container DD instance that proxies for multiple child node targets.<br />
22968  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
22969  * @constructor
22970  * @param {String/HTMLElement/Element} el The container element
22971  * @param {Object} config
22972  */
22973 Roo.dd.DropZone = function(el, config){
22974     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
22975 };
22976
22977 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
22978     /**
22979      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
22980      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
22981      * provide your own custom lookup.
22982      * @param {Event} e The event
22983      * @return {Object} data The custom data
22984      */
22985     getTargetFromEvent : function(e){
22986         return Roo.dd.Registry.getTargetFromEvent(e);
22987     },
22988
22989     /**
22990      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
22991      * that it has registered.  This method has no default implementation and should be overridden to provide
22992      * node-specific processing if necessary.
22993      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
22994      * {@link #getTargetFromEvent} for this node)
22995      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
22996      * @param {Event} e The event
22997      * @param {Object} data An object containing arbitrary data supplied by the drag source
22998      */
22999     onNodeEnter : function(n, dd, e, data){
23000         
23001     },
23002
23003     /**
23004      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23005      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23006      * overridden to provide the proper feedback.
23007      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23008      * {@link #getTargetFromEvent} for this node)
23009      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23010      * @param {Event} e The event
23011      * @param {Object} data An object containing arbitrary data supplied by the drag source
23012      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23013      * underlying {@link Roo.dd.StatusProxy} can be updated
23014      */
23015     onNodeOver : function(n, dd, e, data){
23016         return this.dropAllowed;
23017     },
23018
23019     /**
23020      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23021      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23022      * node-specific processing if necessary.
23023      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23024      * {@link #getTargetFromEvent} for this node)
23025      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23026      * @param {Event} e The event
23027      * @param {Object} data An object containing arbitrary data supplied by the drag source
23028      */
23029     onNodeOut : function(n, dd, e, data){
23030         
23031     },
23032
23033     /**
23034      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23035      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23036      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23037      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23038      * {@link #getTargetFromEvent} for this node)
23039      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23040      * @param {Event} e The event
23041      * @param {Object} data An object containing arbitrary data supplied by the drag source
23042      * @return {Boolean} True if the drop was valid, else false
23043      */
23044     onNodeDrop : function(n, dd, e, data){
23045         return false;
23046     },
23047
23048     /**
23049      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23050      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23051      * it should be overridden to provide the proper feedback if necessary.
23052      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23053      * @param {Event} e The event
23054      * @param {Object} data An object containing arbitrary data supplied by the drag source
23055      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23056      * underlying {@link Roo.dd.StatusProxy} can be updated
23057      */
23058     onContainerOver : function(dd, e, data){
23059         return this.dropNotAllowed;
23060     },
23061
23062     /**
23063      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23064      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23065      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23066      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23067      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23068      * @param {Event} e The event
23069      * @param {Object} data An object containing arbitrary data supplied by the drag source
23070      * @return {Boolean} True if the drop was valid, else false
23071      */
23072     onContainerDrop : function(dd, e, data){
23073         return false;
23074     },
23075
23076     /**
23077      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23078      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23079      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23080      * you should override this method and provide a custom implementation.
23081      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23082      * @param {Event} e The event
23083      * @param {Object} data An object containing arbitrary data supplied by the drag source
23084      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23085      * underlying {@link Roo.dd.StatusProxy} can be updated
23086      */
23087     notifyEnter : function(dd, e, data){
23088         return this.dropNotAllowed;
23089     },
23090
23091     /**
23092      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23093      * This method will be called on every mouse movement while the drag source is over the drop zone.
23094      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23095      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23096      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23097      * registered node, it will call {@link #onContainerOver}.
23098      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23099      * @param {Event} e The event
23100      * @param {Object} data An object containing arbitrary data supplied by the drag source
23101      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23102      * underlying {@link Roo.dd.StatusProxy} can be updated
23103      */
23104     notifyOver : function(dd, e, data){
23105         var n = this.getTargetFromEvent(e);
23106         if(!n){ // not over valid drop target
23107             if(this.lastOverNode){
23108                 this.onNodeOut(this.lastOverNode, dd, e, data);
23109                 this.lastOverNode = null;
23110             }
23111             return this.onContainerOver(dd, e, data);
23112         }
23113         if(this.lastOverNode != n){
23114             if(this.lastOverNode){
23115                 this.onNodeOut(this.lastOverNode, dd, e, data);
23116             }
23117             this.onNodeEnter(n, dd, e, data);
23118             this.lastOverNode = n;
23119         }
23120         return this.onNodeOver(n, dd, e, data);
23121     },
23122
23123     /**
23124      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23125      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23126      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23127      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23128      * @param {Event} e The event
23129      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23130      */
23131     notifyOut : function(dd, e, data){
23132         if(this.lastOverNode){
23133             this.onNodeOut(this.lastOverNode, dd, e, data);
23134             this.lastOverNode = null;
23135         }
23136     },
23137
23138     /**
23139      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23140      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23141      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23142      * otherwise it will call {@link #onContainerDrop}.
23143      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23144      * @param {Event} e The event
23145      * @param {Object} data An object containing arbitrary data supplied by the drag source
23146      * @return {Boolean} True if the drop was valid, else false
23147      */
23148     notifyDrop : function(dd, e, data){
23149         if(this.lastOverNode){
23150             this.onNodeOut(this.lastOverNode, dd, e, data);
23151             this.lastOverNode = null;
23152         }
23153         var n = this.getTargetFromEvent(e);
23154         return n ?
23155             this.onNodeDrop(n, dd, e, data) :
23156             this.onContainerDrop(dd, e, data);
23157     },
23158
23159     // private
23160     triggerCacheRefresh : function(){
23161         Roo.dd.DDM.refreshCache(this.groups);
23162     }  
23163 });/*
23164  * Based on:
23165  * Ext JS Library 1.1.1
23166  * Copyright(c) 2006-2007, Ext JS, LLC.
23167  *
23168  * Originally Released Under LGPL - original licence link has changed is not relivant.
23169  *
23170  * Fork - LGPL
23171  * <script type="text/javascript">
23172  */
23173
23174
23175 /**
23176  * @class Roo.data.SortTypes
23177  * @singleton
23178  * Defines the default sorting (casting?) comparison functions used when sorting data.
23179  */
23180 Roo.data.SortTypes = {
23181     /**
23182      * Default sort that does nothing
23183      * @param {Mixed} s The value being converted
23184      * @return {Mixed} The comparison value
23185      */
23186     none : function(s){
23187         return s;
23188     },
23189     
23190     /**
23191      * The regular expression used to strip tags
23192      * @type {RegExp}
23193      * @property
23194      */
23195     stripTagsRE : /<\/?[^>]+>/gi,
23196     
23197     /**
23198      * Strips all HTML tags to sort on text only
23199      * @param {Mixed} s The value being converted
23200      * @return {String} The comparison value
23201      */
23202     asText : function(s){
23203         return String(s).replace(this.stripTagsRE, "");
23204     },
23205     
23206     /**
23207      * Strips all HTML tags to sort on text only - Case insensitive
23208      * @param {Mixed} s The value being converted
23209      * @return {String} The comparison value
23210      */
23211     asUCText : function(s){
23212         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23213     },
23214     
23215     /**
23216      * Case insensitive string
23217      * @param {Mixed} s The value being converted
23218      * @return {String} The comparison value
23219      */
23220     asUCString : function(s) {
23221         return String(s).toUpperCase();
23222     },
23223     
23224     /**
23225      * Date sorting
23226      * @param {Mixed} s The value being converted
23227      * @return {Number} The comparison value
23228      */
23229     asDate : function(s) {
23230         if(!s){
23231             return 0;
23232         }
23233         if(s instanceof Date){
23234             return s.getTime();
23235         }
23236         return Date.parse(String(s));
23237     },
23238     
23239     /**
23240      * Float sorting
23241      * @param {Mixed} s The value being converted
23242      * @return {Float} The comparison value
23243      */
23244     asFloat : function(s) {
23245         var val = parseFloat(String(s).replace(/,/g, ""));
23246         if(isNaN(val)) {
23247             val = 0;
23248         }
23249         return val;
23250     },
23251     
23252     /**
23253      * Integer sorting
23254      * @param {Mixed} s The value being converted
23255      * @return {Number} The comparison value
23256      */
23257     asInt : function(s) {
23258         var val = parseInt(String(s).replace(/,/g, ""));
23259         if(isNaN(val)) {
23260             val = 0;
23261         }
23262         return val;
23263     }
23264 };/*
23265  * Based on:
23266  * Ext JS Library 1.1.1
23267  * Copyright(c) 2006-2007, Ext JS, LLC.
23268  *
23269  * Originally Released Under LGPL - original licence link has changed is not relivant.
23270  *
23271  * Fork - LGPL
23272  * <script type="text/javascript">
23273  */
23274
23275 /**
23276 * @class Roo.data.Record
23277  * Instances of this class encapsulate both record <em>definition</em> information, and record
23278  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
23279  * to access Records cached in an {@link Roo.data.Store} object.<br>
23280  * <p>
23281  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
23282  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
23283  * objects.<br>
23284  * <p>
23285  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
23286  * @constructor
23287  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
23288  * {@link #create}. The parameters are the same.
23289  * @param {Array} data An associative Array of data values keyed by the field name.
23290  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
23291  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
23292  * not specified an integer id is generated.
23293  */
23294 Roo.data.Record = function(data, id){
23295     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
23296     this.data = data;
23297 };
23298
23299 /**
23300  * Generate a constructor for a specific record layout.
23301  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
23302  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
23303  * Each field definition object may contain the following properties: <ul>
23304  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
23305  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
23306  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
23307  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
23308  * is being used, then this is a string containing the javascript expression to reference the data relative to 
23309  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
23310  * to the data item relative to the record element. If the mapping expression is the same as the field name,
23311  * this may be omitted.</p></li>
23312  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
23313  * <ul><li>auto (Default, implies no conversion)</li>
23314  * <li>string</li>
23315  * <li>int</li>
23316  * <li>float</li>
23317  * <li>boolean</li>
23318  * <li>date</li></ul></p></li>
23319  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
23320  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
23321  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
23322  * by the Reader into an object that will be stored in the Record. It is passed the
23323  * following parameters:<ul>
23324  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
23325  * </ul></p></li>
23326  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
23327  * </ul>
23328  * <br>usage:<br><pre><code>
23329 var TopicRecord = Roo.data.Record.create(
23330     {name: 'title', mapping: 'topic_title'},
23331     {name: 'author', mapping: 'username'},
23332     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
23333     {name: 'lastPost', mapping: 'post_time', type: 'date'},
23334     {name: 'lastPoster', mapping: 'user2'},
23335     {name: 'excerpt', mapping: 'post_text'}
23336 );
23337
23338 var myNewRecord = new TopicRecord({
23339     title: 'Do my job please',
23340     author: 'noobie',
23341     totalPosts: 1,
23342     lastPost: new Date(),
23343     lastPoster: 'Animal',
23344     excerpt: 'No way dude!'
23345 });
23346 myStore.add(myNewRecord);
23347 </code></pre>
23348  * @method create
23349  * @static
23350  */
23351 Roo.data.Record.create = function(o){
23352     var f = function(){
23353         f.superclass.constructor.apply(this, arguments);
23354     };
23355     Roo.extend(f, Roo.data.Record);
23356     var p = f.prototype;
23357     p.fields = new Roo.util.MixedCollection(false, function(field){
23358         return field.name;
23359     });
23360     for(var i = 0, len = o.length; i < len; i++){
23361         p.fields.add(new Roo.data.Field(o[i]));
23362     }
23363     f.getField = function(name){
23364         return p.fields.get(name);  
23365     };
23366     return f;
23367 };
23368
23369 Roo.data.Record.AUTO_ID = 1000;
23370 Roo.data.Record.EDIT = 'edit';
23371 Roo.data.Record.REJECT = 'reject';
23372 Roo.data.Record.COMMIT = 'commit';
23373
23374 Roo.data.Record.prototype = {
23375     /**
23376      * Readonly flag - true if this record has been modified.
23377      * @type Boolean
23378      */
23379     dirty : false,
23380     editing : false,
23381     error: null,
23382     modified: null,
23383
23384     // private
23385     join : function(store){
23386         this.store = store;
23387     },
23388
23389     /**
23390      * Set the named field to the specified value.
23391      * @param {String} name The name of the field to set.
23392      * @param {Object} value The value to set the field to.
23393      */
23394     set : function(name, value){
23395         if(this.data[name] == value){
23396             return;
23397         }
23398         this.dirty = true;
23399         if(!this.modified){
23400             this.modified = {};
23401         }
23402         if(typeof this.modified[name] == 'undefined'){
23403             this.modified[name] = this.data[name];
23404         }
23405         this.data[name] = value;
23406         if(!this.editing && this.store){
23407             this.store.afterEdit(this);
23408         }       
23409     },
23410
23411     /**
23412      * Get the value of the named field.
23413      * @param {String} name The name of the field to get the value of.
23414      * @return {Object} The value of the field.
23415      */
23416     get : function(name){
23417         return this.data[name]; 
23418     },
23419
23420     // private
23421     beginEdit : function(){
23422         this.editing = true;
23423         this.modified = {}; 
23424     },
23425
23426     // private
23427     cancelEdit : function(){
23428         this.editing = false;
23429         delete this.modified;
23430     },
23431
23432     // private
23433     endEdit : function(){
23434         this.editing = false;
23435         if(this.dirty && this.store){
23436             this.store.afterEdit(this);
23437         }
23438     },
23439
23440     /**
23441      * Usually called by the {@link Roo.data.Store} which owns the Record.
23442      * Rejects all changes made to the Record since either creation, or the last commit operation.
23443      * Modified fields are reverted to their original values.
23444      * <p>
23445      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23446      * of reject operations.
23447      */
23448     reject : function(){
23449         var m = this.modified;
23450         for(var n in m){
23451             if(typeof m[n] != "function"){
23452                 this.data[n] = m[n];
23453             }
23454         }
23455         this.dirty = false;
23456         delete this.modified;
23457         this.editing = false;
23458         if(this.store){
23459             this.store.afterReject(this);
23460         }
23461     },
23462
23463     /**
23464      * Usually called by the {@link Roo.data.Store} which owns the Record.
23465      * Commits all changes made to the Record since either creation, or the last commit operation.
23466      * <p>
23467      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
23468      * of commit operations.
23469      */
23470     commit : function(){
23471         this.dirty = false;
23472         delete this.modified;
23473         this.editing = false;
23474         if(this.store){
23475             this.store.afterCommit(this);
23476         }
23477     },
23478
23479     // private
23480     hasError : function(){
23481         return this.error != null;
23482     },
23483
23484     // private
23485     clearError : function(){
23486         this.error = null;
23487     },
23488
23489     /**
23490      * Creates a copy of this record.
23491      * @param {String} id (optional) A new record id if you don't want to use this record's id
23492      * @return {Record}
23493      */
23494     copy : function(newId) {
23495         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
23496     }
23497 };/*
23498  * Based on:
23499  * Ext JS Library 1.1.1
23500  * Copyright(c) 2006-2007, Ext JS, LLC.
23501  *
23502  * Originally Released Under LGPL - original licence link has changed is not relivant.
23503  *
23504  * Fork - LGPL
23505  * <script type="text/javascript">
23506  */
23507
23508
23509
23510 /**
23511  * @class Roo.data.Store
23512  * @extends Roo.util.Observable
23513  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
23514  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
23515  * <p>
23516  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
23517  * has no knowledge of the format of the data returned by the Proxy.<br>
23518  * <p>
23519  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
23520  * instances from the data object. These records are cached and made available through accessor functions.
23521  * @constructor
23522  * Creates a new Store.
23523  * @param {Object} config A config object containing the objects needed for the Store to access data,
23524  * and read the data into Records.
23525  */
23526 Roo.data.Store = function(config){
23527     this.data = new Roo.util.MixedCollection(false);
23528     this.data.getKey = function(o){
23529         return o.id;
23530     };
23531     this.baseParams = {};
23532     // private
23533     this.paramNames = {
23534         "start" : "start",
23535         "limit" : "limit",
23536         "sort" : "sort",
23537         "dir" : "dir",
23538         "multisort" : "_multisort"
23539     };
23540
23541     if(config && config.data){
23542         this.inlineData = config.data;
23543         delete config.data;
23544     }
23545
23546     Roo.apply(this, config);
23547     
23548     if(this.reader){ // reader passed
23549         this.reader = Roo.factory(this.reader, Roo.data);
23550         this.reader.xmodule = this.xmodule || false;
23551         if(!this.recordType){
23552             this.recordType = this.reader.recordType;
23553         }
23554         if(this.reader.onMetaChange){
23555             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
23556         }
23557     }
23558
23559     if(this.recordType){
23560         this.fields = this.recordType.prototype.fields;
23561     }
23562     this.modified = [];
23563
23564     this.addEvents({
23565         /**
23566          * @event datachanged
23567          * Fires when the data cache has changed, and a widget which is using this Store
23568          * as a Record cache should refresh its view.
23569          * @param {Store} this
23570          */
23571         datachanged : true,
23572         /**
23573          * @event metachange
23574          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
23575          * @param {Store} this
23576          * @param {Object} meta The JSON metadata
23577          */
23578         metachange : true,
23579         /**
23580          * @event add
23581          * Fires when Records have been added to the Store
23582          * @param {Store} this
23583          * @param {Roo.data.Record[]} records The array of Records added
23584          * @param {Number} index The index at which the record(s) were added
23585          */
23586         add : true,
23587         /**
23588          * @event remove
23589          * Fires when a Record has been removed from the Store
23590          * @param {Store} this
23591          * @param {Roo.data.Record} record The Record that was removed
23592          * @param {Number} index The index at which the record was removed
23593          */
23594         remove : true,
23595         /**
23596          * @event update
23597          * Fires when a Record has been updated
23598          * @param {Store} this
23599          * @param {Roo.data.Record} record The Record that was updated
23600          * @param {String} operation The update operation being performed.  Value may be one of:
23601          * <pre><code>
23602  Roo.data.Record.EDIT
23603  Roo.data.Record.REJECT
23604  Roo.data.Record.COMMIT
23605          * </code></pre>
23606          */
23607         update : true,
23608         /**
23609          * @event clear
23610          * Fires when the data cache has been cleared.
23611          * @param {Store} this
23612          */
23613         clear : true,
23614         /**
23615          * @event beforeload
23616          * Fires before a request is made for a new data object.  If the beforeload handler returns false
23617          * the load action will be canceled.
23618          * @param {Store} this
23619          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23620          */
23621         beforeload : true,
23622         /**
23623          * @event beforeloadadd
23624          * Fires after a new set of Records has been loaded.
23625          * @param {Store} this
23626          * @param {Roo.data.Record[]} records The Records that were loaded
23627          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23628          */
23629         beforeloadadd : true,
23630         /**
23631          * @event load
23632          * Fires after a new set of Records has been loaded, before they are added to the store.
23633          * @param {Store} this
23634          * @param {Roo.data.Record[]} records The Records that were loaded
23635          * @param {Object} options The loading options that were specified (see {@link #load} for details)
23636          * @params {Object} return from reader
23637          */
23638         load : true,
23639         /**
23640          * @event loadexception
23641          * Fires if an exception occurs in the Proxy during loading.
23642          * Called with the signature of the Proxy's "loadexception" event.
23643          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
23644          * 
23645          * @param {Proxy} 
23646          * @param {Object} return from JsonData.reader() - success, totalRecords, records
23647          * @param {Object} load options 
23648          * @param {Object} jsonData from your request (normally this contains the Exception)
23649          */
23650         loadexception : true
23651     });
23652     
23653     if(this.proxy){
23654         this.proxy = Roo.factory(this.proxy, Roo.data);
23655         this.proxy.xmodule = this.xmodule || false;
23656         this.relayEvents(this.proxy,  ["loadexception"]);
23657     }
23658     this.sortToggle = {};
23659     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
23660
23661     Roo.data.Store.superclass.constructor.call(this);
23662
23663     if(this.inlineData){
23664         this.loadData(this.inlineData);
23665         delete this.inlineData;
23666     }
23667 };
23668
23669 Roo.extend(Roo.data.Store, Roo.util.Observable, {
23670      /**
23671     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
23672     * without a remote query - used by combo/forms at present.
23673     */
23674     
23675     /**
23676     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
23677     */
23678     /**
23679     * @cfg {Array} data Inline data to be loaded when the store is initialized.
23680     */
23681     /**
23682     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
23683     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
23684     */
23685     /**
23686     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
23687     * on any HTTP request
23688     */
23689     /**
23690     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
23691     */
23692     /**
23693     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
23694     */
23695     multiSort: false,
23696     /**
23697     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
23698     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
23699     */
23700     remoteSort : false,
23701
23702     /**
23703     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
23704      * loaded or when a record is removed. (defaults to false).
23705     */
23706     pruneModifiedRecords : false,
23707
23708     // private
23709     lastOptions : null,
23710
23711     /**
23712      * Add Records to the Store and fires the add event.
23713      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23714      */
23715     add : function(records){
23716         records = [].concat(records);
23717         for(var i = 0, len = records.length; i < len; i++){
23718             records[i].join(this);
23719         }
23720         var index = this.data.length;
23721         this.data.addAll(records);
23722         this.fireEvent("add", this, records, index);
23723     },
23724
23725     /**
23726      * Remove a Record from the Store and fires the remove event.
23727      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
23728      */
23729     remove : function(record){
23730         var index = this.data.indexOf(record);
23731         this.data.removeAt(index);
23732  
23733         if(this.pruneModifiedRecords){
23734             this.modified.remove(record);
23735         }
23736         this.fireEvent("remove", this, record, index);
23737     },
23738
23739     /**
23740      * Remove all Records from the Store and fires the clear event.
23741      */
23742     removeAll : function(){
23743         this.data.clear();
23744         if(this.pruneModifiedRecords){
23745             this.modified = [];
23746         }
23747         this.fireEvent("clear", this);
23748     },
23749
23750     /**
23751      * Inserts Records to the Store at the given index and fires the add event.
23752      * @param {Number} index The start index at which to insert the passed Records.
23753      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
23754      */
23755     insert : function(index, records){
23756         records = [].concat(records);
23757         for(var i = 0, len = records.length; i < len; i++){
23758             this.data.insert(index, records[i]);
23759             records[i].join(this);
23760         }
23761         this.fireEvent("add", this, records, index);
23762     },
23763
23764     /**
23765      * Get the index within the cache of the passed Record.
23766      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
23767      * @return {Number} The index of the passed Record. Returns -1 if not found.
23768      */
23769     indexOf : function(record){
23770         return this.data.indexOf(record);
23771     },
23772
23773     /**
23774      * Get the index within the cache of the Record with the passed id.
23775      * @param {String} id The id of the Record to find.
23776      * @return {Number} The index of the Record. Returns -1 if not found.
23777      */
23778     indexOfId : function(id){
23779         return this.data.indexOfKey(id);
23780     },
23781
23782     /**
23783      * Get the Record with the specified id.
23784      * @param {String} id The id of the Record to find.
23785      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
23786      */
23787     getById : function(id){
23788         return this.data.key(id);
23789     },
23790
23791     /**
23792      * Get the Record at the specified index.
23793      * @param {Number} index The index of the Record to find.
23794      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
23795      */
23796     getAt : function(index){
23797         return this.data.itemAt(index);
23798     },
23799
23800     /**
23801      * Returns a range of Records between specified indices.
23802      * @param {Number} startIndex (optional) The starting index (defaults to 0)
23803      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
23804      * @return {Roo.data.Record[]} An array of Records
23805      */
23806     getRange : function(start, end){
23807         return this.data.getRange(start, end);
23808     },
23809
23810     // private
23811     storeOptions : function(o){
23812         o = Roo.apply({}, o);
23813         delete o.callback;
23814         delete o.scope;
23815         this.lastOptions = o;
23816     },
23817
23818     /**
23819      * Loads the Record cache from the configured Proxy using the configured Reader.
23820      * <p>
23821      * If using remote paging, then the first load call must specify the <em>start</em>
23822      * and <em>limit</em> properties in the options.params property to establish the initial
23823      * position within the dataset, and the number of Records to cache on each read from the Proxy.
23824      * <p>
23825      * <strong>It is important to note that for remote data sources, loading is asynchronous,
23826      * and this call will return before the new data has been loaded. Perform any post-processing
23827      * in a callback function, or in a "load" event handler.</strong>
23828      * <p>
23829      * @param {Object} options An object containing properties which control loading options:<ul>
23830      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
23831      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
23832      * passed the following arguments:<ul>
23833      * <li>r : Roo.data.Record[]</li>
23834      * <li>options: Options object from the load call</li>
23835      * <li>success: Boolean success indicator</li></ul></li>
23836      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
23837      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
23838      * </ul>
23839      */
23840     load : function(options){
23841         options = options || {};
23842         if(this.fireEvent("beforeload", this, options) !== false){
23843             this.storeOptions(options);
23844             var p = Roo.apply(options.params || {}, this.baseParams);
23845             // if meta was not loaded from remote source.. try requesting it.
23846             if (!this.reader.metaFromRemote) {
23847                 p._requestMeta = 1;
23848             }
23849             if(this.sortInfo && this.remoteSort){
23850                 var pn = this.paramNames;
23851                 p[pn["sort"]] = this.sortInfo.field;
23852                 p[pn["dir"]] = this.sortInfo.direction;
23853             }
23854             if (this.multiSort) {
23855                 var pn = this.paramNames;
23856                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
23857             }
23858             
23859             this.proxy.load(p, this.reader, this.loadRecords, this, options);
23860         }
23861     },
23862
23863     /**
23864      * Reloads the Record cache from the configured Proxy using the configured Reader and
23865      * the options from the last load operation performed.
23866      * @param {Object} options (optional) An object containing properties which may override the options
23867      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
23868      * the most recently used options are reused).
23869      */
23870     reload : function(options){
23871         this.load(Roo.applyIf(options||{}, this.lastOptions));
23872     },
23873
23874     // private
23875     // Called as a callback by the Reader during a load operation.
23876     loadRecords : function(o, options, success){
23877         if(!o || success === false){
23878             if(success !== false){
23879                 this.fireEvent("load", this, [], options, o);
23880             }
23881             if(options.callback){
23882                 options.callback.call(options.scope || this, [], options, false);
23883             }
23884             return;
23885         }
23886         // if data returned failure - throw an exception.
23887         if (o.success === false) {
23888             // show a message if no listener is registered.
23889             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
23890                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
23891             }
23892             // loadmask wil be hooked into this..
23893             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
23894             return;
23895         }
23896         var r = o.records, t = o.totalRecords || r.length;
23897         
23898         this.fireEvent("beforeloadadd", this, r, options, o);
23899         
23900         if(!options || options.add !== true){
23901             if(this.pruneModifiedRecords){
23902                 this.modified = [];
23903             }
23904             for(var i = 0, len = r.length; i < len; i++){
23905                 r[i].join(this);
23906             }
23907             if(this.snapshot){
23908                 this.data = this.snapshot;
23909                 delete this.snapshot;
23910             }
23911             this.data.clear();
23912             this.data.addAll(r);
23913             this.totalLength = t;
23914             this.applySort();
23915             this.fireEvent("datachanged", this);
23916         }else{
23917             this.totalLength = Math.max(t, this.data.length+r.length);
23918             this.add(r);
23919         }
23920         
23921         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
23922                 
23923             var e = new Roo.data.Record({});
23924
23925             e.set(this.parent.displayField, this.parent.emptyTitle);
23926             e.set(this.parent.valueField, '');
23927
23928             this.insert(0, e);
23929         }
23930             
23931         this.fireEvent("load", this, r, options, o);
23932         if(options.callback){
23933             options.callback.call(options.scope || this, r, options, true);
23934         }
23935     },
23936
23937
23938     /**
23939      * Loads data from a passed data block. A Reader which understands the format of the data
23940      * must have been configured in the constructor.
23941      * @param {Object} data The data block from which to read the Records.  The format of the data expected
23942      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
23943      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
23944      */
23945     loadData : function(o, append){
23946         var r = this.reader.readRecords(o);
23947         this.loadRecords(r, {add: append}, true);
23948     },
23949     
23950      /**
23951      * using 'cn' the nested child reader read the child array into it's child stores.
23952      * @param {Object} rec The record with a 'children array
23953      */
23954     loadDataFromChildren : function(rec)
23955     {
23956         this.loadData(this.reader.toLoadData(rec));
23957     },
23958     
23959
23960     /**
23961      * Gets the number of cached records.
23962      * <p>
23963      * <em>If using paging, this may not be the total size of the dataset. If the data object
23964      * used by the Reader contains the dataset size, then the getTotalCount() function returns
23965      * the data set size</em>
23966      */
23967     getCount : function(){
23968         return this.data.length || 0;
23969     },
23970
23971     /**
23972      * Gets the total number of records in the dataset as returned by the server.
23973      * <p>
23974      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
23975      * the dataset size</em>
23976      */
23977     getTotalCount : function(){
23978         return this.totalLength || 0;
23979     },
23980
23981     /**
23982      * Returns the sort state of the Store as an object with two properties:
23983      * <pre><code>
23984  field {String} The name of the field by which the Records are sorted
23985  direction {String} The sort order, "ASC" or "DESC"
23986      * </code></pre>
23987      */
23988     getSortState : function(){
23989         return this.sortInfo;
23990     },
23991
23992     // private
23993     applySort : function(){
23994         if(this.sortInfo && !this.remoteSort){
23995             var s = this.sortInfo, f = s.field;
23996             var st = this.fields.get(f).sortType;
23997             var fn = function(r1, r2){
23998                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
23999                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24000             };
24001             this.data.sort(s.direction, fn);
24002             if(this.snapshot && this.snapshot != this.data){
24003                 this.snapshot.sort(s.direction, fn);
24004             }
24005         }
24006     },
24007
24008     /**
24009      * Sets the default sort column and order to be used by the next load operation.
24010      * @param {String} fieldName The name of the field to sort by.
24011      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24012      */
24013     setDefaultSort : function(field, dir){
24014         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24015     },
24016
24017     /**
24018      * Sort the Records.
24019      * If remote sorting is used, the sort is performed on the server, and the cache is
24020      * reloaded. If local sorting is used, the cache is sorted internally.
24021      * @param {String} fieldName The name of the field to sort by.
24022      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24023      */
24024     sort : function(fieldName, dir){
24025         var f = this.fields.get(fieldName);
24026         if(!dir){
24027             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24028             
24029             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24030                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24031             }else{
24032                 dir = f.sortDir;
24033             }
24034         }
24035         this.sortToggle[f.name] = dir;
24036         this.sortInfo = {field: f.name, direction: dir};
24037         if(!this.remoteSort){
24038             this.applySort();
24039             this.fireEvent("datachanged", this);
24040         }else{
24041             this.load(this.lastOptions);
24042         }
24043     },
24044
24045     /**
24046      * Calls the specified function for each of the Records in the cache.
24047      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24048      * Returning <em>false</em> aborts and exits the iteration.
24049      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24050      */
24051     each : function(fn, scope){
24052         this.data.each(fn, scope);
24053     },
24054
24055     /**
24056      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24057      * (e.g., during paging).
24058      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24059      */
24060     getModifiedRecords : function(){
24061         return this.modified;
24062     },
24063
24064     // private
24065     createFilterFn : function(property, value, anyMatch){
24066         if(!value.exec){ // not a regex
24067             value = String(value);
24068             if(value.length == 0){
24069                 return false;
24070             }
24071             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24072         }
24073         return function(r){
24074             return value.test(r.data[property]);
24075         };
24076     },
24077
24078     /**
24079      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24080      * @param {String} property A field on your records
24081      * @param {Number} start The record index to start at (defaults to 0)
24082      * @param {Number} end The last record index to include (defaults to length - 1)
24083      * @return {Number} The sum
24084      */
24085     sum : function(property, start, end){
24086         var rs = this.data.items, v = 0;
24087         start = start || 0;
24088         end = (end || end === 0) ? end : rs.length-1;
24089
24090         for(var i = start; i <= end; i++){
24091             v += (rs[i].data[property] || 0);
24092         }
24093         return v;
24094     },
24095
24096     /**
24097      * Filter the records by a specified property.
24098      * @param {String} field A field on your records
24099      * @param {String/RegExp} value Either a string that the field
24100      * should start with or a RegExp to test against the field
24101      * @param {Boolean} anyMatch True to match any part not just the beginning
24102      */
24103     filter : function(property, value, anyMatch){
24104         var fn = this.createFilterFn(property, value, anyMatch);
24105         return fn ? this.filterBy(fn) : this.clearFilter();
24106     },
24107
24108     /**
24109      * Filter by a function. The specified function will be called with each
24110      * record in this data source. If the function returns true the record is included,
24111      * otherwise it is filtered.
24112      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24113      * @param {Object} scope (optional) The scope of the function (defaults to this)
24114      */
24115     filterBy : function(fn, scope){
24116         this.snapshot = this.snapshot || this.data;
24117         this.data = this.queryBy(fn, scope||this);
24118         this.fireEvent("datachanged", this);
24119     },
24120
24121     /**
24122      * Query the records by a specified property.
24123      * @param {String} field A field on your records
24124      * @param {String/RegExp} value Either a string that the field
24125      * should start with or a RegExp to test against the field
24126      * @param {Boolean} anyMatch True to match any part not just the beginning
24127      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24128      */
24129     query : function(property, value, anyMatch){
24130         var fn = this.createFilterFn(property, value, anyMatch);
24131         return fn ? this.queryBy(fn) : this.data.clone();
24132     },
24133
24134     /**
24135      * Query by a function. The specified function will be called with each
24136      * record in this data source. If the function returns true the record is included
24137      * in the results.
24138      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24139      * @param {Object} scope (optional) The scope of the function (defaults to this)
24140       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24141      **/
24142     queryBy : function(fn, scope){
24143         var data = this.snapshot || this.data;
24144         return data.filterBy(fn, scope||this);
24145     },
24146
24147     /**
24148      * Collects unique values for a particular dataIndex from this store.
24149      * @param {String} dataIndex The property to collect
24150      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24151      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24152      * @return {Array} An array of the unique values
24153      **/
24154     collect : function(dataIndex, allowNull, bypassFilter){
24155         var d = (bypassFilter === true && this.snapshot) ?
24156                 this.snapshot.items : this.data.items;
24157         var v, sv, r = [], l = {};
24158         for(var i = 0, len = d.length; i < len; i++){
24159             v = d[i].data[dataIndex];
24160             sv = String(v);
24161             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24162                 l[sv] = true;
24163                 r[r.length] = v;
24164             }
24165         }
24166         return r;
24167     },
24168
24169     /**
24170      * Revert to a view of the Record cache with no filtering applied.
24171      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24172      */
24173     clearFilter : function(suppressEvent){
24174         if(this.snapshot && this.snapshot != this.data){
24175             this.data = this.snapshot;
24176             delete this.snapshot;
24177             if(suppressEvent !== true){
24178                 this.fireEvent("datachanged", this);
24179             }
24180         }
24181     },
24182
24183     // private
24184     afterEdit : function(record){
24185         if(this.modified.indexOf(record) == -1){
24186             this.modified.push(record);
24187         }
24188         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24189     },
24190     
24191     // private
24192     afterReject : function(record){
24193         this.modified.remove(record);
24194         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24195     },
24196
24197     // private
24198     afterCommit : function(record){
24199         this.modified.remove(record);
24200         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24201     },
24202
24203     /**
24204      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24205      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24206      */
24207     commitChanges : function(){
24208         var m = this.modified.slice(0);
24209         this.modified = [];
24210         for(var i = 0, len = m.length; i < len; i++){
24211             m[i].commit();
24212         }
24213     },
24214
24215     /**
24216      * Cancel outstanding changes on all changed records.
24217      */
24218     rejectChanges : function(){
24219         var m = this.modified.slice(0);
24220         this.modified = [];
24221         for(var i = 0, len = m.length; i < len; i++){
24222             m[i].reject();
24223         }
24224     },
24225
24226     onMetaChange : function(meta, rtype, o){
24227         this.recordType = rtype;
24228         this.fields = rtype.prototype.fields;
24229         delete this.snapshot;
24230         this.sortInfo = meta.sortInfo || this.sortInfo;
24231         this.modified = [];
24232         this.fireEvent('metachange', this, this.reader.meta);
24233     },
24234     
24235     moveIndex : function(data, type)
24236     {
24237         var index = this.indexOf(data);
24238         
24239         var newIndex = index + type;
24240         
24241         this.remove(data);
24242         
24243         this.insert(newIndex, data);
24244         
24245     }
24246 });/*
24247  * Based on:
24248  * Ext JS Library 1.1.1
24249  * Copyright(c) 2006-2007, Ext JS, LLC.
24250  *
24251  * Originally Released Under LGPL - original licence link has changed is not relivant.
24252  *
24253  * Fork - LGPL
24254  * <script type="text/javascript">
24255  */
24256
24257 /**
24258  * @class Roo.data.SimpleStore
24259  * @extends Roo.data.Store
24260  * Small helper class to make creating Stores from Array data easier.
24261  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24262  * @cfg {Array} fields An array of field definition objects, or field name strings.
24263  * @cfg {Object} an existing reader (eg. copied from another store)
24264  * @cfg {Array} data The multi-dimensional array of data
24265  * @constructor
24266  * @param {Object} config
24267  */
24268 Roo.data.SimpleStore = function(config)
24269 {
24270     Roo.data.SimpleStore.superclass.constructor.call(this, {
24271         isLocal : true,
24272         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
24273                 id: config.id
24274             },
24275             Roo.data.Record.create(config.fields)
24276         ),
24277         proxy : new Roo.data.MemoryProxy(config.data)
24278     });
24279     this.load();
24280 };
24281 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
24282  * Based on:
24283  * Ext JS Library 1.1.1
24284  * Copyright(c) 2006-2007, Ext JS, LLC.
24285  *
24286  * Originally Released Under LGPL - original licence link has changed is not relivant.
24287  *
24288  * Fork - LGPL
24289  * <script type="text/javascript">
24290  */
24291
24292 /**
24293 /**
24294  * @extends Roo.data.Store
24295  * @class Roo.data.JsonStore
24296  * Small helper class to make creating Stores for JSON data easier. <br/>
24297 <pre><code>
24298 var store = new Roo.data.JsonStore({
24299     url: 'get-images.php',
24300     root: 'images',
24301     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
24302 });
24303 </code></pre>
24304  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
24305  * JsonReader and HttpProxy (unless inline data is provided).</b>
24306  * @cfg {Array} fields An array of field definition objects, or field name strings.
24307  * @constructor
24308  * @param {Object} config
24309  */
24310 Roo.data.JsonStore = function(c){
24311     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
24312         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
24313         reader: new Roo.data.JsonReader(c, c.fields)
24314     }));
24315 };
24316 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
24317  * Based on:
24318  * Ext JS Library 1.1.1
24319  * Copyright(c) 2006-2007, Ext JS, LLC.
24320  *
24321  * Originally Released Under LGPL - original licence link has changed is not relivant.
24322  *
24323  * Fork - LGPL
24324  * <script type="text/javascript">
24325  */
24326
24327  
24328 Roo.data.Field = function(config){
24329     if(typeof config == "string"){
24330         config = {name: config};
24331     }
24332     Roo.apply(this, config);
24333     
24334     if(!this.type){
24335         this.type = "auto";
24336     }
24337     
24338     var st = Roo.data.SortTypes;
24339     // named sortTypes are supported, here we look them up
24340     if(typeof this.sortType == "string"){
24341         this.sortType = st[this.sortType];
24342     }
24343     
24344     // set default sortType for strings and dates
24345     if(!this.sortType){
24346         switch(this.type){
24347             case "string":
24348                 this.sortType = st.asUCString;
24349                 break;
24350             case "date":
24351                 this.sortType = st.asDate;
24352                 break;
24353             default:
24354                 this.sortType = st.none;
24355         }
24356     }
24357
24358     // define once
24359     var stripRe = /[\$,%]/g;
24360
24361     // prebuilt conversion function for this field, instead of
24362     // switching every time we're reading a value
24363     if(!this.convert){
24364         var cv, dateFormat = this.dateFormat;
24365         switch(this.type){
24366             case "":
24367             case "auto":
24368             case undefined:
24369                 cv = function(v){ return v; };
24370                 break;
24371             case "string":
24372                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
24373                 break;
24374             case "int":
24375                 cv = function(v){
24376                     return v !== undefined && v !== null && v !== '' ?
24377                            parseInt(String(v).replace(stripRe, ""), 10) : '';
24378                     };
24379                 break;
24380             case "float":
24381                 cv = function(v){
24382                     return v !== undefined && v !== null && v !== '' ?
24383                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
24384                     };
24385                 break;
24386             case "bool":
24387             case "boolean":
24388                 cv = function(v){ return v === true || v === "true" || v == 1; };
24389                 break;
24390             case "date":
24391                 cv = function(v){
24392                     if(!v){
24393                         return '';
24394                     }
24395                     if(v instanceof Date){
24396                         return v;
24397                     }
24398                     if(dateFormat){
24399                         if(dateFormat == "timestamp"){
24400                             return new Date(v*1000);
24401                         }
24402                         return Date.parseDate(v, dateFormat);
24403                     }
24404                     var parsed = Date.parse(v);
24405                     return parsed ? new Date(parsed) : null;
24406                 };
24407              break;
24408             
24409         }
24410         this.convert = cv;
24411     }
24412 };
24413
24414 Roo.data.Field.prototype = {
24415     dateFormat: null,
24416     defaultValue: "",
24417     mapping: null,
24418     sortType : null,
24419     sortDir : "ASC"
24420 };/*
24421  * Based on:
24422  * Ext JS Library 1.1.1
24423  * Copyright(c) 2006-2007, Ext JS, LLC.
24424  *
24425  * Originally Released Under LGPL - original licence link has changed is not relivant.
24426  *
24427  * Fork - LGPL
24428  * <script type="text/javascript">
24429  */
24430  
24431 // Base class for reading structured data from a data source.  This class is intended to be
24432 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
24433
24434 /**
24435  * @class Roo.data.DataReader
24436  * Base class for reading structured data from a data source.  This class is intended to be
24437  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
24438  */
24439
24440 Roo.data.DataReader = function(meta, recordType){
24441     
24442     this.meta = meta;
24443     
24444     this.recordType = recordType instanceof Array ? 
24445         Roo.data.Record.create(recordType) : recordType;
24446 };
24447
24448 Roo.data.DataReader.prototype = {
24449     
24450     
24451     readerType : 'Data',
24452      /**
24453      * Create an empty record
24454      * @param {Object} data (optional) - overlay some values
24455      * @return {Roo.data.Record} record created.
24456      */
24457     newRow :  function(d) {
24458         var da =  {};
24459         this.recordType.prototype.fields.each(function(c) {
24460             switch( c.type) {
24461                 case 'int' : da[c.name] = 0; break;
24462                 case 'date' : da[c.name] = new Date(); break;
24463                 case 'float' : da[c.name] = 0.0; break;
24464                 case 'boolean' : da[c.name] = false; break;
24465                 default : da[c.name] = ""; break;
24466             }
24467             
24468         });
24469         return new this.recordType(Roo.apply(da, d));
24470     }
24471     
24472     
24473 };/*
24474  * Based on:
24475  * Ext JS Library 1.1.1
24476  * Copyright(c) 2006-2007, Ext JS, LLC.
24477  *
24478  * Originally Released Under LGPL - original licence link has changed is not relivant.
24479  *
24480  * Fork - LGPL
24481  * <script type="text/javascript">
24482  */
24483
24484 /**
24485  * @class Roo.data.DataProxy
24486  * @extends Roo.data.Observable
24487  * This class is an abstract base class for implementations which provide retrieval of
24488  * unformatted data objects.<br>
24489  * <p>
24490  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
24491  * (of the appropriate type which knows how to parse the data object) to provide a block of
24492  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
24493  * <p>
24494  * Custom implementations must implement the load method as described in
24495  * {@link Roo.data.HttpProxy#load}.
24496  */
24497 Roo.data.DataProxy = function(){
24498     this.addEvents({
24499         /**
24500          * @event beforeload
24501          * Fires before a network request is made to retrieve a data object.
24502          * @param {Object} This DataProxy object.
24503          * @param {Object} params The params parameter to the load function.
24504          */
24505         beforeload : true,
24506         /**
24507          * @event load
24508          * Fires before the load method's callback is called.
24509          * @param {Object} This DataProxy object.
24510          * @param {Object} o The data object.
24511          * @param {Object} arg The callback argument object passed to the load function.
24512          */
24513         load : true,
24514         /**
24515          * @event loadexception
24516          * Fires if an Exception occurs during data retrieval.
24517          * @param {Object} This DataProxy object.
24518          * @param {Object} o The data object.
24519          * @param {Object} arg The callback argument object passed to the load function.
24520          * @param {Object} e The Exception.
24521          */
24522         loadexception : true
24523     });
24524     Roo.data.DataProxy.superclass.constructor.call(this);
24525 };
24526
24527 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
24528
24529     /**
24530      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
24531      */
24532 /*
24533  * Based on:
24534  * Ext JS Library 1.1.1
24535  * Copyright(c) 2006-2007, Ext JS, LLC.
24536  *
24537  * Originally Released Under LGPL - original licence link has changed is not relivant.
24538  *
24539  * Fork - LGPL
24540  * <script type="text/javascript">
24541  */
24542 /**
24543  * @class Roo.data.MemoryProxy
24544  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
24545  * to the Reader when its load method is called.
24546  * @constructor
24547  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
24548  */
24549 Roo.data.MemoryProxy = function(data){
24550     if (data.data) {
24551         data = data.data;
24552     }
24553     Roo.data.MemoryProxy.superclass.constructor.call(this);
24554     this.data = data;
24555 };
24556
24557 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
24558     
24559     /**
24560      * Load data from the requested source (in this case an in-memory
24561      * data object passed to the constructor), read the data object into
24562      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24563      * process that block using the passed callback.
24564      * @param {Object} params This parameter is not used by the MemoryProxy class.
24565      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24566      * object into a block of Roo.data.Records.
24567      * @param {Function} callback The function into which to pass the block of Roo.data.records.
24568      * The function must be passed <ul>
24569      * <li>The Record block object</li>
24570      * <li>The "arg" argument from the load function</li>
24571      * <li>A boolean success indicator</li>
24572      * </ul>
24573      * @param {Object} scope The scope in which to call the callback
24574      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24575      */
24576     load : function(params, reader, callback, scope, arg){
24577         params = params || {};
24578         var result;
24579         try {
24580             result = reader.readRecords(params.data ? params.data :this.data);
24581         }catch(e){
24582             this.fireEvent("loadexception", this, arg, null, e);
24583             callback.call(scope, null, arg, false);
24584             return;
24585         }
24586         callback.call(scope, result, arg, true);
24587     },
24588     
24589     // private
24590     update : function(params, records){
24591         
24592     }
24593 });/*
24594  * Based on:
24595  * Ext JS Library 1.1.1
24596  * Copyright(c) 2006-2007, Ext JS, LLC.
24597  *
24598  * Originally Released Under LGPL - original licence link has changed is not relivant.
24599  *
24600  * Fork - LGPL
24601  * <script type="text/javascript">
24602  */
24603 /**
24604  * @class Roo.data.HttpProxy
24605  * @extends Roo.data.DataProxy
24606  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
24607  * configured to reference a certain URL.<br><br>
24608  * <p>
24609  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
24610  * from which the running page was served.<br><br>
24611  * <p>
24612  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
24613  * <p>
24614  * Be aware that to enable the browser to parse an XML document, the server must set
24615  * the Content-Type header in the HTTP response to "text/xml".
24616  * @constructor
24617  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
24618  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
24619  * will be used to make the request.
24620  */
24621 Roo.data.HttpProxy = function(conn){
24622     Roo.data.HttpProxy.superclass.constructor.call(this);
24623     // is conn a conn config or a real conn?
24624     this.conn = conn;
24625     this.useAjax = !conn || !conn.events;
24626   
24627 };
24628
24629 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
24630     // thse are take from connection...
24631     
24632     /**
24633      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
24634      */
24635     /**
24636      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
24637      * extra parameters to each request made by this object. (defaults to undefined)
24638      */
24639     /**
24640      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
24641      *  to each request made by this object. (defaults to undefined)
24642      */
24643     /**
24644      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
24645      */
24646     /**
24647      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
24648      */
24649      /**
24650      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
24651      * @type Boolean
24652      */
24653   
24654
24655     /**
24656      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
24657      * @type Boolean
24658      */
24659     /**
24660      * Return the {@link Roo.data.Connection} object being used by this Proxy.
24661      * @return {Connection} The Connection object. This object may be used to subscribe to events on
24662      * a finer-grained basis than the DataProxy events.
24663      */
24664     getConnection : function(){
24665         return this.useAjax ? Roo.Ajax : this.conn;
24666     },
24667
24668     /**
24669      * Load data from the configured {@link Roo.data.Connection}, read the data object into
24670      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
24671      * process that block using the passed callback.
24672      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24673      * for the request to the remote server.
24674      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24675      * object into a block of Roo.data.Records.
24676      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24677      * The function must be passed <ul>
24678      * <li>The Record block object</li>
24679      * <li>The "arg" argument from the load function</li>
24680      * <li>A boolean success indicator</li>
24681      * </ul>
24682      * @param {Object} scope The scope in which to call the callback
24683      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24684      */
24685     load : function(params, reader, callback, scope, arg){
24686         if(this.fireEvent("beforeload", this, params) !== false){
24687             var  o = {
24688                 params : params || {},
24689                 request: {
24690                     callback : callback,
24691                     scope : scope,
24692                     arg : arg
24693                 },
24694                 reader: reader,
24695                 callback : this.loadResponse,
24696                 scope: this
24697             };
24698             if(this.useAjax){
24699                 Roo.applyIf(o, this.conn);
24700                 if(this.activeRequest){
24701                     Roo.Ajax.abort(this.activeRequest);
24702                 }
24703                 this.activeRequest = Roo.Ajax.request(o);
24704             }else{
24705                 this.conn.request(o);
24706             }
24707         }else{
24708             callback.call(scope||this, null, arg, false);
24709         }
24710     },
24711
24712     // private
24713     loadResponse : function(o, success, response){
24714         delete this.activeRequest;
24715         if(!success){
24716             this.fireEvent("loadexception", this, o, response);
24717             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24718             return;
24719         }
24720         var result;
24721         try {
24722             result = o.reader.read(response);
24723         }catch(e){
24724             this.fireEvent("loadexception", this, o, response, e);
24725             o.request.callback.call(o.request.scope, null, o.request.arg, false);
24726             return;
24727         }
24728         
24729         this.fireEvent("load", this, o, o.request.arg);
24730         o.request.callback.call(o.request.scope, result, o.request.arg, true);
24731     },
24732
24733     // private
24734     update : function(dataSet){
24735
24736     },
24737
24738     // private
24739     updateResponse : function(dataSet){
24740
24741     }
24742 });/*
24743  * Based on:
24744  * Ext JS Library 1.1.1
24745  * Copyright(c) 2006-2007, Ext JS, LLC.
24746  *
24747  * Originally Released Under LGPL - original licence link has changed is not relivant.
24748  *
24749  * Fork - LGPL
24750  * <script type="text/javascript">
24751  */
24752
24753 /**
24754  * @class Roo.data.ScriptTagProxy
24755  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
24756  * other than the originating domain of the running page.<br><br>
24757  * <p>
24758  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
24759  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
24760  * <p>
24761  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
24762  * source code that is used as the source inside a &lt;script> tag.<br><br>
24763  * <p>
24764  * In order for the browser to process the returned data, the server must wrap the data object
24765  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
24766  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
24767  * depending on whether the callback name was passed:
24768  * <p>
24769  * <pre><code>
24770 boolean scriptTag = false;
24771 String cb = request.getParameter("callback");
24772 if (cb != null) {
24773     scriptTag = true;
24774     response.setContentType("text/javascript");
24775 } else {
24776     response.setContentType("application/x-json");
24777 }
24778 Writer out = response.getWriter();
24779 if (scriptTag) {
24780     out.write(cb + "(");
24781 }
24782 out.print(dataBlock.toJsonString());
24783 if (scriptTag) {
24784     out.write(");");
24785 }
24786 </pre></code>
24787  *
24788  * @constructor
24789  * @param {Object} config A configuration object.
24790  */
24791 Roo.data.ScriptTagProxy = function(config){
24792     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
24793     Roo.apply(this, config);
24794     this.head = document.getElementsByTagName("head")[0];
24795 };
24796
24797 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
24798
24799 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
24800     /**
24801      * @cfg {String} url The URL from which to request the data object.
24802      */
24803     /**
24804      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
24805      */
24806     timeout : 30000,
24807     /**
24808      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
24809      * the server the name of the callback function set up by the load call to process the returned data object.
24810      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
24811      * javascript output which calls this named function passing the data object as its only parameter.
24812      */
24813     callbackParam : "callback",
24814     /**
24815      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
24816      * name to the request.
24817      */
24818     nocache : true,
24819
24820     /**
24821      * Load data from the configured URL, read the data object into
24822      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
24823      * process that block using the passed callback.
24824      * @param {Object} params An object containing properties which are to be used as HTTP parameters
24825      * for the request to the remote server.
24826      * @param {Roo.data.DataReader} reader The Reader object which converts the data
24827      * object into a block of Roo.data.Records.
24828      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
24829      * The function must be passed <ul>
24830      * <li>The Record block object</li>
24831      * <li>The "arg" argument from the load function</li>
24832      * <li>A boolean success indicator</li>
24833      * </ul>
24834      * @param {Object} scope The scope in which to call the callback
24835      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
24836      */
24837     load : function(params, reader, callback, scope, arg){
24838         if(this.fireEvent("beforeload", this, params) !== false){
24839
24840             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
24841
24842             var url = this.url;
24843             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
24844             if(this.nocache){
24845                 url += "&_dc=" + (new Date().getTime());
24846             }
24847             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
24848             var trans = {
24849                 id : transId,
24850                 cb : "stcCallback"+transId,
24851                 scriptId : "stcScript"+transId,
24852                 params : params,
24853                 arg : arg,
24854                 url : url,
24855                 callback : callback,
24856                 scope : scope,
24857                 reader : reader
24858             };
24859             var conn = this;
24860
24861             window[trans.cb] = function(o){
24862                 conn.handleResponse(o, trans);
24863             };
24864
24865             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
24866
24867             if(this.autoAbort !== false){
24868                 this.abort();
24869             }
24870
24871             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
24872
24873             var script = document.createElement("script");
24874             script.setAttribute("src", url);
24875             script.setAttribute("type", "text/javascript");
24876             script.setAttribute("id", trans.scriptId);
24877             this.head.appendChild(script);
24878
24879             this.trans = trans;
24880         }else{
24881             callback.call(scope||this, null, arg, false);
24882         }
24883     },
24884
24885     // private
24886     isLoading : function(){
24887         return this.trans ? true : false;
24888     },
24889
24890     /**
24891      * Abort the current server request.
24892      */
24893     abort : function(){
24894         if(this.isLoading()){
24895             this.destroyTrans(this.trans);
24896         }
24897     },
24898
24899     // private
24900     destroyTrans : function(trans, isLoaded){
24901         this.head.removeChild(document.getElementById(trans.scriptId));
24902         clearTimeout(trans.timeoutId);
24903         if(isLoaded){
24904             window[trans.cb] = undefined;
24905             try{
24906                 delete window[trans.cb];
24907             }catch(e){}
24908         }else{
24909             // if hasn't been loaded, wait for load to remove it to prevent script error
24910             window[trans.cb] = function(){
24911                 window[trans.cb] = undefined;
24912                 try{
24913                     delete window[trans.cb];
24914                 }catch(e){}
24915             };
24916         }
24917     },
24918
24919     // private
24920     handleResponse : function(o, trans){
24921         this.trans = false;
24922         this.destroyTrans(trans, true);
24923         var result;
24924         try {
24925             result = trans.reader.readRecords(o);
24926         }catch(e){
24927             this.fireEvent("loadexception", this, o, trans.arg, e);
24928             trans.callback.call(trans.scope||window, null, trans.arg, false);
24929             return;
24930         }
24931         this.fireEvent("load", this, o, trans.arg);
24932         trans.callback.call(trans.scope||window, result, trans.arg, true);
24933     },
24934
24935     // private
24936     handleFailure : function(trans){
24937         this.trans = false;
24938         this.destroyTrans(trans, false);
24939         this.fireEvent("loadexception", this, null, trans.arg);
24940         trans.callback.call(trans.scope||window, null, trans.arg, false);
24941     }
24942 });/*
24943  * Based on:
24944  * Ext JS Library 1.1.1
24945  * Copyright(c) 2006-2007, Ext JS, LLC.
24946  *
24947  * Originally Released Under LGPL - original licence link has changed is not relivant.
24948  *
24949  * Fork - LGPL
24950  * <script type="text/javascript">
24951  */
24952
24953 /**
24954  * @class Roo.data.JsonReader
24955  * @extends Roo.data.DataReader
24956  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
24957  * based on mappings in a provided Roo.data.Record constructor.
24958  * 
24959  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
24960  * in the reply previously. 
24961  * 
24962  * <p>
24963  * Example code:
24964  * <pre><code>
24965 var RecordDef = Roo.data.Record.create([
24966     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
24967     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
24968 ]);
24969 var myReader = new Roo.data.JsonReader({
24970     totalProperty: "results",    // The property which contains the total dataset size (optional)
24971     root: "rows",                // The property which contains an Array of row objects
24972     id: "id"                     // The property within each row object that provides an ID for the record (optional)
24973 }, RecordDef);
24974 </code></pre>
24975  * <p>
24976  * This would consume a JSON file like this:
24977  * <pre><code>
24978 { 'results': 2, 'rows': [
24979     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
24980     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
24981 }
24982 </code></pre>
24983  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
24984  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
24985  * paged from the remote server.
24986  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
24987  * @cfg {String} root name of the property which contains the Array of row objects.
24988  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
24989  * @cfg {Array} fields Array of field definition objects
24990  * @constructor
24991  * Create a new JsonReader
24992  * @param {Object} meta Metadata configuration options
24993  * @param {Object} recordType Either an Array of field definition objects,
24994  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
24995  */
24996 Roo.data.JsonReader = function(meta, recordType){
24997     
24998     meta = meta || {};
24999     // set some defaults:
25000     Roo.applyIf(meta, {
25001         totalProperty: 'total',
25002         successProperty : 'success',
25003         root : 'data',
25004         id : 'id'
25005     });
25006     
25007     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25008 };
25009 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25010     
25011     readerType : 'Json',
25012     
25013     /**
25014      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25015      * Used by Store query builder to append _requestMeta to params.
25016      * 
25017      */
25018     metaFromRemote : false,
25019     /**
25020      * This method is only used by a DataProxy which has retrieved data from a remote server.
25021      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25022      * @return {Object} data A data block which is used by an Roo.data.Store object as
25023      * a cache of Roo.data.Records.
25024      */
25025     read : function(response){
25026         var json = response.responseText;
25027        
25028         var o = /* eval:var:o */ eval("("+json+")");
25029         if(!o) {
25030             throw {message: "JsonReader.read: Json object not found"};
25031         }
25032         
25033         if(o.metaData){
25034             
25035             delete this.ef;
25036             this.metaFromRemote = true;
25037             this.meta = o.metaData;
25038             this.recordType = Roo.data.Record.create(o.metaData.fields);
25039             this.onMetaChange(this.meta, this.recordType, o);
25040         }
25041         return this.readRecords(o);
25042     },
25043
25044     // private function a store will implement
25045     onMetaChange : function(meta, recordType, o){
25046
25047     },
25048
25049     /**
25050          * @ignore
25051          */
25052     simpleAccess: function(obj, subsc) {
25053         return obj[subsc];
25054     },
25055
25056         /**
25057          * @ignore
25058          */
25059     getJsonAccessor: function(){
25060         var re = /[\[\.]/;
25061         return function(expr) {
25062             try {
25063                 return(re.test(expr))
25064                     ? new Function("obj", "return obj." + expr)
25065                     : function(obj){
25066                         return obj[expr];
25067                     };
25068             } catch(e){}
25069             return Roo.emptyFn;
25070         };
25071     }(),
25072
25073     /**
25074      * Create a data block containing Roo.data.Records from an XML document.
25075      * @param {Object} o An object which contains an Array of row objects in the property specified
25076      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25077      * which contains the total size of the dataset.
25078      * @return {Object} data A data block which is used by an Roo.data.Store object as
25079      * a cache of Roo.data.Records.
25080      */
25081     readRecords : function(o){
25082         /**
25083          * After any data loads, the raw JSON data is available for further custom processing.
25084          * @type Object
25085          */
25086         this.o = o;
25087         var s = this.meta, Record = this.recordType,
25088             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25089
25090 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25091         if (!this.ef) {
25092             if(s.totalProperty) {
25093                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25094                 }
25095                 if(s.successProperty) {
25096                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25097                 }
25098                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25099                 if (s.id) {
25100                         var g = this.getJsonAccessor(s.id);
25101                         this.getId = function(rec) {
25102                                 var r = g(rec);  
25103                                 return (r === undefined || r === "") ? null : r;
25104                         };
25105                 } else {
25106                         this.getId = function(){return null;};
25107                 }
25108             this.ef = [];
25109             for(var jj = 0; jj < fl; jj++){
25110                 f = fi[jj];
25111                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25112                 this.ef[jj] = this.getJsonAccessor(map);
25113             }
25114         }
25115
25116         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25117         if(s.totalProperty){
25118             var vt = parseInt(this.getTotal(o), 10);
25119             if(!isNaN(vt)){
25120                 totalRecords = vt;
25121             }
25122         }
25123         if(s.successProperty){
25124             var vs = this.getSuccess(o);
25125             if(vs === false || vs === 'false'){
25126                 success = false;
25127             }
25128         }
25129         var records = [];
25130         for(var i = 0; i < c; i++){
25131                 var n = root[i];
25132             var values = {};
25133             var id = this.getId(n);
25134             for(var j = 0; j < fl; j++){
25135                 f = fi[j];
25136             var v = this.ef[j](n);
25137             if (!f.convert) {
25138                 Roo.log('missing convert for ' + f.name);
25139                 Roo.log(f);
25140                 continue;
25141             }
25142             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25143             }
25144             var record = new Record(values, id);
25145             record.json = n;
25146             records[i] = record;
25147         }
25148         return {
25149             raw : o,
25150             success : success,
25151             records : records,
25152             totalRecords : totalRecords
25153         };
25154     },
25155     // used when loading children.. @see loadDataFromChildren
25156     toLoadData: function(rec)
25157     {
25158         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25159         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25160         return { data : data, total : data.length };
25161         
25162     }
25163 });/*
25164  * Based on:
25165  * Ext JS Library 1.1.1
25166  * Copyright(c) 2006-2007, Ext JS, LLC.
25167  *
25168  * Originally Released Under LGPL - original licence link has changed is not relivant.
25169  *
25170  * Fork - LGPL
25171  * <script type="text/javascript">
25172  */
25173
25174 /**
25175  * @class Roo.data.XmlReader
25176  * @extends Roo.data.DataReader
25177  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25178  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25179  * <p>
25180  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25181  * header in the HTTP response must be set to "text/xml".</em>
25182  * <p>
25183  * Example code:
25184  * <pre><code>
25185 var RecordDef = Roo.data.Record.create([
25186    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25187    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25188 ]);
25189 var myReader = new Roo.data.XmlReader({
25190    totalRecords: "results", // The element which contains the total dataset size (optional)
25191    record: "row",           // The repeated element which contains row information
25192    id: "id"                 // The element within the row that provides an ID for the record (optional)
25193 }, RecordDef);
25194 </code></pre>
25195  * <p>
25196  * This would consume an XML file like this:
25197  * <pre><code>
25198 &lt;?xml?>
25199 &lt;dataset>
25200  &lt;results>2&lt;/results>
25201  &lt;row>
25202    &lt;id>1&lt;/id>
25203    &lt;name>Bill&lt;/name>
25204    &lt;occupation>Gardener&lt;/occupation>
25205  &lt;/row>
25206  &lt;row>
25207    &lt;id>2&lt;/id>
25208    &lt;name>Ben&lt;/name>
25209    &lt;occupation>Horticulturalist&lt;/occupation>
25210  &lt;/row>
25211 &lt;/dataset>
25212 </code></pre>
25213  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25214  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25215  * paged from the remote server.
25216  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25217  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25218  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25219  * a record identifier value.
25220  * @constructor
25221  * Create a new XmlReader
25222  * @param {Object} meta Metadata configuration options
25223  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25224  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25225  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25226  */
25227 Roo.data.XmlReader = function(meta, recordType){
25228     meta = meta || {};
25229     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25230 };
25231 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25232     
25233     readerType : 'Xml',
25234     
25235     /**
25236      * This method is only used by a DataProxy which has retrieved data from a remote server.
25237          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25238          * to contain a method called 'responseXML' that returns an XML document object.
25239      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25240      * a cache of Roo.data.Records.
25241      */
25242     read : function(response){
25243         var doc = response.responseXML;
25244         if(!doc) {
25245             throw {message: "XmlReader.read: XML Document not available"};
25246         }
25247         return this.readRecords(doc);
25248     },
25249
25250     /**
25251      * Create a data block containing Roo.data.Records from an XML document.
25252          * @param {Object} doc A parsed XML document.
25253      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25254      * a cache of Roo.data.Records.
25255      */
25256     readRecords : function(doc){
25257         /**
25258          * After any data loads/reads, the raw XML Document is available for further custom processing.
25259          * @type XMLDocument
25260          */
25261         this.xmlData = doc;
25262         var root = doc.documentElement || doc;
25263         var q = Roo.DomQuery;
25264         var recordType = this.recordType, fields = recordType.prototype.fields;
25265         var sid = this.meta.id;
25266         var totalRecords = 0, success = true;
25267         if(this.meta.totalRecords){
25268             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
25269         }
25270         
25271         if(this.meta.success){
25272             var sv = q.selectValue(this.meta.success, root, true);
25273             success = sv !== false && sv !== 'false';
25274         }
25275         var records = [];
25276         var ns = q.select(this.meta.record, root);
25277         for(var i = 0, len = ns.length; i < len; i++) {
25278                 var n = ns[i];
25279                 var values = {};
25280                 var id = sid ? q.selectValue(sid, n) : undefined;
25281                 for(var j = 0, jlen = fields.length; j < jlen; j++){
25282                     var f = fields.items[j];
25283                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
25284                     v = f.convert(v);
25285                     values[f.name] = v;
25286                 }
25287                 var record = new recordType(values, id);
25288                 record.node = n;
25289                 records[records.length] = record;
25290             }
25291
25292             return {
25293                 success : success,
25294                 records : records,
25295                 totalRecords : totalRecords || records.length
25296             };
25297     }
25298 });/*
25299  * Based on:
25300  * Ext JS Library 1.1.1
25301  * Copyright(c) 2006-2007, Ext JS, LLC.
25302  *
25303  * Originally Released Under LGPL - original licence link has changed is not relivant.
25304  *
25305  * Fork - LGPL
25306  * <script type="text/javascript">
25307  */
25308
25309 /**
25310  * @class Roo.data.ArrayReader
25311  * @extends Roo.data.DataReader
25312  * Data reader class to create an Array of Roo.data.Record objects from an Array.
25313  * Each element of that Array represents a row of data fields. The
25314  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
25315  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
25316  * <p>
25317  * Example code:.
25318  * <pre><code>
25319 var RecordDef = Roo.data.Record.create([
25320     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
25321     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
25322 ]);
25323 var myReader = new Roo.data.ArrayReader({
25324     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
25325 }, RecordDef);
25326 </code></pre>
25327  * <p>
25328  * This would consume an Array like this:
25329  * <pre><code>
25330 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
25331   </code></pre>
25332  
25333  * @constructor
25334  * Create a new JsonReader
25335  * @param {Object} meta Metadata configuration options.
25336  * @param {Object|Array} recordType Either an Array of field definition objects
25337  * 
25338  * @cfg {Array} fields Array of field definition objects
25339  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25340  * as specified to {@link Roo.data.Record#create},
25341  * or an {@link Roo.data.Record} object
25342  *
25343  * 
25344  * created using {@link Roo.data.Record#create}.
25345  */
25346 Roo.data.ArrayReader = function(meta, recordType)
25347 {    
25348     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25349 };
25350
25351 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
25352     
25353       /**
25354      * Create a data block containing Roo.data.Records from an XML document.
25355      * @param {Object} o An Array of row objects which represents the dataset.
25356      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
25357      * a cache of Roo.data.Records.
25358      */
25359     readRecords : function(o)
25360     {
25361         var sid = this.meta ? this.meta.id : null;
25362         var recordType = this.recordType, fields = recordType.prototype.fields;
25363         var records = [];
25364         var root = o;
25365         for(var i = 0; i < root.length; i++){
25366             var n = root[i];
25367             var values = {};
25368             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
25369             for(var j = 0, jlen = fields.length; j < jlen; j++){
25370                 var f = fields.items[j];
25371                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
25372                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
25373                 v = f.convert(v);
25374                 values[f.name] = v;
25375             }
25376             var record = new recordType(values, id);
25377             record.json = n;
25378             records[records.length] = record;
25379         }
25380         return {
25381             records : records,
25382             totalRecords : records.length
25383         };
25384     },
25385     // used when loading children.. @see loadDataFromChildren
25386     toLoadData: function(rec)
25387     {
25388         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25389         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25390         
25391     }
25392     
25393     
25394 });/*
25395  * Based on:
25396  * Ext JS Library 1.1.1
25397  * Copyright(c) 2006-2007, Ext JS, LLC.
25398  *
25399  * Originally Released Under LGPL - original licence link has changed is not relivant.
25400  *
25401  * Fork - LGPL
25402  * <script type="text/javascript">
25403  */
25404
25405
25406 /**
25407  * @class Roo.data.Tree
25408  * @extends Roo.util.Observable
25409  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
25410  * in the tree have most standard DOM functionality.
25411  * @constructor
25412  * @param {Node} root (optional) The root node
25413  */
25414 Roo.data.Tree = function(root){
25415    this.nodeHash = {};
25416    /**
25417     * The root node for this tree
25418     * @type Node
25419     */
25420    this.root = null;
25421    if(root){
25422        this.setRootNode(root);
25423    }
25424    this.addEvents({
25425        /**
25426         * @event append
25427         * Fires when a new child node is appended to a node in this tree.
25428         * @param {Tree} tree The owner tree
25429         * @param {Node} parent The parent node
25430         * @param {Node} node The newly appended node
25431         * @param {Number} index The index of the newly appended node
25432         */
25433        "append" : true,
25434        /**
25435         * @event remove
25436         * Fires when a child node is removed from a node in this tree.
25437         * @param {Tree} tree The owner tree
25438         * @param {Node} parent The parent node
25439         * @param {Node} node The child node removed
25440         */
25441        "remove" : true,
25442        /**
25443         * @event move
25444         * Fires when a node is moved to a new location in the tree
25445         * @param {Tree} tree The owner tree
25446         * @param {Node} node The node moved
25447         * @param {Node} oldParent The old parent of this node
25448         * @param {Node} newParent The new parent of this node
25449         * @param {Number} index The index it was moved to
25450         */
25451        "move" : true,
25452        /**
25453         * @event insert
25454         * Fires when a new child node is inserted in a node in this tree.
25455         * @param {Tree} tree The owner tree
25456         * @param {Node} parent The parent node
25457         * @param {Node} node The child node inserted
25458         * @param {Node} refNode The child node the node was inserted before
25459         */
25460        "insert" : true,
25461        /**
25462         * @event beforeappend
25463         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
25464         * @param {Tree} tree The owner tree
25465         * @param {Node} parent The parent node
25466         * @param {Node} node The child node to be appended
25467         */
25468        "beforeappend" : true,
25469        /**
25470         * @event beforeremove
25471         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
25472         * @param {Tree} tree The owner tree
25473         * @param {Node} parent The parent node
25474         * @param {Node} node The child node to be removed
25475         */
25476        "beforeremove" : true,
25477        /**
25478         * @event beforemove
25479         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
25480         * @param {Tree} tree The owner tree
25481         * @param {Node} node The node being moved
25482         * @param {Node} oldParent The parent of the node
25483         * @param {Node} newParent The new parent the node is moving to
25484         * @param {Number} index The index it is being moved to
25485         */
25486        "beforemove" : true,
25487        /**
25488         * @event beforeinsert
25489         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
25490         * @param {Tree} tree The owner tree
25491         * @param {Node} parent The parent node
25492         * @param {Node} node The child node to be inserted
25493         * @param {Node} refNode The child node the node is being inserted before
25494         */
25495        "beforeinsert" : true
25496    });
25497
25498     Roo.data.Tree.superclass.constructor.call(this);
25499 };
25500
25501 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
25502     pathSeparator: "/",
25503
25504     proxyNodeEvent : function(){
25505         return this.fireEvent.apply(this, arguments);
25506     },
25507
25508     /**
25509      * Returns the root node for this tree.
25510      * @return {Node}
25511      */
25512     getRootNode : function(){
25513         return this.root;
25514     },
25515
25516     /**
25517      * Sets the root node for this tree.
25518      * @param {Node} node
25519      * @return {Node}
25520      */
25521     setRootNode : function(node){
25522         this.root = node;
25523         node.ownerTree = this;
25524         node.isRoot = true;
25525         this.registerNode(node);
25526         return node;
25527     },
25528
25529     /**
25530      * Gets a node in this tree by its id.
25531      * @param {String} id
25532      * @return {Node}
25533      */
25534     getNodeById : function(id){
25535         return this.nodeHash[id];
25536     },
25537
25538     registerNode : function(node){
25539         this.nodeHash[node.id] = node;
25540     },
25541
25542     unregisterNode : function(node){
25543         delete this.nodeHash[node.id];
25544     },
25545
25546     toString : function(){
25547         return "[Tree"+(this.id?" "+this.id:"")+"]";
25548     }
25549 });
25550
25551 /**
25552  * @class Roo.data.Node
25553  * @extends Roo.util.Observable
25554  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
25555  * @cfg {String} id The id for this node. If one is not specified, one is generated.
25556  * @constructor
25557  * @param {Object} attributes The attributes/config for the node
25558  */
25559 Roo.data.Node = function(attributes){
25560     /**
25561      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
25562      * @type {Object}
25563      */
25564     this.attributes = attributes || {};
25565     this.leaf = this.attributes.leaf;
25566     /**
25567      * The node id. @type String
25568      */
25569     this.id = this.attributes.id;
25570     if(!this.id){
25571         this.id = Roo.id(null, "ynode-");
25572         this.attributes.id = this.id;
25573     }
25574      
25575     
25576     /**
25577      * All child nodes of this node. @type Array
25578      */
25579     this.childNodes = [];
25580     if(!this.childNodes.indexOf){ // indexOf is a must
25581         this.childNodes.indexOf = function(o){
25582             for(var i = 0, len = this.length; i < len; i++){
25583                 if(this[i] == o) {
25584                     return i;
25585                 }
25586             }
25587             return -1;
25588         };
25589     }
25590     /**
25591      * The parent node for this node. @type Node
25592      */
25593     this.parentNode = null;
25594     /**
25595      * The first direct child node of this node, or null if this node has no child nodes. @type Node
25596      */
25597     this.firstChild = null;
25598     /**
25599      * The last direct child node of this node, or null if this node has no child nodes. @type Node
25600      */
25601     this.lastChild = null;
25602     /**
25603      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
25604      */
25605     this.previousSibling = null;
25606     /**
25607      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
25608      */
25609     this.nextSibling = null;
25610
25611     this.addEvents({
25612        /**
25613         * @event append
25614         * Fires when a new child node is appended
25615         * @param {Tree} tree The owner tree
25616         * @param {Node} this This node
25617         * @param {Node} node The newly appended node
25618         * @param {Number} index The index of the newly appended node
25619         */
25620        "append" : true,
25621        /**
25622         * @event remove
25623         * Fires when a child node is removed
25624         * @param {Tree} tree The owner tree
25625         * @param {Node} this This node
25626         * @param {Node} node The removed node
25627         */
25628        "remove" : true,
25629        /**
25630         * @event move
25631         * Fires when this node is moved to a new location in the tree
25632         * @param {Tree} tree The owner tree
25633         * @param {Node} this This node
25634         * @param {Node} oldParent The old parent of this node
25635         * @param {Node} newParent The new parent of this node
25636         * @param {Number} index The index it was moved to
25637         */
25638        "move" : true,
25639        /**
25640         * @event insert
25641         * Fires when a new child node is inserted.
25642         * @param {Tree} tree The owner tree
25643         * @param {Node} this This node
25644         * @param {Node} node The child node inserted
25645         * @param {Node} refNode The child node the node was inserted before
25646         */
25647        "insert" : true,
25648        /**
25649         * @event beforeappend
25650         * Fires before a new child is appended, return false to cancel the append.
25651         * @param {Tree} tree The owner tree
25652         * @param {Node} this This node
25653         * @param {Node} node The child node to be appended
25654         */
25655        "beforeappend" : true,
25656        /**
25657         * @event beforeremove
25658         * Fires before a child is removed, return false to cancel the remove.
25659         * @param {Tree} tree The owner tree
25660         * @param {Node} this This node
25661         * @param {Node} node The child node to be removed
25662         */
25663        "beforeremove" : true,
25664        /**
25665         * @event beforemove
25666         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
25667         * @param {Tree} tree The owner tree
25668         * @param {Node} this This node
25669         * @param {Node} oldParent The parent of this node
25670         * @param {Node} newParent The new parent this node is moving to
25671         * @param {Number} index The index it is being moved to
25672         */
25673        "beforemove" : true,
25674        /**
25675         * @event beforeinsert
25676         * Fires before a new child is inserted, return false to cancel the insert.
25677         * @param {Tree} tree The owner tree
25678         * @param {Node} this This node
25679         * @param {Node} node The child node to be inserted
25680         * @param {Node} refNode The child node the node is being inserted before
25681         */
25682        "beforeinsert" : true
25683    });
25684     this.listeners = this.attributes.listeners;
25685     Roo.data.Node.superclass.constructor.call(this);
25686 };
25687
25688 Roo.extend(Roo.data.Node, Roo.util.Observable, {
25689     fireEvent : function(evtName){
25690         // first do standard event for this node
25691         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
25692             return false;
25693         }
25694         // then bubble it up to the tree if the event wasn't cancelled
25695         var ot = this.getOwnerTree();
25696         if(ot){
25697             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
25698                 return false;
25699             }
25700         }
25701         return true;
25702     },
25703
25704     /**
25705      * Returns true if this node is a leaf
25706      * @return {Boolean}
25707      */
25708     isLeaf : function(){
25709         return this.leaf === true;
25710     },
25711
25712     // private
25713     setFirstChild : function(node){
25714         this.firstChild = node;
25715     },
25716
25717     //private
25718     setLastChild : function(node){
25719         this.lastChild = node;
25720     },
25721
25722
25723     /**
25724      * Returns true if this node is the last child of its parent
25725      * @return {Boolean}
25726      */
25727     isLast : function(){
25728        return (!this.parentNode ? true : this.parentNode.lastChild == this);
25729     },
25730
25731     /**
25732      * Returns true if this node is the first child of its parent
25733      * @return {Boolean}
25734      */
25735     isFirst : function(){
25736        return (!this.parentNode ? true : this.parentNode.firstChild == this);
25737     },
25738
25739     hasChildNodes : function(){
25740         return !this.isLeaf() && this.childNodes.length > 0;
25741     },
25742
25743     /**
25744      * Insert node(s) as the last child node of this node.
25745      * @param {Node/Array} node The node or Array of nodes to append
25746      * @return {Node} The appended node if single append, or null if an array was passed
25747      */
25748     appendChild : function(node){
25749         var multi = false;
25750         if(node instanceof Array){
25751             multi = node;
25752         }else if(arguments.length > 1){
25753             multi = arguments;
25754         }
25755         
25756         // if passed an array or multiple args do them one by one
25757         if(multi){
25758             for(var i = 0, len = multi.length; i < len; i++) {
25759                 this.appendChild(multi[i]);
25760             }
25761         }else{
25762             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
25763                 return false;
25764             }
25765             var index = this.childNodes.length;
25766             var oldParent = node.parentNode;
25767             // it's a move, make sure we move it cleanly
25768             if(oldParent){
25769                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
25770                     return false;
25771                 }
25772                 oldParent.removeChild(node);
25773             }
25774             
25775             index = this.childNodes.length;
25776             if(index == 0){
25777                 this.setFirstChild(node);
25778             }
25779             this.childNodes.push(node);
25780             node.parentNode = this;
25781             var ps = this.childNodes[index-1];
25782             if(ps){
25783                 node.previousSibling = ps;
25784                 ps.nextSibling = node;
25785             }else{
25786                 node.previousSibling = null;
25787             }
25788             node.nextSibling = null;
25789             this.setLastChild(node);
25790             node.setOwnerTree(this.getOwnerTree());
25791             this.fireEvent("append", this.ownerTree, this, node, index);
25792             if(this.ownerTree) {
25793                 this.ownerTree.fireEvent("appendnode", this, node, index);
25794             }
25795             if(oldParent){
25796                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
25797             }
25798             return node;
25799         }
25800     },
25801
25802     /**
25803      * Removes a child node from this node.
25804      * @param {Node} node The node to remove
25805      * @return {Node} The removed node
25806      */
25807     removeChild : function(node){
25808         var index = this.childNodes.indexOf(node);
25809         if(index == -1){
25810             return false;
25811         }
25812         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
25813             return false;
25814         }
25815
25816         // remove it from childNodes collection
25817         this.childNodes.splice(index, 1);
25818
25819         // update siblings
25820         if(node.previousSibling){
25821             node.previousSibling.nextSibling = node.nextSibling;
25822         }
25823         if(node.nextSibling){
25824             node.nextSibling.previousSibling = node.previousSibling;
25825         }
25826
25827         // update child refs
25828         if(this.firstChild == node){
25829             this.setFirstChild(node.nextSibling);
25830         }
25831         if(this.lastChild == node){
25832             this.setLastChild(node.previousSibling);
25833         }
25834
25835         node.setOwnerTree(null);
25836         // clear any references from the node
25837         node.parentNode = null;
25838         node.previousSibling = null;
25839         node.nextSibling = null;
25840         this.fireEvent("remove", this.ownerTree, this, node);
25841         return node;
25842     },
25843
25844     /**
25845      * Inserts the first node before the second node in this nodes childNodes collection.
25846      * @param {Node} node The node to insert
25847      * @param {Node} refNode The node to insert before (if null the node is appended)
25848      * @return {Node} The inserted node
25849      */
25850     insertBefore : function(node, refNode){
25851         if(!refNode){ // like standard Dom, refNode can be null for append
25852             return this.appendChild(node);
25853         }
25854         // nothing to do
25855         if(node == refNode){
25856             return false;
25857         }
25858
25859         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
25860             return false;
25861         }
25862         var index = this.childNodes.indexOf(refNode);
25863         var oldParent = node.parentNode;
25864         var refIndex = index;
25865
25866         // when moving internally, indexes will change after remove
25867         if(oldParent == this && this.childNodes.indexOf(node) < index){
25868             refIndex--;
25869         }
25870
25871         // it's a move, make sure we move it cleanly
25872         if(oldParent){
25873             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
25874                 return false;
25875             }
25876             oldParent.removeChild(node);
25877         }
25878         if(refIndex == 0){
25879             this.setFirstChild(node);
25880         }
25881         this.childNodes.splice(refIndex, 0, node);
25882         node.parentNode = this;
25883         var ps = this.childNodes[refIndex-1];
25884         if(ps){
25885             node.previousSibling = ps;
25886             ps.nextSibling = node;
25887         }else{
25888             node.previousSibling = null;
25889         }
25890         node.nextSibling = refNode;
25891         refNode.previousSibling = node;
25892         node.setOwnerTree(this.getOwnerTree());
25893         this.fireEvent("insert", this.ownerTree, this, node, refNode);
25894         if(oldParent){
25895             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
25896         }
25897         return node;
25898     },
25899
25900     /**
25901      * Returns the child node at the specified index.
25902      * @param {Number} index
25903      * @return {Node}
25904      */
25905     item : function(index){
25906         return this.childNodes[index];
25907     },
25908
25909     /**
25910      * Replaces one child node in this node with another.
25911      * @param {Node} newChild The replacement node
25912      * @param {Node} oldChild The node to replace
25913      * @return {Node} The replaced node
25914      */
25915     replaceChild : function(newChild, oldChild){
25916         this.insertBefore(newChild, oldChild);
25917         this.removeChild(oldChild);
25918         return oldChild;
25919     },
25920
25921     /**
25922      * Returns the index of a child node
25923      * @param {Node} node
25924      * @return {Number} The index of the node or -1 if it was not found
25925      */
25926     indexOf : function(child){
25927         return this.childNodes.indexOf(child);
25928     },
25929
25930     /**
25931      * Returns the tree this node is in.
25932      * @return {Tree}
25933      */
25934     getOwnerTree : function(){
25935         // if it doesn't have one, look for one
25936         if(!this.ownerTree){
25937             var p = this;
25938             while(p){
25939                 if(p.ownerTree){
25940                     this.ownerTree = p.ownerTree;
25941                     break;
25942                 }
25943                 p = p.parentNode;
25944             }
25945         }
25946         return this.ownerTree;
25947     },
25948
25949     /**
25950      * Returns depth of this node (the root node has a depth of 0)
25951      * @return {Number}
25952      */
25953     getDepth : function(){
25954         var depth = 0;
25955         var p = this;
25956         while(p.parentNode){
25957             ++depth;
25958             p = p.parentNode;
25959         }
25960         return depth;
25961     },
25962
25963     // private
25964     setOwnerTree : function(tree){
25965         // if it's move, we need to update everyone
25966         if(tree != this.ownerTree){
25967             if(this.ownerTree){
25968                 this.ownerTree.unregisterNode(this);
25969             }
25970             this.ownerTree = tree;
25971             var cs = this.childNodes;
25972             for(var i = 0, len = cs.length; i < len; i++) {
25973                 cs[i].setOwnerTree(tree);
25974             }
25975             if(tree){
25976                 tree.registerNode(this);
25977             }
25978         }
25979     },
25980
25981     /**
25982      * Returns the path for this node. The path can be used to expand or select this node programmatically.
25983      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
25984      * @return {String} The path
25985      */
25986     getPath : function(attr){
25987         attr = attr || "id";
25988         var p = this.parentNode;
25989         var b = [this.attributes[attr]];
25990         while(p){
25991             b.unshift(p.attributes[attr]);
25992             p = p.parentNode;
25993         }
25994         var sep = this.getOwnerTree().pathSeparator;
25995         return sep + b.join(sep);
25996     },
25997
25998     /**
25999      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26000      * function call will be the scope provided or the current node. The arguments to the function
26001      * will be the args provided or the current node. If the function returns false at any point,
26002      * the bubble is stopped.
26003      * @param {Function} fn The function to call
26004      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26005      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26006      */
26007     bubble : function(fn, scope, args){
26008         var p = this;
26009         while(p){
26010             if(fn.call(scope || p, args || p) === false){
26011                 break;
26012             }
26013             p = p.parentNode;
26014         }
26015     },
26016
26017     /**
26018      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26019      * function call will be the scope provided or the current node. The arguments to the function
26020      * will be the args provided or the current node. If the function returns false at any point,
26021      * the cascade is stopped on that branch.
26022      * @param {Function} fn The function to call
26023      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26024      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26025      */
26026     cascade : function(fn, scope, args){
26027         if(fn.call(scope || this, args || this) !== false){
26028             var cs = this.childNodes;
26029             for(var i = 0, len = cs.length; i < len; i++) {
26030                 cs[i].cascade(fn, scope, args);
26031             }
26032         }
26033     },
26034
26035     /**
26036      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26037      * function call will be the scope provided or the current node. The arguments to the function
26038      * will be the args provided or the current node. If the function returns false at any point,
26039      * the iteration stops.
26040      * @param {Function} fn The function to call
26041      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26042      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26043      */
26044     eachChild : function(fn, scope, args){
26045         var cs = this.childNodes;
26046         for(var i = 0, len = cs.length; i < len; i++) {
26047                 if(fn.call(scope || this, args || cs[i]) === false){
26048                     break;
26049                 }
26050         }
26051     },
26052
26053     /**
26054      * Finds the first child that has the attribute with the specified value.
26055      * @param {String} attribute The attribute name
26056      * @param {Mixed} value The value to search for
26057      * @return {Node} The found child or null if none was found
26058      */
26059     findChild : function(attribute, value){
26060         var cs = this.childNodes;
26061         for(var i = 0, len = cs.length; i < len; i++) {
26062                 if(cs[i].attributes[attribute] == value){
26063                     return cs[i];
26064                 }
26065         }
26066         return null;
26067     },
26068
26069     /**
26070      * Finds the first child by a custom function. The child matches if the function passed
26071      * returns true.
26072      * @param {Function} fn
26073      * @param {Object} scope (optional)
26074      * @return {Node} The found child or null if none was found
26075      */
26076     findChildBy : function(fn, scope){
26077         var cs = this.childNodes;
26078         for(var i = 0, len = cs.length; i < len; i++) {
26079                 if(fn.call(scope||cs[i], cs[i]) === true){
26080                     return cs[i];
26081                 }
26082         }
26083         return null;
26084     },
26085
26086     /**
26087      * Sorts this nodes children using the supplied sort function
26088      * @param {Function} fn
26089      * @param {Object} scope (optional)
26090      */
26091     sort : function(fn, scope){
26092         var cs = this.childNodes;
26093         var len = cs.length;
26094         if(len > 0){
26095             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26096             cs.sort(sortFn);
26097             for(var i = 0; i < len; i++){
26098                 var n = cs[i];
26099                 n.previousSibling = cs[i-1];
26100                 n.nextSibling = cs[i+1];
26101                 if(i == 0){
26102                     this.setFirstChild(n);
26103                 }
26104                 if(i == len-1){
26105                     this.setLastChild(n);
26106                 }
26107             }
26108         }
26109     },
26110
26111     /**
26112      * Returns true if this node is an ancestor (at any point) of the passed node.
26113      * @param {Node} node
26114      * @return {Boolean}
26115      */
26116     contains : function(node){
26117         return node.isAncestor(this);
26118     },
26119
26120     /**
26121      * Returns true if the passed node is an ancestor (at any point) of this node.
26122      * @param {Node} node
26123      * @return {Boolean}
26124      */
26125     isAncestor : function(node){
26126         var p = this.parentNode;
26127         while(p){
26128             if(p == node){
26129                 return true;
26130             }
26131             p = p.parentNode;
26132         }
26133         return false;
26134     },
26135
26136     toString : function(){
26137         return "[Node"+(this.id?" "+this.id:"")+"]";
26138     }
26139 });/*
26140  * Based on:
26141  * Ext JS Library 1.1.1
26142  * Copyright(c) 2006-2007, Ext JS, LLC.
26143  *
26144  * Originally Released Under LGPL - original licence link has changed is not relivant.
26145  *
26146  * Fork - LGPL
26147  * <script type="text/javascript">
26148  */
26149
26150
26151 /**
26152  * @class Roo.Shadow
26153  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26154  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26155  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26156  * @constructor
26157  * Create a new Shadow
26158  * @param {Object} config The config object
26159  */
26160 Roo.Shadow = function(config){
26161     Roo.apply(this, config);
26162     if(typeof this.mode != "string"){
26163         this.mode = this.defaultMode;
26164     }
26165     var o = this.offset, a = {h: 0};
26166     var rad = Math.floor(this.offset/2);
26167     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26168         case "drop":
26169             a.w = 0;
26170             a.l = a.t = o;
26171             a.t -= 1;
26172             if(Roo.isIE){
26173                 a.l -= this.offset + rad;
26174                 a.t -= this.offset + rad;
26175                 a.w -= rad;
26176                 a.h -= rad;
26177                 a.t += 1;
26178             }
26179         break;
26180         case "sides":
26181             a.w = (o*2);
26182             a.l = -o;
26183             a.t = o-1;
26184             if(Roo.isIE){
26185                 a.l -= (this.offset - rad);
26186                 a.t -= this.offset + rad;
26187                 a.l += 1;
26188                 a.w -= (this.offset - rad)*2;
26189                 a.w -= rad + 1;
26190                 a.h -= 1;
26191             }
26192         break;
26193         case "frame":
26194             a.w = a.h = (o*2);
26195             a.l = a.t = -o;
26196             a.t += 1;
26197             a.h -= 2;
26198             if(Roo.isIE){
26199                 a.l -= (this.offset - rad);
26200                 a.t -= (this.offset - rad);
26201                 a.l += 1;
26202                 a.w -= (this.offset + rad + 1);
26203                 a.h -= (this.offset + rad);
26204                 a.h += 1;
26205             }
26206         break;
26207     };
26208
26209     this.adjusts = a;
26210 };
26211
26212 Roo.Shadow.prototype = {
26213     /**
26214      * @cfg {String} mode
26215      * The shadow display mode.  Supports the following options:<br />
26216      * sides: Shadow displays on both sides and bottom only<br />
26217      * frame: Shadow displays equally on all four sides<br />
26218      * drop: Traditional bottom-right drop shadow (default)
26219      */
26220     /**
26221      * @cfg {String} offset
26222      * The number of pixels to offset the shadow from the element (defaults to 4)
26223      */
26224     offset: 4,
26225
26226     // private
26227     defaultMode: "drop",
26228
26229     /**
26230      * Displays the shadow under the target element
26231      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26232      */
26233     show : function(target){
26234         target = Roo.get(target);
26235         if(!this.el){
26236             this.el = Roo.Shadow.Pool.pull();
26237             if(this.el.dom.nextSibling != target.dom){
26238                 this.el.insertBefore(target);
26239             }
26240         }
26241         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26242         if(Roo.isIE){
26243             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26244         }
26245         this.realign(
26246             target.getLeft(true),
26247             target.getTop(true),
26248             target.getWidth(),
26249             target.getHeight()
26250         );
26251         this.el.dom.style.display = "block";
26252     },
26253
26254     /**
26255      * Returns true if the shadow is visible, else false
26256      */
26257     isVisible : function(){
26258         return this.el ? true : false;  
26259     },
26260
26261     /**
26262      * Direct alignment when values are already available. Show must be called at least once before
26263      * calling this method to ensure it is initialized.
26264      * @param {Number} left The target element left position
26265      * @param {Number} top The target element top position
26266      * @param {Number} width The target element width
26267      * @param {Number} height The target element height
26268      */
26269     realign : function(l, t, w, h){
26270         if(!this.el){
26271             return;
26272         }
26273         var a = this.adjusts, d = this.el.dom, s = d.style;
26274         var iea = 0;
26275         s.left = (l+a.l)+"px";
26276         s.top = (t+a.t)+"px";
26277         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
26278  
26279         if(s.width != sws || s.height != shs){
26280             s.width = sws;
26281             s.height = shs;
26282             if(!Roo.isIE){
26283                 var cn = d.childNodes;
26284                 var sww = Math.max(0, (sw-12))+"px";
26285                 cn[0].childNodes[1].style.width = sww;
26286                 cn[1].childNodes[1].style.width = sww;
26287                 cn[2].childNodes[1].style.width = sww;
26288                 cn[1].style.height = Math.max(0, (sh-12))+"px";
26289             }
26290         }
26291     },
26292
26293     /**
26294      * Hides this shadow
26295      */
26296     hide : function(){
26297         if(this.el){
26298             this.el.dom.style.display = "none";
26299             Roo.Shadow.Pool.push(this.el);
26300             delete this.el;
26301         }
26302     },
26303
26304     /**
26305      * Adjust the z-index of this shadow
26306      * @param {Number} zindex The new z-index
26307      */
26308     setZIndex : function(z){
26309         this.zIndex = z;
26310         if(this.el){
26311             this.el.setStyle("z-index", z);
26312         }
26313     }
26314 };
26315
26316 // Private utility class that manages the internal Shadow cache
26317 Roo.Shadow.Pool = function(){
26318     var p = [];
26319     var markup = Roo.isIE ?
26320                  '<div class="x-ie-shadow"></div>' :
26321                  '<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>';
26322     return {
26323         pull : function(){
26324             var sh = p.shift();
26325             if(!sh){
26326                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
26327                 sh.autoBoxAdjust = false;
26328             }
26329             return sh;
26330         },
26331
26332         push : function(sh){
26333             p.push(sh);
26334         }
26335     };
26336 }();/*
26337  * Based on:
26338  * Ext JS Library 1.1.1
26339  * Copyright(c) 2006-2007, Ext JS, LLC.
26340  *
26341  * Originally Released Under LGPL - original licence link has changed is not relivant.
26342  *
26343  * Fork - LGPL
26344  * <script type="text/javascript">
26345  */
26346
26347
26348 /**
26349  * @class Roo.SplitBar
26350  * @extends Roo.util.Observable
26351  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
26352  * <br><br>
26353  * Usage:
26354  * <pre><code>
26355 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
26356                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
26357 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
26358 split.minSize = 100;
26359 split.maxSize = 600;
26360 split.animate = true;
26361 split.on('moved', splitterMoved);
26362 </code></pre>
26363  * @constructor
26364  * Create a new SplitBar
26365  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
26366  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
26367  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26368  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
26369                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
26370                         position of the SplitBar).
26371  */
26372 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
26373     
26374     /** @private */
26375     this.el = Roo.get(dragElement, true);
26376     this.el.dom.unselectable = "on";
26377     /** @private */
26378     this.resizingEl = Roo.get(resizingElement, true);
26379
26380     /**
26381      * @private
26382      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
26383      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
26384      * @type Number
26385      */
26386     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
26387     
26388     /**
26389      * The minimum size of the resizing element. (Defaults to 0)
26390      * @type Number
26391      */
26392     this.minSize = 0;
26393     
26394     /**
26395      * The maximum size of the resizing element. (Defaults to 2000)
26396      * @type Number
26397      */
26398     this.maxSize = 2000;
26399     
26400     /**
26401      * Whether to animate the transition to the new size
26402      * @type Boolean
26403      */
26404     this.animate = false;
26405     
26406     /**
26407      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
26408      * @type Boolean
26409      */
26410     this.useShim = false;
26411     
26412     /** @private */
26413     this.shim = null;
26414     
26415     if(!existingProxy){
26416         /** @private */
26417         this.proxy = Roo.SplitBar.createProxy(this.orientation);
26418     }else{
26419         this.proxy = Roo.get(existingProxy).dom;
26420     }
26421     /** @private */
26422     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
26423     
26424     /** @private */
26425     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
26426     
26427     /** @private */
26428     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
26429     
26430     /** @private */
26431     this.dragSpecs = {};
26432     
26433     /**
26434      * @private The adapter to use to positon and resize elements
26435      */
26436     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
26437     this.adapter.init(this);
26438     
26439     if(this.orientation == Roo.SplitBar.HORIZONTAL){
26440         /** @private */
26441         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
26442         this.el.addClass("x-splitbar-h");
26443     }else{
26444         /** @private */
26445         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
26446         this.el.addClass("x-splitbar-v");
26447     }
26448     
26449     this.addEvents({
26450         /**
26451          * @event resize
26452          * Fires when the splitter is moved (alias for {@link #event-moved})
26453          * @param {Roo.SplitBar} this
26454          * @param {Number} newSize the new width or height
26455          */
26456         "resize" : true,
26457         /**
26458          * @event moved
26459          * Fires when the splitter is moved
26460          * @param {Roo.SplitBar} this
26461          * @param {Number} newSize the new width or height
26462          */
26463         "moved" : true,
26464         /**
26465          * @event beforeresize
26466          * Fires before the splitter is dragged
26467          * @param {Roo.SplitBar} this
26468          */
26469         "beforeresize" : true,
26470
26471         "beforeapply" : true
26472     });
26473
26474     Roo.util.Observable.call(this);
26475 };
26476
26477 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
26478     onStartProxyDrag : function(x, y){
26479         this.fireEvent("beforeresize", this);
26480         if(!this.overlay){
26481             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
26482             o.unselectable();
26483             o.enableDisplayMode("block");
26484             // all splitbars share the same overlay
26485             Roo.SplitBar.prototype.overlay = o;
26486         }
26487         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
26488         this.overlay.show();
26489         Roo.get(this.proxy).setDisplayed("block");
26490         var size = this.adapter.getElementSize(this);
26491         this.activeMinSize = this.getMinimumSize();;
26492         this.activeMaxSize = this.getMaximumSize();;
26493         var c1 = size - this.activeMinSize;
26494         var c2 = Math.max(this.activeMaxSize - size, 0);
26495         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26496             this.dd.resetConstraints();
26497             this.dd.setXConstraint(
26498                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
26499                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
26500             );
26501             this.dd.setYConstraint(0, 0);
26502         }else{
26503             this.dd.resetConstraints();
26504             this.dd.setXConstraint(0, 0);
26505             this.dd.setYConstraint(
26506                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
26507                 this.placement == Roo.SplitBar.TOP ? c2 : c1
26508             );
26509          }
26510         this.dragSpecs.startSize = size;
26511         this.dragSpecs.startPoint = [x, y];
26512         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
26513     },
26514     
26515     /** 
26516      * @private Called after the drag operation by the DDProxy
26517      */
26518     onEndProxyDrag : function(e){
26519         Roo.get(this.proxy).setDisplayed(false);
26520         var endPoint = Roo.lib.Event.getXY(e);
26521         if(this.overlay){
26522             this.overlay.hide();
26523         }
26524         var newSize;
26525         if(this.orientation == Roo.SplitBar.HORIZONTAL){
26526             newSize = this.dragSpecs.startSize + 
26527                 (this.placement == Roo.SplitBar.LEFT ?
26528                     endPoint[0] - this.dragSpecs.startPoint[0] :
26529                     this.dragSpecs.startPoint[0] - endPoint[0]
26530                 );
26531         }else{
26532             newSize = this.dragSpecs.startSize + 
26533                 (this.placement == Roo.SplitBar.TOP ?
26534                     endPoint[1] - this.dragSpecs.startPoint[1] :
26535                     this.dragSpecs.startPoint[1] - endPoint[1]
26536                 );
26537         }
26538         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
26539         if(newSize != this.dragSpecs.startSize){
26540             if(this.fireEvent('beforeapply', this, newSize) !== false){
26541                 this.adapter.setElementSize(this, newSize);
26542                 this.fireEvent("moved", this, newSize);
26543                 this.fireEvent("resize", this, newSize);
26544             }
26545         }
26546     },
26547     
26548     /**
26549      * Get the adapter this SplitBar uses
26550      * @return The adapter object
26551      */
26552     getAdapter : function(){
26553         return this.adapter;
26554     },
26555     
26556     /**
26557      * Set the adapter this SplitBar uses
26558      * @param {Object} adapter A SplitBar adapter object
26559      */
26560     setAdapter : function(adapter){
26561         this.adapter = adapter;
26562         this.adapter.init(this);
26563     },
26564     
26565     /**
26566      * Gets the minimum size for the resizing element
26567      * @return {Number} The minimum size
26568      */
26569     getMinimumSize : function(){
26570         return this.minSize;
26571     },
26572     
26573     /**
26574      * Sets the minimum size for the resizing element
26575      * @param {Number} minSize The minimum size
26576      */
26577     setMinimumSize : function(minSize){
26578         this.minSize = minSize;
26579     },
26580     
26581     /**
26582      * Gets the maximum size for the resizing element
26583      * @return {Number} The maximum size
26584      */
26585     getMaximumSize : function(){
26586         return this.maxSize;
26587     },
26588     
26589     /**
26590      * Sets the maximum size for the resizing element
26591      * @param {Number} maxSize The maximum size
26592      */
26593     setMaximumSize : function(maxSize){
26594         this.maxSize = maxSize;
26595     },
26596     
26597     /**
26598      * Sets the initialize size for the resizing element
26599      * @param {Number} size The initial size
26600      */
26601     setCurrentSize : function(size){
26602         var oldAnimate = this.animate;
26603         this.animate = false;
26604         this.adapter.setElementSize(this, size);
26605         this.animate = oldAnimate;
26606     },
26607     
26608     /**
26609      * Destroy this splitbar. 
26610      * @param {Boolean} removeEl True to remove the element
26611      */
26612     destroy : function(removeEl){
26613         if(this.shim){
26614             this.shim.remove();
26615         }
26616         this.dd.unreg();
26617         this.proxy.parentNode.removeChild(this.proxy);
26618         if(removeEl){
26619             this.el.remove();
26620         }
26621     }
26622 });
26623
26624 /**
26625  * @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.
26626  */
26627 Roo.SplitBar.createProxy = function(dir){
26628     var proxy = new Roo.Element(document.createElement("div"));
26629     proxy.unselectable();
26630     var cls = 'x-splitbar-proxy';
26631     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
26632     document.body.appendChild(proxy.dom);
26633     return proxy.dom;
26634 };
26635
26636 /** 
26637  * @class Roo.SplitBar.BasicLayoutAdapter
26638  * Default Adapter. It assumes the splitter and resizing element are not positioned
26639  * elements and only gets/sets the width of the element. Generally used for table based layouts.
26640  */
26641 Roo.SplitBar.BasicLayoutAdapter = function(){
26642 };
26643
26644 Roo.SplitBar.BasicLayoutAdapter.prototype = {
26645     // do nothing for now
26646     init : function(s){
26647     
26648     },
26649     /**
26650      * Called before drag operations to get the current size of the resizing element. 
26651      * @param {Roo.SplitBar} s The SplitBar using this adapter
26652      */
26653      getElementSize : function(s){
26654         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26655             return s.resizingEl.getWidth();
26656         }else{
26657             return s.resizingEl.getHeight();
26658         }
26659     },
26660     
26661     /**
26662      * Called after drag operations to set the size of the resizing element.
26663      * @param {Roo.SplitBar} s The SplitBar using this adapter
26664      * @param {Number} newSize The new size to set
26665      * @param {Function} onComplete A function to be invoked when resizing is complete
26666      */
26667     setElementSize : function(s, newSize, onComplete){
26668         if(s.orientation == Roo.SplitBar.HORIZONTAL){
26669             if(!s.animate){
26670                 s.resizingEl.setWidth(newSize);
26671                 if(onComplete){
26672                     onComplete(s, newSize);
26673                 }
26674             }else{
26675                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
26676             }
26677         }else{
26678             
26679             if(!s.animate){
26680                 s.resizingEl.setHeight(newSize);
26681                 if(onComplete){
26682                     onComplete(s, newSize);
26683                 }
26684             }else{
26685                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
26686             }
26687         }
26688     }
26689 };
26690
26691 /** 
26692  *@class Roo.SplitBar.AbsoluteLayoutAdapter
26693  * @extends Roo.SplitBar.BasicLayoutAdapter
26694  * Adapter that  moves the splitter element to align with the resized sizing element. 
26695  * Used with an absolute positioned SplitBar.
26696  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
26697  * document.body, make sure you assign an id to the body element.
26698  */
26699 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
26700     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
26701     this.container = Roo.get(container);
26702 };
26703
26704 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
26705     init : function(s){
26706         this.basic.init(s);
26707     },
26708     
26709     getElementSize : function(s){
26710         return this.basic.getElementSize(s);
26711     },
26712     
26713     setElementSize : function(s, newSize, onComplete){
26714         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
26715     },
26716     
26717     moveSplitter : function(s){
26718         var yes = Roo.SplitBar;
26719         switch(s.placement){
26720             case yes.LEFT:
26721                 s.el.setX(s.resizingEl.getRight());
26722                 break;
26723             case yes.RIGHT:
26724                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
26725                 break;
26726             case yes.TOP:
26727                 s.el.setY(s.resizingEl.getBottom());
26728                 break;
26729             case yes.BOTTOM:
26730                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
26731                 break;
26732         }
26733     }
26734 };
26735
26736 /**
26737  * Orientation constant - Create a vertical SplitBar
26738  * @static
26739  * @type Number
26740  */
26741 Roo.SplitBar.VERTICAL = 1;
26742
26743 /**
26744  * Orientation constant - Create a horizontal SplitBar
26745  * @static
26746  * @type Number
26747  */
26748 Roo.SplitBar.HORIZONTAL = 2;
26749
26750 /**
26751  * Placement constant - The resizing element is to the left of the splitter element
26752  * @static
26753  * @type Number
26754  */
26755 Roo.SplitBar.LEFT = 1;
26756
26757 /**
26758  * Placement constant - The resizing element is to the right of the splitter element
26759  * @static
26760  * @type Number
26761  */
26762 Roo.SplitBar.RIGHT = 2;
26763
26764 /**
26765  * Placement constant - The resizing element is positioned above the splitter element
26766  * @static
26767  * @type Number
26768  */
26769 Roo.SplitBar.TOP = 3;
26770
26771 /**
26772  * Placement constant - The resizing element is positioned under splitter element
26773  * @static
26774  * @type Number
26775  */
26776 Roo.SplitBar.BOTTOM = 4;
26777 /*
26778  * Based on:
26779  * Ext JS Library 1.1.1
26780  * Copyright(c) 2006-2007, Ext JS, LLC.
26781  *
26782  * Originally Released Under LGPL - original licence link has changed is not relivant.
26783  *
26784  * Fork - LGPL
26785  * <script type="text/javascript">
26786  */
26787
26788 /**
26789  * @class Roo.View
26790  * @extends Roo.util.Observable
26791  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
26792  * This class also supports single and multi selection modes. <br>
26793  * Create a data model bound view:
26794  <pre><code>
26795  var store = new Roo.data.Store(...);
26796
26797  var view = new Roo.View({
26798     el : "my-element",
26799     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
26800  
26801     singleSelect: true,
26802     selectedClass: "ydataview-selected",
26803     store: store
26804  });
26805
26806  // listen for node click?
26807  view.on("click", function(vw, index, node, e){
26808  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
26809  });
26810
26811  // load XML data
26812  dataModel.load("foobar.xml");
26813  </code></pre>
26814  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
26815  * <br><br>
26816  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
26817  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
26818  * 
26819  * Note: old style constructor is still suported (container, template, config)
26820  * 
26821  * @constructor
26822  * Create a new View
26823  * @param {Object} config The config object
26824  * 
26825  */
26826 Roo.View = function(config, depreciated_tpl, depreciated_config){
26827     
26828     this.parent = false;
26829     
26830     if (typeof(depreciated_tpl) == 'undefined') {
26831         // new way.. - universal constructor.
26832         Roo.apply(this, config);
26833         this.el  = Roo.get(this.el);
26834     } else {
26835         // old format..
26836         this.el  = Roo.get(config);
26837         this.tpl = depreciated_tpl;
26838         Roo.apply(this, depreciated_config);
26839     }
26840     this.wrapEl  = this.el.wrap().wrap();
26841     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
26842     
26843     
26844     if(typeof(this.tpl) == "string"){
26845         this.tpl = new Roo.Template(this.tpl);
26846     } else {
26847         // support xtype ctors..
26848         this.tpl = new Roo.factory(this.tpl, Roo);
26849     }
26850     
26851     
26852     this.tpl.compile();
26853     
26854     /** @private */
26855     this.addEvents({
26856         /**
26857          * @event beforeclick
26858          * Fires before a click is processed. Returns false to cancel the default action.
26859          * @param {Roo.View} this
26860          * @param {Number} index The index of the target node
26861          * @param {HTMLElement} node The target node
26862          * @param {Roo.EventObject} e The raw event object
26863          */
26864             "beforeclick" : true,
26865         /**
26866          * @event click
26867          * Fires when a template node is clicked.
26868          * @param {Roo.View} this
26869          * @param {Number} index The index of the target node
26870          * @param {HTMLElement} node The target node
26871          * @param {Roo.EventObject} e The raw event object
26872          */
26873             "click" : true,
26874         /**
26875          * @event dblclick
26876          * Fires when a template node is double clicked.
26877          * @param {Roo.View} this
26878          * @param {Number} index The index of the target node
26879          * @param {HTMLElement} node The target node
26880          * @param {Roo.EventObject} e The raw event object
26881          */
26882             "dblclick" : true,
26883         /**
26884          * @event contextmenu
26885          * Fires when a template node is right clicked.
26886          * @param {Roo.View} this
26887          * @param {Number} index The index of the target node
26888          * @param {HTMLElement} node The target node
26889          * @param {Roo.EventObject} e The raw event object
26890          */
26891             "contextmenu" : true,
26892         /**
26893          * @event selectionchange
26894          * Fires when the selected nodes change.
26895          * @param {Roo.View} this
26896          * @param {Array} selections Array of the selected nodes
26897          */
26898             "selectionchange" : true,
26899     
26900         /**
26901          * @event beforeselect
26902          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
26903          * @param {Roo.View} this
26904          * @param {HTMLElement} node The node to be selected
26905          * @param {Array} selections Array of currently selected nodes
26906          */
26907             "beforeselect" : true,
26908         /**
26909          * @event preparedata
26910          * Fires on every row to render, to allow you to change the data.
26911          * @param {Roo.View} this
26912          * @param {Object} data to be rendered (change this)
26913          */
26914           "preparedata" : true
26915           
26916           
26917         });
26918
26919
26920
26921     this.el.on({
26922         "click": this.onClick,
26923         "dblclick": this.onDblClick,
26924         "contextmenu": this.onContextMenu,
26925         scope:this
26926     });
26927
26928     this.selections = [];
26929     this.nodes = [];
26930     this.cmp = new Roo.CompositeElementLite([]);
26931     if(this.store){
26932         this.store = Roo.factory(this.store, Roo.data);
26933         this.setStore(this.store, true);
26934     }
26935     
26936     if ( this.footer && this.footer.xtype) {
26937            
26938          var fctr = this.wrapEl.appendChild(document.createElement("div"));
26939         
26940         this.footer.dataSource = this.store;
26941         this.footer.container = fctr;
26942         this.footer = Roo.factory(this.footer, Roo);
26943         fctr.insertFirst(this.el);
26944         
26945         // this is a bit insane - as the paging toolbar seems to detach the el..
26946 //        dom.parentNode.parentNode.parentNode
26947          // they get detached?
26948     }
26949     
26950     
26951     Roo.View.superclass.constructor.call(this);
26952     
26953     
26954 };
26955
26956 Roo.extend(Roo.View, Roo.util.Observable, {
26957     
26958      /**
26959      * @cfg {Roo.data.Store} store Data store to load data from.
26960      */
26961     store : false,
26962     
26963     /**
26964      * @cfg {String|Roo.Element} el The container element.
26965      */
26966     el : '',
26967     
26968     /**
26969      * @cfg {String|Roo.Template} tpl The template used by this View 
26970      */
26971     tpl : false,
26972     /**
26973      * @cfg {String} dataName the named area of the template to use as the data area
26974      *                          Works with domtemplates roo-name="name"
26975      */
26976     dataName: false,
26977     /**
26978      * @cfg {String} selectedClass The css class to add to selected nodes
26979      */
26980     selectedClass : "x-view-selected",
26981      /**
26982      * @cfg {String} emptyText The empty text to show when nothing is loaded.
26983      */
26984     emptyText : "",
26985     
26986     /**
26987      * @cfg {String} text to display on mask (default Loading)
26988      */
26989     mask : false,
26990     /**
26991      * @cfg {Boolean} multiSelect Allow multiple selection
26992      */
26993     multiSelect : false,
26994     /**
26995      * @cfg {Boolean} singleSelect Allow single selection
26996      */
26997     singleSelect:  false,
26998     
26999     /**
27000      * @cfg {Boolean} toggleSelect - selecting 
27001      */
27002     toggleSelect : false,
27003     
27004     /**
27005      * @cfg {Boolean} tickable - selecting 
27006      */
27007     tickable : false,
27008     
27009     /**
27010      * Returns the element this view is bound to.
27011      * @return {Roo.Element}
27012      */
27013     getEl : function(){
27014         return this.wrapEl;
27015     },
27016     
27017     
27018
27019     /**
27020      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27021      */
27022     refresh : function(){
27023         //Roo.log('refresh');
27024         var t = this.tpl;
27025         
27026         // if we are using something like 'domtemplate', then
27027         // the what gets used is:
27028         // t.applySubtemplate(NAME, data, wrapping data..)
27029         // the outer template then get' applied with
27030         //     the store 'extra data'
27031         // and the body get's added to the
27032         //      roo-name="data" node?
27033         //      <span class='roo-tpl-{name}'></span> ?????
27034         
27035         
27036         
27037         this.clearSelections();
27038         this.el.update("");
27039         var html = [];
27040         var records = this.store.getRange();
27041         if(records.length < 1) {
27042             
27043             // is this valid??  = should it render a template??
27044             
27045             this.el.update(this.emptyText);
27046             return;
27047         }
27048         var el = this.el;
27049         if (this.dataName) {
27050             this.el.update(t.apply(this.store.meta)); //????
27051             el = this.el.child('.roo-tpl-' + this.dataName);
27052         }
27053         
27054         for(var i = 0, len = records.length; i < len; i++){
27055             var data = this.prepareData(records[i].data, i, records[i]);
27056             this.fireEvent("preparedata", this, data, i, records[i]);
27057             
27058             var d = Roo.apply({}, data);
27059             
27060             if(this.tickable){
27061                 Roo.apply(d, {'roo-id' : Roo.id()});
27062                 
27063                 var _this = this;
27064             
27065                 Roo.each(this.parent.item, function(item){
27066                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27067                         return;
27068                     }
27069                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27070                 });
27071             }
27072             
27073             html[html.length] = Roo.util.Format.trim(
27074                 this.dataName ?
27075                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27076                     t.apply(d)
27077             );
27078         }
27079         
27080         
27081         
27082         el.update(html.join(""));
27083         this.nodes = el.dom.childNodes;
27084         this.updateIndexes(0);
27085     },
27086     
27087
27088     /**
27089      * Function to override to reformat the data that is sent to
27090      * the template for each node.
27091      * DEPRICATED - use the preparedata event handler.
27092      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27093      * a JSON object for an UpdateManager bound view).
27094      */
27095     prepareData : function(data, index, record)
27096     {
27097         this.fireEvent("preparedata", this, data, index, record);
27098         return data;
27099     },
27100
27101     onUpdate : function(ds, record){
27102         // Roo.log('on update');   
27103         this.clearSelections();
27104         var index = this.store.indexOf(record);
27105         var n = this.nodes[index];
27106         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27107         n.parentNode.removeChild(n);
27108         this.updateIndexes(index, index);
27109     },
27110
27111     
27112     
27113 // --------- FIXME     
27114     onAdd : function(ds, records, index)
27115     {
27116         //Roo.log(['on Add', ds, records, index] );        
27117         this.clearSelections();
27118         if(this.nodes.length == 0){
27119             this.refresh();
27120             return;
27121         }
27122         var n = this.nodes[index];
27123         for(var i = 0, len = records.length; i < len; i++){
27124             var d = this.prepareData(records[i].data, i, records[i]);
27125             if(n){
27126                 this.tpl.insertBefore(n, d);
27127             }else{
27128                 
27129                 this.tpl.append(this.el, d);
27130             }
27131         }
27132         this.updateIndexes(index);
27133     },
27134
27135     onRemove : function(ds, record, index){
27136        // Roo.log('onRemove');
27137         this.clearSelections();
27138         var el = this.dataName  ?
27139             this.el.child('.roo-tpl-' + this.dataName) :
27140             this.el; 
27141         
27142         el.dom.removeChild(this.nodes[index]);
27143         this.updateIndexes(index);
27144     },
27145
27146     /**
27147      * Refresh an individual node.
27148      * @param {Number} index
27149      */
27150     refreshNode : function(index){
27151         this.onUpdate(this.store, this.store.getAt(index));
27152     },
27153
27154     updateIndexes : function(startIndex, endIndex){
27155         var ns = this.nodes;
27156         startIndex = startIndex || 0;
27157         endIndex = endIndex || ns.length - 1;
27158         for(var i = startIndex; i <= endIndex; i++){
27159             ns[i].nodeIndex = i;
27160         }
27161     },
27162
27163     /**
27164      * Changes the data store this view uses and refresh the view.
27165      * @param {Store} store
27166      */
27167     setStore : function(store, initial){
27168         if(!initial && this.store){
27169             this.store.un("datachanged", this.refresh);
27170             this.store.un("add", this.onAdd);
27171             this.store.un("remove", this.onRemove);
27172             this.store.un("update", this.onUpdate);
27173             this.store.un("clear", this.refresh);
27174             this.store.un("beforeload", this.onBeforeLoad);
27175             this.store.un("load", this.onLoad);
27176             this.store.un("loadexception", this.onLoad);
27177         }
27178         if(store){
27179           
27180             store.on("datachanged", this.refresh, this);
27181             store.on("add", this.onAdd, this);
27182             store.on("remove", this.onRemove, this);
27183             store.on("update", this.onUpdate, this);
27184             store.on("clear", this.refresh, this);
27185             store.on("beforeload", this.onBeforeLoad, this);
27186             store.on("load", this.onLoad, this);
27187             store.on("loadexception", this.onLoad, this);
27188         }
27189         
27190         if(store){
27191             this.refresh();
27192         }
27193     },
27194     /**
27195      * onbeforeLoad - masks the loading area.
27196      *
27197      */
27198     onBeforeLoad : function(store,opts)
27199     {
27200          //Roo.log('onBeforeLoad');   
27201         if (!opts.add) {
27202             this.el.update("");
27203         }
27204         this.el.mask(this.mask ? this.mask : "Loading" ); 
27205     },
27206     onLoad : function ()
27207     {
27208         this.el.unmask();
27209     },
27210     
27211
27212     /**
27213      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27214      * @param {HTMLElement} node
27215      * @return {HTMLElement} The template node
27216      */
27217     findItemFromChild : function(node){
27218         var el = this.dataName  ?
27219             this.el.child('.roo-tpl-' + this.dataName,true) :
27220             this.el.dom; 
27221         
27222         if(!node || node.parentNode == el){
27223                     return node;
27224             }
27225             var p = node.parentNode;
27226             while(p && p != el){
27227             if(p.parentNode == el){
27228                 return p;
27229             }
27230             p = p.parentNode;
27231         }
27232             return null;
27233     },
27234
27235     /** @ignore */
27236     onClick : function(e){
27237         var item = this.findItemFromChild(e.getTarget());
27238         if(item){
27239             var index = this.indexOf(item);
27240             if(this.onItemClick(item, index, e) !== false){
27241                 this.fireEvent("click", this, index, item, e);
27242             }
27243         }else{
27244             this.clearSelections();
27245         }
27246     },
27247
27248     /** @ignore */
27249     onContextMenu : function(e){
27250         var item = this.findItemFromChild(e.getTarget());
27251         if(item){
27252             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27253         }
27254     },
27255
27256     /** @ignore */
27257     onDblClick : function(e){
27258         var item = this.findItemFromChild(e.getTarget());
27259         if(item){
27260             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
27261         }
27262     },
27263
27264     onItemClick : function(item, index, e)
27265     {
27266         if(this.fireEvent("beforeclick", this, index, item, e) === false){
27267             return false;
27268         }
27269         if (this.toggleSelect) {
27270             var m = this.isSelected(item) ? 'unselect' : 'select';
27271             //Roo.log(m);
27272             var _t = this;
27273             _t[m](item, true, false);
27274             return true;
27275         }
27276         if(this.multiSelect || this.singleSelect){
27277             if(this.multiSelect && e.shiftKey && this.lastSelection){
27278                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
27279             }else{
27280                 this.select(item, this.multiSelect && e.ctrlKey);
27281                 this.lastSelection = item;
27282             }
27283             
27284             if(!this.tickable){
27285                 e.preventDefault();
27286             }
27287             
27288         }
27289         return true;
27290     },
27291
27292     /**
27293      * Get the number of selected nodes.
27294      * @return {Number}
27295      */
27296     getSelectionCount : function(){
27297         return this.selections.length;
27298     },
27299
27300     /**
27301      * Get the currently selected nodes.
27302      * @return {Array} An array of HTMLElements
27303      */
27304     getSelectedNodes : function(){
27305         return this.selections;
27306     },
27307
27308     /**
27309      * Get the indexes of the selected nodes.
27310      * @return {Array}
27311      */
27312     getSelectedIndexes : function(){
27313         var indexes = [], s = this.selections;
27314         for(var i = 0, len = s.length; i < len; i++){
27315             indexes.push(s[i].nodeIndex);
27316         }
27317         return indexes;
27318     },
27319
27320     /**
27321      * Clear all selections
27322      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
27323      */
27324     clearSelections : function(suppressEvent){
27325         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
27326             this.cmp.elements = this.selections;
27327             this.cmp.removeClass(this.selectedClass);
27328             this.selections = [];
27329             if(!suppressEvent){
27330                 this.fireEvent("selectionchange", this, this.selections);
27331             }
27332         }
27333     },
27334
27335     /**
27336      * Returns true if the passed node is selected
27337      * @param {HTMLElement/Number} node The node or node index
27338      * @return {Boolean}
27339      */
27340     isSelected : function(node){
27341         var s = this.selections;
27342         if(s.length < 1){
27343             return false;
27344         }
27345         node = this.getNode(node);
27346         return s.indexOf(node) !== -1;
27347     },
27348
27349     /**
27350      * Selects nodes.
27351      * @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
27352      * @param {Boolean} keepExisting (optional) true to keep existing selections
27353      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27354      */
27355     select : function(nodeInfo, keepExisting, suppressEvent){
27356         if(nodeInfo instanceof Array){
27357             if(!keepExisting){
27358                 this.clearSelections(true);
27359             }
27360             for(var i = 0, len = nodeInfo.length; i < len; i++){
27361                 this.select(nodeInfo[i], true, true);
27362             }
27363             return;
27364         } 
27365         var node = this.getNode(nodeInfo);
27366         if(!node || this.isSelected(node)){
27367             return; // already selected.
27368         }
27369         if(!keepExisting){
27370             this.clearSelections(true);
27371         }
27372         
27373         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
27374             Roo.fly(node).addClass(this.selectedClass);
27375             this.selections.push(node);
27376             if(!suppressEvent){
27377                 this.fireEvent("selectionchange", this, this.selections);
27378             }
27379         }
27380         
27381         
27382     },
27383       /**
27384      * Unselects nodes.
27385      * @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
27386      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
27387      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
27388      */
27389     unselect : function(nodeInfo, keepExisting, suppressEvent)
27390     {
27391         if(nodeInfo instanceof Array){
27392             Roo.each(this.selections, function(s) {
27393                 this.unselect(s, nodeInfo);
27394             }, this);
27395             return;
27396         }
27397         var node = this.getNode(nodeInfo);
27398         if(!node || !this.isSelected(node)){
27399             //Roo.log("not selected");
27400             return; // not selected.
27401         }
27402         // fireevent???
27403         var ns = [];
27404         Roo.each(this.selections, function(s) {
27405             if (s == node ) {
27406                 Roo.fly(node).removeClass(this.selectedClass);
27407
27408                 return;
27409             }
27410             ns.push(s);
27411         },this);
27412         
27413         this.selections= ns;
27414         this.fireEvent("selectionchange", this, this.selections);
27415     },
27416
27417     /**
27418      * Gets a template node.
27419      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27420      * @return {HTMLElement} The node or null if it wasn't found
27421      */
27422     getNode : function(nodeInfo){
27423         if(typeof nodeInfo == "string"){
27424             return document.getElementById(nodeInfo);
27425         }else if(typeof nodeInfo == "number"){
27426             return this.nodes[nodeInfo];
27427         }
27428         return nodeInfo;
27429     },
27430
27431     /**
27432      * Gets a range template nodes.
27433      * @param {Number} startIndex
27434      * @param {Number} endIndex
27435      * @return {Array} An array of nodes
27436      */
27437     getNodes : function(start, end){
27438         var ns = this.nodes;
27439         start = start || 0;
27440         end = typeof end == "undefined" ? ns.length - 1 : end;
27441         var nodes = [];
27442         if(start <= end){
27443             for(var i = start; i <= end; i++){
27444                 nodes.push(ns[i]);
27445             }
27446         } else{
27447             for(var i = start; i >= end; i--){
27448                 nodes.push(ns[i]);
27449             }
27450         }
27451         return nodes;
27452     },
27453
27454     /**
27455      * Finds the index of the passed node
27456      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
27457      * @return {Number} The index of the node or -1
27458      */
27459     indexOf : function(node){
27460         node = this.getNode(node);
27461         if(typeof node.nodeIndex == "number"){
27462             return node.nodeIndex;
27463         }
27464         var ns = this.nodes;
27465         for(var i = 0, len = ns.length; i < len; i++){
27466             if(ns[i] == node){
27467                 return i;
27468             }
27469         }
27470         return -1;
27471     }
27472 });
27473 /*
27474  * Based on:
27475  * Ext JS Library 1.1.1
27476  * Copyright(c) 2006-2007, Ext JS, LLC.
27477  *
27478  * Originally Released Under LGPL - original licence link has changed is not relivant.
27479  *
27480  * Fork - LGPL
27481  * <script type="text/javascript">
27482  */
27483
27484 /**
27485  * @class Roo.JsonView
27486  * @extends Roo.View
27487  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
27488 <pre><code>
27489 var view = new Roo.JsonView({
27490     container: "my-element",
27491     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
27492     multiSelect: true, 
27493     jsonRoot: "data" 
27494 });
27495
27496 // listen for node click?
27497 view.on("click", function(vw, index, node, e){
27498     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27499 });
27500
27501 // direct load of JSON data
27502 view.load("foobar.php");
27503
27504 // Example from my blog list
27505 var tpl = new Roo.Template(
27506     '&lt;div class="entry"&gt;' +
27507     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
27508     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
27509     "&lt;/div&gt;&lt;hr /&gt;"
27510 );
27511
27512 var moreView = new Roo.JsonView({
27513     container :  "entry-list", 
27514     template : tpl,
27515     jsonRoot: "posts"
27516 });
27517 moreView.on("beforerender", this.sortEntries, this);
27518 moreView.load({
27519     url: "/blog/get-posts.php",
27520     params: "allposts=true",
27521     text: "Loading Blog Entries..."
27522 });
27523 </code></pre>
27524
27525 * Note: old code is supported with arguments : (container, template, config)
27526
27527
27528  * @constructor
27529  * Create a new JsonView
27530  * 
27531  * @param {Object} config The config object
27532  * 
27533  */
27534 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
27535     
27536     
27537     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
27538
27539     var um = this.el.getUpdateManager();
27540     um.setRenderer(this);
27541     um.on("update", this.onLoad, this);
27542     um.on("failure", this.onLoadException, this);
27543
27544     /**
27545      * @event beforerender
27546      * Fires before rendering of the downloaded JSON data.
27547      * @param {Roo.JsonView} this
27548      * @param {Object} data The JSON data loaded
27549      */
27550     /**
27551      * @event load
27552      * Fires when data is loaded.
27553      * @param {Roo.JsonView} this
27554      * @param {Object} data The JSON data loaded
27555      * @param {Object} response The raw Connect response object
27556      */
27557     /**
27558      * @event loadexception
27559      * Fires when loading fails.
27560      * @param {Roo.JsonView} this
27561      * @param {Object} response The raw Connect response object
27562      */
27563     this.addEvents({
27564         'beforerender' : true,
27565         'load' : true,
27566         'loadexception' : true
27567     });
27568 };
27569 Roo.extend(Roo.JsonView, Roo.View, {
27570     /**
27571      * @type {String} The root property in the loaded JSON object that contains the data
27572      */
27573     jsonRoot : "",
27574
27575     /**
27576      * Refreshes the view.
27577      */
27578     refresh : function(){
27579         this.clearSelections();
27580         this.el.update("");
27581         var html = [];
27582         var o = this.jsonData;
27583         if(o && o.length > 0){
27584             for(var i = 0, len = o.length; i < len; i++){
27585                 var data = this.prepareData(o[i], i, o);
27586                 html[html.length] = this.tpl.apply(data);
27587             }
27588         }else{
27589             html.push(this.emptyText);
27590         }
27591         this.el.update(html.join(""));
27592         this.nodes = this.el.dom.childNodes;
27593         this.updateIndexes(0);
27594     },
27595
27596     /**
27597      * 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.
27598      * @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:
27599      <pre><code>
27600      view.load({
27601          url: "your-url.php",
27602          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
27603          callback: yourFunction,
27604          scope: yourObject, //(optional scope)
27605          discardUrl: false,
27606          nocache: false,
27607          text: "Loading...",
27608          timeout: 30,
27609          scripts: false
27610      });
27611      </code></pre>
27612      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
27613      * 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.
27614      * @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}
27615      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
27616      * @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.
27617      */
27618     load : function(){
27619         var um = this.el.getUpdateManager();
27620         um.update.apply(um, arguments);
27621     },
27622
27623     // note - render is a standard framework call...
27624     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
27625     render : function(el, response){
27626         
27627         this.clearSelections();
27628         this.el.update("");
27629         var o;
27630         try{
27631             if (response != '') {
27632                 o = Roo.util.JSON.decode(response.responseText);
27633                 if(this.jsonRoot){
27634                     
27635                     o = o[this.jsonRoot];
27636                 }
27637             }
27638         } catch(e){
27639         }
27640         /**
27641          * The current JSON data or null
27642          */
27643         this.jsonData = o;
27644         this.beforeRender();
27645         this.refresh();
27646     },
27647
27648 /**
27649  * Get the number of records in the current JSON dataset
27650  * @return {Number}
27651  */
27652     getCount : function(){
27653         return this.jsonData ? this.jsonData.length : 0;
27654     },
27655
27656 /**
27657  * Returns the JSON object for the specified node(s)
27658  * @param {HTMLElement/Array} node The node or an array of nodes
27659  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
27660  * you get the JSON object for the node
27661  */
27662     getNodeData : function(node){
27663         if(node instanceof Array){
27664             var data = [];
27665             for(var i = 0, len = node.length; i < len; i++){
27666                 data.push(this.getNodeData(node[i]));
27667             }
27668             return data;
27669         }
27670         return this.jsonData[this.indexOf(node)] || null;
27671     },
27672
27673     beforeRender : function(){
27674         this.snapshot = this.jsonData;
27675         if(this.sortInfo){
27676             this.sort.apply(this, this.sortInfo);
27677         }
27678         this.fireEvent("beforerender", this, this.jsonData);
27679     },
27680
27681     onLoad : function(el, o){
27682         this.fireEvent("load", this, this.jsonData, o);
27683     },
27684
27685     onLoadException : function(el, o){
27686         this.fireEvent("loadexception", this, o);
27687     },
27688
27689 /**
27690  * Filter the data by a specific property.
27691  * @param {String} property A property on your JSON objects
27692  * @param {String/RegExp} value Either string that the property values
27693  * should start with, or a RegExp to test against the property
27694  */
27695     filter : function(property, value){
27696         if(this.jsonData){
27697             var data = [];
27698             var ss = this.snapshot;
27699             if(typeof value == "string"){
27700                 var vlen = value.length;
27701                 if(vlen == 0){
27702                     this.clearFilter();
27703                     return;
27704                 }
27705                 value = value.toLowerCase();
27706                 for(var i = 0, len = ss.length; i < len; i++){
27707                     var o = ss[i];
27708                     if(o[property].substr(0, vlen).toLowerCase() == value){
27709                         data.push(o);
27710                     }
27711                 }
27712             } else if(value.exec){ // regex?
27713                 for(var i = 0, len = ss.length; i < len; i++){
27714                     var o = ss[i];
27715                     if(value.test(o[property])){
27716                         data.push(o);
27717                     }
27718                 }
27719             } else{
27720                 return;
27721             }
27722             this.jsonData = data;
27723             this.refresh();
27724         }
27725     },
27726
27727 /**
27728  * Filter by a function. The passed function will be called with each
27729  * object in the current dataset. If the function returns true the value is kept,
27730  * otherwise it is filtered.
27731  * @param {Function} fn
27732  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
27733  */
27734     filterBy : function(fn, scope){
27735         if(this.jsonData){
27736             var data = [];
27737             var ss = this.snapshot;
27738             for(var i = 0, len = ss.length; i < len; i++){
27739                 var o = ss[i];
27740                 if(fn.call(scope || this, o)){
27741                     data.push(o);
27742                 }
27743             }
27744             this.jsonData = data;
27745             this.refresh();
27746         }
27747     },
27748
27749 /**
27750  * Clears the current filter.
27751  */
27752     clearFilter : function(){
27753         if(this.snapshot && this.jsonData != this.snapshot){
27754             this.jsonData = this.snapshot;
27755             this.refresh();
27756         }
27757     },
27758
27759
27760 /**
27761  * Sorts the data for this view and refreshes it.
27762  * @param {String} property A property on your JSON objects to sort on
27763  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
27764  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
27765  */
27766     sort : function(property, dir, sortType){
27767         this.sortInfo = Array.prototype.slice.call(arguments, 0);
27768         if(this.jsonData){
27769             var p = property;
27770             var dsc = dir && dir.toLowerCase() == "desc";
27771             var f = function(o1, o2){
27772                 var v1 = sortType ? sortType(o1[p]) : o1[p];
27773                 var v2 = sortType ? sortType(o2[p]) : o2[p];
27774                 ;
27775                 if(v1 < v2){
27776                     return dsc ? +1 : -1;
27777                 } else if(v1 > v2){
27778                     return dsc ? -1 : +1;
27779                 } else{
27780                     return 0;
27781                 }
27782             };
27783             this.jsonData.sort(f);
27784             this.refresh();
27785             if(this.jsonData != this.snapshot){
27786                 this.snapshot.sort(f);
27787             }
27788         }
27789     }
27790 });/*
27791  * Based on:
27792  * Ext JS Library 1.1.1
27793  * Copyright(c) 2006-2007, Ext JS, LLC.
27794  *
27795  * Originally Released Under LGPL - original licence link has changed is not relivant.
27796  *
27797  * Fork - LGPL
27798  * <script type="text/javascript">
27799  */
27800  
27801
27802 /**
27803  * @class Roo.ColorPalette
27804  * @extends Roo.Component
27805  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
27806  * Here's an example of typical usage:
27807  * <pre><code>
27808 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
27809 cp.render('my-div');
27810
27811 cp.on('select', function(palette, selColor){
27812     // do something with selColor
27813 });
27814 </code></pre>
27815  * @constructor
27816  * Create a new ColorPalette
27817  * @param {Object} config The config object
27818  */
27819 Roo.ColorPalette = function(config){
27820     Roo.ColorPalette.superclass.constructor.call(this, config);
27821     this.addEvents({
27822         /**
27823              * @event select
27824              * Fires when a color is selected
27825              * @param {ColorPalette} this
27826              * @param {String} color The 6-digit color hex code (without the # symbol)
27827              */
27828         select: true
27829     });
27830
27831     if(this.handler){
27832         this.on("select", this.handler, this.scope, true);
27833     }
27834 };
27835 Roo.extend(Roo.ColorPalette, Roo.Component, {
27836     /**
27837      * @cfg {String} itemCls
27838      * The CSS class to apply to the containing element (defaults to "x-color-palette")
27839      */
27840     itemCls : "x-color-palette",
27841     /**
27842      * @cfg {String} value
27843      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
27844      * the hex codes are case-sensitive.
27845      */
27846     value : null,
27847     clickEvent:'click',
27848     // private
27849     ctype: "Roo.ColorPalette",
27850
27851     /**
27852      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
27853      */
27854     allowReselect : false,
27855
27856     /**
27857      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
27858      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
27859      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
27860      * of colors with the width setting until the box is symmetrical.</p>
27861      * <p>You can override individual colors if needed:</p>
27862      * <pre><code>
27863 var cp = new Roo.ColorPalette();
27864 cp.colors[0] = "FF0000";  // change the first box to red
27865 </code></pre>
27866
27867 Or you can provide a custom array of your own for complete control:
27868 <pre><code>
27869 var cp = new Roo.ColorPalette();
27870 cp.colors = ["000000", "993300", "333300"];
27871 </code></pre>
27872      * @type Array
27873      */
27874     colors : [
27875         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
27876         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
27877         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
27878         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
27879         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
27880     ],
27881
27882     // private
27883     onRender : function(container, position){
27884         var t = new Roo.MasterTemplate(
27885             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
27886         );
27887         var c = this.colors;
27888         for(var i = 0, len = c.length; i < len; i++){
27889             t.add([c[i]]);
27890         }
27891         var el = document.createElement("div");
27892         el.className = this.itemCls;
27893         t.overwrite(el);
27894         container.dom.insertBefore(el, position);
27895         this.el = Roo.get(el);
27896         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
27897         if(this.clickEvent != 'click'){
27898             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
27899         }
27900     },
27901
27902     // private
27903     afterRender : function(){
27904         Roo.ColorPalette.superclass.afterRender.call(this);
27905         if(this.value){
27906             var s = this.value;
27907             this.value = null;
27908             this.select(s);
27909         }
27910     },
27911
27912     // private
27913     handleClick : function(e, t){
27914         e.preventDefault();
27915         if(!this.disabled){
27916             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
27917             this.select(c.toUpperCase());
27918         }
27919     },
27920
27921     /**
27922      * Selects the specified color in the palette (fires the select event)
27923      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
27924      */
27925     select : function(color){
27926         color = color.replace("#", "");
27927         if(color != this.value || this.allowReselect){
27928             var el = this.el;
27929             if(this.value){
27930                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
27931             }
27932             el.child("a.color-"+color).addClass("x-color-palette-sel");
27933             this.value = color;
27934             this.fireEvent("select", this, color);
27935         }
27936     }
27937 });/*
27938  * Based on:
27939  * Ext JS Library 1.1.1
27940  * Copyright(c) 2006-2007, Ext JS, LLC.
27941  *
27942  * Originally Released Under LGPL - original licence link has changed is not relivant.
27943  *
27944  * Fork - LGPL
27945  * <script type="text/javascript">
27946  */
27947  
27948 /**
27949  * @class Roo.DatePicker
27950  * @extends Roo.Component
27951  * Simple date picker class.
27952  * @constructor
27953  * Create a new DatePicker
27954  * @param {Object} config The config object
27955  */
27956 Roo.DatePicker = function(config){
27957     Roo.DatePicker.superclass.constructor.call(this, config);
27958
27959     this.value = config && config.value ?
27960                  config.value.clearTime() : new Date().clearTime();
27961
27962     this.addEvents({
27963         /**
27964              * @event select
27965              * Fires when a date is selected
27966              * @param {DatePicker} this
27967              * @param {Date} date The selected date
27968              */
27969         'select': true,
27970         /**
27971              * @event monthchange
27972              * Fires when the displayed month changes 
27973              * @param {DatePicker} this
27974              * @param {Date} date The selected month
27975              */
27976         'monthchange': true
27977     });
27978
27979     if(this.handler){
27980         this.on("select", this.handler,  this.scope || this);
27981     }
27982     // build the disabledDatesRE
27983     if(!this.disabledDatesRE && this.disabledDates){
27984         var dd = this.disabledDates;
27985         var re = "(?:";
27986         for(var i = 0; i < dd.length; i++){
27987             re += dd[i];
27988             if(i != dd.length-1) {
27989                 re += "|";
27990             }
27991         }
27992         this.disabledDatesRE = new RegExp(re + ")");
27993     }
27994 };
27995
27996 Roo.extend(Roo.DatePicker, Roo.Component, {
27997     /**
27998      * @cfg {String} todayText
27999      * The text to display on the button that selects the current date (defaults to "Today")
28000      */
28001     todayText : "Today",
28002     /**
28003      * @cfg {String} okText
28004      * The text to display on the ok button
28005      */
28006     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28007     /**
28008      * @cfg {String} cancelText
28009      * The text to display on the cancel button
28010      */
28011     cancelText : "Cancel",
28012     /**
28013      * @cfg {String} todayTip
28014      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28015      */
28016     todayTip : "{0} (Spacebar)",
28017     /**
28018      * @cfg {Date} minDate
28019      * Minimum allowable date (JavaScript date object, defaults to null)
28020      */
28021     minDate : null,
28022     /**
28023      * @cfg {Date} maxDate
28024      * Maximum allowable date (JavaScript date object, defaults to null)
28025      */
28026     maxDate : null,
28027     /**
28028      * @cfg {String} minText
28029      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28030      */
28031     minText : "This date is before the minimum date",
28032     /**
28033      * @cfg {String} maxText
28034      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28035      */
28036     maxText : "This date is after the maximum date",
28037     /**
28038      * @cfg {String} format
28039      * The default date format string which can be overriden for localization support.  The format must be
28040      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28041      */
28042     format : "m/d/y",
28043     /**
28044      * @cfg {Array} disabledDays
28045      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28046      */
28047     disabledDays : null,
28048     /**
28049      * @cfg {String} disabledDaysText
28050      * The tooltip to display when the date falls on a disabled day (defaults to "")
28051      */
28052     disabledDaysText : "",
28053     /**
28054      * @cfg {RegExp} disabledDatesRE
28055      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28056      */
28057     disabledDatesRE : null,
28058     /**
28059      * @cfg {String} disabledDatesText
28060      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28061      */
28062     disabledDatesText : "",
28063     /**
28064      * @cfg {Boolean} constrainToViewport
28065      * True to constrain the date picker to the viewport (defaults to true)
28066      */
28067     constrainToViewport : true,
28068     /**
28069      * @cfg {Array} monthNames
28070      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28071      */
28072     monthNames : Date.monthNames,
28073     /**
28074      * @cfg {Array} dayNames
28075      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28076      */
28077     dayNames : Date.dayNames,
28078     /**
28079      * @cfg {String} nextText
28080      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28081      */
28082     nextText: 'Next Month (Control+Right)',
28083     /**
28084      * @cfg {String} prevText
28085      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28086      */
28087     prevText: 'Previous Month (Control+Left)',
28088     /**
28089      * @cfg {String} monthYearText
28090      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28091      */
28092     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28093     /**
28094      * @cfg {Number} startDay
28095      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28096      */
28097     startDay : 0,
28098     /**
28099      * @cfg {Bool} showClear
28100      * Show a clear button (usefull for date form elements that can be blank.)
28101      */
28102     
28103     showClear: false,
28104     
28105     /**
28106      * Sets the value of the date field
28107      * @param {Date} value The date to set
28108      */
28109     setValue : function(value){
28110         var old = this.value;
28111         
28112         if (typeof(value) == 'string') {
28113          
28114             value = Date.parseDate(value, this.format);
28115         }
28116         if (!value) {
28117             value = new Date();
28118         }
28119         
28120         this.value = value.clearTime(true);
28121         if(this.el){
28122             this.update(this.value);
28123         }
28124     },
28125
28126     /**
28127      * Gets the current selected value of the date field
28128      * @return {Date} The selected date
28129      */
28130     getValue : function(){
28131         return this.value;
28132     },
28133
28134     // private
28135     focus : function(){
28136         if(this.el){
28137             this.update(this.activeDate);
28138         }
28139     },
28140
28141     // privateval
28142     onRender : function(container, position){
28143         
28144         var m = [
28145              '<table cellspacing="0">',
28146                 '<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>',
28147                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28148         var dn = this.dayNames;
28149         for(var i = 0; i < 7; i++){
28150             var d = this.startDay+i;
28151             if(d > 6){
28152                 d = d-7;
28153             }
28154             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28155         }
28156         m[m.length] = "</tr></thead><tbody><tr>";
28157         for(var i = 0; i < 42; i++) {
28158             if(i % 7 == 0 && i != 0){
28159                 m[m.length] = "</tr><tr>";
28160             }
28161             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28162         }
28163         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28164             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28165
28166         var el = document.createElement("div");
28167         el.className = "x-date-picker";
28168         el.innerHTML = m.join("");
28169
28170         container.dom.insertBefore(el, position);
28171
28172         this.el = Roo.get(el);
28173         this.eventEl = Roo.get(el.firstChild);
28174
28175         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28176             handler: this.showPrevMonth,
28177             scope: this,
28178             preventDefault:true,
28179             stopDefault:true
28180         });
28181
28182         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28183             handler: this.showNextMonth,
28184             scope: this,
28185             preventDefault:true,
28186             stopDefault:true
28187         });
28188
28189         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28190
28191         this.monthPicker = this.el.down('div.x-date-mp');
28192         this.monthPicker.enableDisplayMode('block');
28193         
28194         var kn = new Roo.KeyNav(this.eventEl, {
28195             "left" : function(e){
28196                 e.ctrlKey ?
28197                     this.showPrevMonth() :
28198                     this.update(this.activeDate.add("d", -1));
28199             },
28200
28201             "right" : function(e){
28202                 e.ctrlKey ?
28203                     this.showNextMonth() :
28204                     this.update(this.activeDate.add("d", 1));
28205             },
28206
28207             "up" : function(e){
28208                 e.ctrlKey ?
28209                     this.showNextYear() :
28210                     this.update(this.activeDate.add("d", -7));
28211             },
28212
28213             "down" : function(e){
28214                 e.ctrlKey ?
28215                     this.showPrevYear() :
28216                     this.update(this.activeDate.add("d", 7));
28217             },
28218
28219             "pageUp" : function(e){
28220                 this.showNextMonth();
28221             },
28222
28223             "pageDown" : function(e){
28224                 this.showPrevMonth();
28225             },
28226
28227             "enter" : function(e){
28228                 e.stopPropagation();
28229                 return true;
28230             },
28231
28232             scope : this
28233         });
28234
28235         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28236
28237         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28238
28239         this.el.unselectable();
28240         
28241         this.cells = this.el.select("table.x-date-inner tbody td");
28242         this.textNodes = this.el.query("table.x-date-inner tbody span");
28243
28244         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28245             text: "&#160;",
28246             tooltip: this.monthYearText
28247         });
28248
28249         this.mbtn.on('click', this.showMonthPicker, this);
28250         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28251
28252
28253         var today = (new Date()).dateFormat(this.format);
28254         
28255         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
28256         if (this.showClear) {
28257             baseTb.add( new Roo.Toolbar.Fill());
28258         }
28259         baseTb.add({
28260             text: String.format(this.todayText, today),
28261             tooltip: String.format(this.todayTip, today),
28262             handler: this.selectToday,
28263             scope: this
28264         });
28265         
28266         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
28267             
28268         //});
28269         if (this.showClear) {
28270             
28271             baseTb.add( new Roo.Toolbar.Fill());
28272             baseTb.add({
28273                 text: '&#160;',
28274                 cls: 'x-btn-icon x-btn-clear',
28275                 handler: function() {
28276                     //this.value = '';
28277                     this.fireEvent("select", this, '');
28278                 },
28279                 scope: this
28280             });
28281         }
28282         
28283         
28284         if(Roo.isIE){
28285             this.el.repaint();
28286         }
28287         this.update(this.value);
28288     },
28289
28290     createMonthPicker : function(){
28291         if(!this.monthPicker.dom.firstChild){
28292             var buf = ['<table border="0" cellspacing="0">'];
28293             for(var i = 0; i < 6; i++){
28294                 buf.push(
28295                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
28296                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
28297                     i == 0 ?
28298                     '<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>' :
28299                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
28300                 );
28301             }
28302             buf.push(
28303                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
28304                     this.okText,
28305                     '</button><button type="button" class="x-date-mp-cancel">',
28306                     this.cancelText,
28307                     '</button></td></tr>',
28308                 '</table>'
28309             );
28310             this.monthPicker.update(buf.join(''));
28311             this.monthPicker.on('click', this.onMonthClick, this);
28312             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
28313
28314             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
28315             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
28316
28317             this.mpMonths.each(function(m, a, i){
28318                 i += 1;
28319                 if((i%2) == 0){
28320                     m.dom.xmonth = 5 + Math.round(i * .5);
28321                 }else{
28322                     m.dom.xmonth = Math.round((i-1) * .5);
28323                 }
28324             });
28325         }
28326     },
28327
28328     showMonthPicker : function(){
28329         this.createMonthPicker();
28330         var size = this.el.getSize();
28331         this.monthPicker.setSize(size);
28332         this.monthPicker.child('table').setSize(size);
28333
28334         this.mpSelMonth = (this.activeDate || this.value).getMonth();
28335         this.updateMPMonth(this.mpSelMonth);
28336         this.mpSelYear = (this.activeDate || this.value).getFullYear();
28337         this.updateMPYear(this.mpSelYear);
28338
28339         this.monthPicker.slideIn('t', {duration:.2});
28340     },
28341
28342     updateMPYear : function(y){
28343         this.mpyear = y;
28344         var ys = this.mpYears.elements;
28345         for(var i = 1; i <= 10; i++){
28346             var td = ys[i-1], y2;
28347             if((i%2) == 0){
28348                 y2 = y + Math.round(i * .5);
28349                 td.firstChild.innerHTML = y2;
28350                 td.xyear = y2;
28351             }else{
28352                 y2 = y - (5-Math.round(i * .5));
28353                 td.firstChild.innerHTML = y2;
28354                 td.xyear = y2;
28355             }
28356             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
28357         }
28358     },
28359
28360     updateMPMonth : function(sm){
28361         this.mpMonths.each(function(m, a, i){
28362             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
28363         });
28364     },
28365
28366     selectMPMonth: function(m){
28367         
28368     },
28369
28370     onMonthClick : function(e, t){
28371         e.stopEvent();
28372         var el = new Roo.Element(t), pn;
28373         if(el.is('button.x-date-mp-cancel')){
28374             this.hideMonthPicker();
28375         }
28376         else if(el.is('button.x-date-mp-ok')){
28377             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28378             this.hideMonthPicker();
28379         }
28380         else if(pn = el.up('td.x-date-mp-month', 2)){
28381             this.mpMonths.removeClass('x-date-mp-sel');
28382             pn.addClass('x-date-mp-sel');
28383             this.mpSelMonth = pn.dom.xmonth;
28384         }
28385         else if(pn = el.up('td.x-date-mp-year', 2)){
28386             this.mpYears.removeClass('x-date-mp-sel');
28387             pn.addClass('x-date-mp-sel');
28388             this.mpSelYear = pn.dom.xyear;
28389         }
28390         else if(el.is('a.x-date-mp-prev')){
28391             this.updateMPYear(this.mpyear-10);
28392         }
28393         else if(el.is('a.x-date-mp-next')){
28394             this.updateMPYear(this.mpyear+10);
28395         }
28396     },
28397
28398     onMonthDblClick : function(e, t){
28399         e.stopEvent();
28400         var el = new Roo.Element(t), pn;
28401         if(pn = el.up('td.x-date-mp-month', 2)){
28402             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
28403             this.hideMonthPicker();
28404         }
28405         else if(pn = el.up('td.x-date-mp-year', 2)){
28406             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
28407             this.hideMonthPicker();
28408         }
28409     },
28410
28411     hideMonthPicker : function(disableAnim){
28412         if(this.monthPicker){
28413             if(disableAnim === true){
28414                 this.monthPicker.hide();
28415             }else{
28416                 this.monthPicker.slideOut('t', {duration:.2});
28417             }
28418         }
28419     },
28420
28421     // private
28422     showPrevMonth : function(e){
28423         this.update(this.activeDate.add("mo", -1));
28424     },
28425
28426     // private
28427     showNextMonth : function(e){
28428         this.update(this.activeDate.add("mo", 1));
28429     },
28430
28431     // private
28432     showPrevYear : function(){
28433         this.update(this.activeDate.add("y", -1));
28434     },
28435
28436     // private
28437     showNextYear : function(){
28438         this.update(this.activeDate.add("y", 1));
28439     },
28440
28441     // private
28442     handleMouseWheel : function(e){
28443         var delta = e.getWheelDelta();
28444         if(delta > 0){
28445             this.showPrevMonth();
28446             e.stopEvent();
28447         } else if(delta < 0){
28448             this.showNextMonth();
28449             e.stopEvent();
28450         }
28451     },
28452
28453     // private
28454     handleDateClick : function(e, t){
28455         e.stopEvent();
28456         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
28457             this.setValue(new Date(t.dateValue));
28458             this.fireEvent("select", this, this.value);
28459         }
28460     },
28461
28462     // private
28463     selectToday : function(){
28464         this.setValue(new Date().clearTime());
28465         this.fireEvent("select", this, this.value);
28466     },
28467
28468     // private
28469     update : function(date)
28470     {
28471         var vd = this.activeDate;
28472         this.activeDate = date;
28473         if(vd && this.el){
28474             var t = date.getTime();
28475             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
28476                 this.cells.removeClass("x-date-selected");
28477                 this.cells.each(function(c){
28478                    if(c.dom.firstChild.dateValue == t){
28479                        c.addClass("x-date-selected");
28480                        setTimeout(function(){
28481                             try{c.dom.firstChild.focus();}catch(e){}
28482                        }, 50);
28483                        return false;
28484                    }
28485                 });
28486                 return;
28487             }
28488         }
28489         
28490         var days = date.getDaysInMonth();
28491         var firstOfMonth = date.getFirstDateOfMonth();
28492         var startingPos = firstOfMonth.getDay()-this.startDay;
28493
28494         if(startingPos <= this.startDay){
28495             startingPos += 7;
28496         }
28497
28498         var pm = date.add("mo", -1);
28499         var prevStart = pm.getDaysInMonth()-startingPos;
28500
28501         var cells = this.cells.elements;
28502         var textEls = this.textNodes;
28503         days += startingPos;
28504
28505         // convert everything to numbers so it's fast
28506         var day = 86400000;
28507         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
28508         var today = new Date().clearTime().getTime();
28509         var sel = date.clearTime().getTime();
28510         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
28511         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
28512         var ddMatch = this.disabledDatesRE;
28513         var ddText = this.disabledDatesText;
28514         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
28515         var ddaysText = this.disabledDaysText;
28516         var format = this.format;
28517
28518         var setCellClass = function(cal, cell){
28519             cell.title = "";
28520             var t = d.getTime();
28521             cell.firstChild.dateValue = t;
28522             if(t == today){
28523                 cell.className += " x-date-today";
28524                 cell.title = cal.todayText;
28525             }
28526             if(t == sel){
28527                 cell.className += " x-date-selected";
28528                 setTimeout(function(){
28529                     try{cell.firstChild.focus();}catch(e){}
28530                 }, 50);
28531             }
28532             // disabling
28533             if(t < min) {
28534                 cell.className = " x-date-disabled";
28535                 cell.title = cal.minText;
28536                 return;
28537             }
28538             if(t > max) {
28539                 cell.className = " x-date-disabled";
28540                 cell.title = cal.maxText;
28541                 return;
28542             }
28543             if(ddays){
28544                 if(ddays.indexOf(d.getDay()) != -1){
28545                     cell.title = ddaysText;
28546                     cell.className = " x-date-disabled";
28547                 }
28548             }
28549             if(ddMatch && format){
28550                 var fvalue = d.dateFormat(format);
28551                 if(ddMatch.test(fvalue)){
28552                     cell.title = ddText.replace("%0", fvalue);
28553                     cell.className = " x-date-disabled";
28554                 }
28555             }
28556         };
28557
28558         var i = 0;
28559         for(; i < startingPos; i++) {
28560             textEls[i].innerHTML = (++prevStart);
28561             d.setDate(d.getDate()+1);
28562             cells[i].className = "x-date-prevday";
28563             setCellClass(this, cells[i]);
28564         }
28565         for(; i < days; i++){
28566             intDay = i - startingPos + 1;
28567             textEls[i].innerHTML = (intDay);
28568             d.setDate(d.getDate()+1);
28569             cells[i].className = "x-date-active";
28570             setCellClass(this, cells[i]);
28571         }
28572         var extraDays = 0;
28573         for(; i < 42; i++) {
28574              textEls[i].innerHTML = (++extraDays);
28575              d.setDate(d.getDate()+1);
28576              cells[i].className = "x-date-nextday";
28577              setCellClass(this, cells[i]);
28578         }
28579
28580         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
28581         this.fireEvent('monthchange', this, date);
28582         
28583         if(!this.internalRender){
28584             var main = this.el.dom.firstChild;
28585             var w = main.offsetWidth;
28586             this.el.setWidth(w + this.el.getBorderWidth("lr"));
28587             Roo.fly(main).setWidth(w);
28588             this.internalRender = true;
28589             // opera does not respect the auto grow header center column
28590             // then, after it gets a width opera refuses to recalculate
28591             // without a second pass
28592             if(Roo.isOpera && !this.secondPass){
28593                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
28594                 this.secondPass = true;
28595                 this.update.defer(10, this, [date]);
28596             }
28597         }
28598         
28599         
28600     }
28601 });        /*
28602  * Based on:
28603  * Ext JS Library 1.1.1
28604  * Copyright(c) 2006-2007, Ext JS, LLC.
28605  *
28606  * Originally Released Under LGPL - original licence link has changed is not relivant.
28607  *
28608  * Fork - LGPL
28609  * <script type="text/javascript">
28610  */
28611 /**
28612  * @class Roo.TabPanel
28613  * @extends Roo.util.Observable
28614  * A lightweight tab container.
28615  * <br><br>
28616  * Usage:
28617  * <pre><code>
28618 // basic tabs 1, built from existing content
28619 var tabs = new Roo.TabPanel("tabs1");
28620 tabs.addTab("script", "View Script");
28621 tabs.addTab("markup", "View Markup");
28622 tabs.activate("script");
28623
28624 // more advanced tabs, built from javascript
28625 var jtabs = new Roo.TabPanel("jtabs");
28626 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
28627
28628 // set up the UpdateManager
28629 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
28630 var updater = tab2.getUpdateManager();
28631 updater.setDefaultUrl("ajax1.htm");
28632 tab2.on('activate', updater.refresh, updater, true);
28633
28634 // Use setUrl for Ajax loading
28635 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
28636 tab3.setUrl("ajax2.htm", null, true);
28637
28638 // Disabled tab
28639 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
28640 tab4.disable();
28641
28642 jtabs.activate("jtabs-1");
28643  * </code></pre>
28644  * @constructor
28645  * Create a new TabPanel.
28646  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
28647  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
28648  */
28649 Roo.TabPanel = function(container, config){
28650     /**
28651     * The container element for this TabPanel.
28652     * @type Roo.Element
28653     */
28654     this.el = Roo.get(container, true);
28655     if(config){
28656         if(typeof config == "boolean"){
28657             this.tabPosition = config ? "bottom" : "top";
28658         }else{
28659             Roo.apply(this, config);
28660         }
28661     }
28662     if(this.tabPosition == "bottom"){
28663         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28664         this.el.addClass("x-tabs-bottom");
28665     }
28666     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
28667     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
28668     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
28669     if(Roo.isIE){
28670         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
28671     }
28672     if(this.tabPosition != "bottom"){
28673         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
28674          * @type Roo.Element
28675          */
28676         this.bodyEl = Roo.get(this.createBody(this.el.dom));
28677         this.el.addClass("x-tabs-top");
28678     }
28679     this.items = [];
28680
28681     this.bodyEl.setStyle("position", "relative");
28682
28683     this.active = null;
28684     this.activateDelegate = this.activate.createDelegate(this);
28685
28686     this.addEvents({
28687         /**
28688          * @event tabchange
28689          * Fires when the active tab changes
28690          * @param {Roo.TabPanel} this
28691          * @param {Roo.TabPanelItem} activePanel The new active tab
28692          */
28693         "tabchange": true,
28694         /**
28695          * @event beforetabchange
28696          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
28697          * @param {Roo.TabPanel} this
28698          * @param {Object} e Set cancel to true on this object to cancel the tab change
28699          * @param {Roo.TabPanelItem} tab The tab being changed to
28700          */
28701         "beforetabchange" : true
28702     });
28703
28704     Roo.EventManager.onWindowResize(this.onResize, this);
28705     this.cpad = this.el.getPadding("lr");
28706     this.hiddenCount = 0;
28707
28708
28709     // toolbar on the tabbar support...
28710     if (this.toolbar) {
28711         var tcfg = this.toolbar;
28712         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
28713         this.toolbar = new Roo.Toolbar(tcfg);
28714         if (Roo.isSafari) {
28715             var tbl = tcfg.container.child('table', true);
28716             tbl.setAttribute('width', '100%');
28717         }
28718         
28719     }
28720    
28721
28722
28723     Roo.TabPanel.superclass.constructor.call(this);
28724 };
28725
28726 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
28727     /*
28728      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
28729      */
28730     tabPosition : "top",
28731     /*
28732      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
28733      */
28734     currentTabWidth : 0,
28735     /*
28736      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
28737      */
28738     minTabWidth : 40,
28739     /*
28740      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
28741      */
28742     maxTabWidth : 250,
28743     /*
28744      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
28745      */
28746     preferredTabWidth : 175,
28747     /*
28748      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
28749      */
28750     resizeTabs : false,
28751     /*
28752      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
28753      */
28754     monitorResize : true,
28755     /*
28756      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
28757      */
28758     toolbar : false,
28759
28760     /**
28761      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
28762      * @param {String} id The id of the div to use <b>or create</b>
28763      * @param {String} text The text for the tab
28764      * @param {String} content (optional) Content to put in the TabPanelItem body
28765      * @param {Boolean} closable (optional) True to create a close icon on the tab
28766      * @return {Roo.TabPanelItem} The created TabPanelItem
28767      */
28768     addTab : function(id, text, content, closable){
28769         var item = new Roo.TabPanelItem(this, id, text, closable);
28770         this.addTabItem(item);
28771         if(content){
28772             item.setContent(content);
28773         }
28774         return item;
28775     },
28776
28777     /**
28778      * Returns the {@link Roo.TabPanelItem} with the specified id/index
28779      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
28780      * @return {Roo.TabPanelItem}
28781      */
28782     getTab : function(id){
28783         return this.items[id];
28784     },
28785
28786     /**
28787      * Hides the {@link Roo.TabPanelItem} with the specified id/index
28788      * @param {String/Number} id The id or index of the TabPanelItem to hide.
28789      */
28790     hideTab : function(id){
28791         var t = this.items[id];
28792         if(!t.isHidden()){
28793            t.setHidden(true);
28794            this.hiddenCount++;
28795            this.autoSizeTabs();
28796         }
28797     },
28798
28799     /**
28800      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
28801      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
28802      */
28803     unhideTab : function(id){
28804         var t = this.items[id];
28805         if(t.isHidden()){
28806            t.setHidden(false);
28807            this.hiddenCount--;
28808            this.autoSizeTabs();
28809         }
28810     },
28811
28812     /**
28813      * Adds an existing {@link Roo.TabPanelItem}.
28814      * @param {Roo.TabPanelItem} item The TabPanelItem to add
28815      */
28816     addTabItem : function(item){
28817         this.items[item.id] = item;
28818         this.items.push(item);
28819         if(this.resizeTabs){
28820            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
28821            this.autoSizeTabs();
28822         }else{
28823             item.autoSize();
28824         }
28825     },
28826
28827     /**
28828      * Removes a {@link Roo.TabPanelItem}.
28829      * @param {String/Number} id The id or index of the TabPanelItem to remove.
28830      */
28831     removeTab : function(id){
28832         var items = this.items;
28833         var tab = items[id];
28834         if(!tab) { return; }
28835         var index = items.indexOf(tab);
28836         if(this.active == tab && items.length > 1){
28837             var newTab = this.getNextAvailable(index);
28838             if(newTab) {
28839                 newTab.activate();
28840             }
28841         }
28842         this.stripEl.dom.removeChild(tab.pnode.dom);
28843         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
28844             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
28845         }
28846         items.splice(index, 1);
28847         delete this.items[tab.id];
28848         tab.fireEvent("close", tab);
28849         tab.purgeListeners();
28850         this.autoSizeTabs();
28851     },
28852
28853     getNextAvailable : function(start){
28854         var items = this.items;
28855         var index = start;
28856         // look for a next tab that will slide over to
28857         // replace the one being removed
28858         while(index < items.length){
28859             var item = items[++index];
28860             if(item && !item.isHidden()){
28861                 return item;
28862             }
28863         }
28864         // if one isn't found select the previous tab (on the left)
28865         index = start;
28866         while(index >= 0){
28867             var item = items[--index];
28868             if(item && !item.isHidden()){
28869                 return item;
28870             }
28871         }
28872         return null;
28873     },
28874
28875     /**
28876      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
28877      * @param {String/Number} id The id or index of the TabPanelItem to disable.
28878      */
28879     disableTab : function(id){
28880         var tab = this.items[id];
28881         if(tab && this.active != tab){
28882             tab.disable();
28883         }
28884     },
28885
28886     /**
28887      * Enables a {@link Roo.TabPanelItem} that is disabled.
28888      * @param {String/Number} id The id or index of the TabPanelItem to enable.
28889      */
28890     enableTab : function(id){
28891         var tab = this.items[id];
28892         tab.enable();
28893     },
28894
28895     /**
28896      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
28897      * @param {String/Number} id The id or index of the TabPanelItem to activate.
28898      * @return {Roo.TabPanelItem} The TabPanelItem.
28899      */
28900     activate : function(id){
28901         var tab = this.items[id];
28902         if(!tab){
28903             return null;
28904         }
28905         if(tab == this.active || tab.disabled){
28906             return tab;
28907         }
28908         var e = {};
28909         this.fireEvent("beforetabchange", this, e, tab);
28910         if(e.cancel !== true && !tab.disabled){
28911             if(this.active){
28912                 this.active.hide();
28913             }
28914             this.active = this.items[id];
28915             this.active.show();
28916             this.fireEvent("tabchange", this, this.active);
28917         }
28918         return tab;
28919     },
28920
28921     /**
28922      * Gets the active {@link Roo.TabPanelItem}.
28923      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
28924      */
28925     getActiveTab : function(){
28926         return this.active;
28927     },
28928
28929     /**
28930      * Updates the tab body element to fit the height of the container element
28931      * for overflow scrolling
28932      * @param {Number} targetHeight (optional) Override the starting height from the elements height
28933      */
28934     syncHeight : function(targetHeight){
28935         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
28936         var bm = this.bodyEl.getMargins();
28937         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
28938         this.bodyEl.setHeight(newHeight);
28939         return newHeight;
28940     },
28941
28942     onResize : function(){
28943         if(this.monitorResize){
28944             this.autoSizeTabs();
28945         }
28946     },
28947
28948     /**
28949      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
28950      */
28951     beginUpdate : function(){
28952         this.updating = true;
28953     },
28954
28955     /**
28956      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
28957      */
28958     endUpdate : function(){
28959         this.updating = false;
28960         this.autoSizeTabs();
28961     },
28962
28963     /**
28964      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
28965      */
28966     autoSizeTabs : function(){
28967         var count = this.items.length;
28968         var vcount = count - this.hiddenCount;
28969         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
28970             return;
28971         }
28972         var w = Math.max(this.el.getWidth() - this.cpad, 10);
28973         var availWidth = Math.floor(w / vcount);
28974         var b = this.stripBody;
28975         if(b.getWidth() > w){
28976             var tabs = this.items;
28977             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
28978             if(availWidth < this.minTabWidth){
28979                 /*if(!this.sleft){    // incomplete scrolling code
28980                     this.createScrollButtons();
28981                 }
28982                 this.showScroll();
28983                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
28984             }
28985         }else{
28986             if(this.currentTabWidth < this.preferredTabWidth){
28987                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
28988             }
28989         }
28990     },
28991
28992     /**
28993      * Returns the number of tabs in this TabPanel.
28994      * @return {Number}
28995      */
28996      getCount : function(){
28997          return this.items.length;
28998      },
28999
29000     /**
29001      * Resizes all the tabs to the passed width
29002      * @param {Number} The new width
29003      */
29004     setTabWidth : function(width){
29005         this.currentTabWidth = width;
29006         for(var i = 0, len = this.items.length; i < len; i++) {
29007                 if(!this.items[i].isHidden()) {
29008                 this.items[i].setWidth(width);
29009             }
29010         }
29011     },
29012
29013     /**
29014      * Destroys this TabPanel
29015      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29016      */
29017     destroy : function(removeEl){
29018         Roo.EventManager.removeResizeListener(this.onResize, this);
29019         for(var i = 0, len = this.items.length; i < len; i++){
29020             this.items[i].purgeListeners();
29021         }
29022         if(removeEl === true){
29023             this.el.update("");
29024             this.el.remove();
29025         }
29026     }
29027 });
29028
29029 /**
29030  * @class Roo.TabPanelItem
29031  * @extends Roo.util.Observable
29032  * Represents an individual item (tab plus body) in a TabPanel.
29033  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29034  * @param {String} id The id of this TabPanelItem
29035  * @param {String} text The text for the tab of this TabPanelItem
29036  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29037  */
29038 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29039     /**
29040      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29041      * @type Roo.TabPanel
29042      */
29043     this.tabPanel = tabPanel;
29044     /**
29045      * The id for this TabPanelItem
29046      * @type String
29047      */
29048     this.id = id;
29049     /** @private */
29050     this.disabled = false;
29051     /** @private */
29052     this.text = text;
29053     /** @private */
29054     this.loaded = false;
29055     this.closable = closable;
29056
29057     /**
29058      * The body element for this TabPanelItem.
29059      * @type Roo.Element
29060      */
29061     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29062     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29063     this.bodyEl.setStyle("display", "block");
29064     this.bodyEl.setStyle("zoom", "1");
29065     this.hideAction();
29066
29067     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29068     /** @private */
29069     this.el = Roo.get(els.el, true);
29070     this.inner = Roo.get(els.inner, true);
29071     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29072     this.pnode = Roo.get(els.el.parentNode, true);
29073     this.el.on("mousedown", this.onTabMouseDown, this);
29074     this.el.on("click", this.onTabClick, this);
29075     /** @private */
29076     if(closable){
29077         var c = Roo.get(els.close, true);
29078         c.dom.title = this.closeText;
29079         c.addClassOnOver("close-over");
29080         c.on("click", this.closeClick, this);
29081      }
29082
29083     this.addEvents({
29084          /**
29085          * @event activate
29086          * Fires when this tab becomes the active tab.
29087          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29088          * @param {Roo.TabPanelItem} this
29089          */
29090         "activate": true,
29091         /**
29092          * @event beforeclose
29093          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29094          * @param {Roo.TabPanelItem} this
29095          * @param {Object} e Set cancel to true on this object to cancel the close.
29096          */
29097         "beforeclose": true,
29098         /**
29099          * @event close
29100          * Fires when this tab is closed.
29101          * @param {Roo.TabPanelItem} this
29102          */
29103          "close": true,
29104         /**
29105          * @event deactivate
29106          * Fires when this tab is no longer the active tab.
29107          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29108          * @param {Roo.TabPanelItem} this
29109          */
29110          "deactivate" : true
29111     });
29112     this.hidden = false;
29113
29114     Roo.TabPanelItem.superclass.constructor.call(this);
29115 };
29116
29117 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29118     purgeListeners : function(){
29119        Roo.util.Observable.prototype.purgeListeners.call(this);
29120        this.el.removeAllListeners();
29121     },
29122     /**
29123      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29124      */
29125     show : function(){
29126         this.pnode.addClass("on");
29127         this.showAction();
29128         if(Roo.isOpera){
29129             this.tabPanel.stripWrap.repaint();
29130         }
29131         this.fireEvent("activate", this.tabPanel, this);
29132     },
29133
29134     /**
29135      * Returns true if this tab is the active tab.
29136      * @return {Boolean}
29137      */
29138     isActive : function(){
29139         return this.tabPanel.getActiveTab() == this;
29140     },
29141
29142     /**
29143      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29144      */
29145     hide : function(){
29146         this.pnode.removeClass("on");
29147         this.hideAction();
29148         this.fireEvent("deactivate", this.tabPanel, this);
29149     },
29150
29151     hideAction : function(){
29152         this.bodyEl.hide();
29153         this.bodyEl.setStyle("position", "absolute");
29154         this.bodyEl.setLeft("-20000px");
29155         this.bodyEl.setTop("-20000px");
29156     },
29157
29158     showAction : function(){
29159         this.bodyEl.setStyle("position", "relative");
29160         this.bodyEl.setTop("");
29161         this.bodyEl.setLeft("");
29162         this.bodyEl.show();
29163     },
29164
29165     /**
29166      * Set the tooltip for the tab.
29167      * @param {String} tooltip The tab's tooltip
29168      */
29169     setTooltip : function(text){
29170         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29171             this.textEl.dom.qtip = text;
29172             this.textEl.dom.removeAttribute('title');
29173         }else{
29174             this.textEl.dom.title = text;
29175         }
29176     },
29177
29178     onTabClick : function(e){
29179         e.preventDefault();
29180         this.tabPanel.activate(this.id);
29181     },
29182
29183     onTabMouseDown : function(e){
29184         e.preventDefault();
29185         this.tabPanel.activate(this.id);
29186     },
29187
29188     getWidth : function(){
29189         return this.inner.getWidth();
29190     },
29191
29192     setWidth : function(width){
29193         var iwidth = width - this.pnode.getPadding("lr");
29194         this.inner.setWidth(iwidth);
29195         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29196         this.pnode.setWidth(width);
29197     },
29198
29199     /**
29200      * Show or hide the tab
29201      * @param {Boolean} hidden True to hide or false to show.
29202      */
29203     setHidden : function(hidden){
29204         this.hidden = hidden;
29205         this.pnode.setStyle("display", hidden ? "none" : "");
29206     },
29207
29208     /**
29209      * Returns true if this tab is "hidden"
29210      * @return {Boolean}
29211      */
29212     isHidden : function(){
29213         return this.hidden;
29214     },
29215
29216     /**
29217      * Returns the text for this tab
29218      * @return {String}
29219      */
29220     getText : function(){
29221         return this.text;
29222     },
29223
29224     autoSize : function(){
29225         //this.el.beginMeasure();
29226         this.textEl.setWidth(1);
29227         /*
29228          *  #2804 [new] Tabs in Roojs
29229          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29230          */
29231         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29232         //this.el.endMeasure();
29233     },
29234
29235     /**
29236      * Sets the text for the tab (Note: this also sets the tooltip text)
29237      * @param {String} text The tab's text and tooltip
29238      */
29239     setText : function(text){
29240         this.text = text;
29241         this.textEl.update(text);
29242         this.setTooltip(text);
29243         if(!this.tabPanel.resizeTabs){
29244             this.autoSize();
29245         }
29246     },
29247     /**
29248      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29249      */
29250     activate : function(){
29251         this.tabPanel.activate(this.id);
29252     },
29253
29254     /**
29255      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
29256      */
29257     disable : function(){
29258         if(this.tabPanel.active != this){
29259             this.disabled = true;
29260             this.pnode.addClass("disabled");
29261         }
29262     },
29263
29264     /**
29265      * Enables this TabPanelItem if it was previously disabled.
29266      */
29267     enable : function(){
29268         this.disabled = false;
29269         this.pnode.removeClass("disabled");
29270     },
29271
29272     /**
29273      * Sets the content for this TabPanelItem.
29274      * @param {String} content The content
29275      * @param {Boolean} loadScripts true to look for and load scripts
29276      */
29277     setContent : function(content, loadScripts){
29278         this.bodyEl.update(content, loadScripts);
29279     },
29280
29281     /**
29282      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
29283      * @return {Roo.UpdateManager} The UpdateManager
29284      */
29285     getUpdateManager : function(){
29286         return this.bodyEl.getUpdateManager();
29287     },
29288
29289     /**
29290      * Set a URL to be used to load the content for this TabPanelItem.
29291      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
29292      * @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)
29293      * @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)
29294      * @return {Roo.UpdateManager} The UpdateManager
29295      */
29296     setUrl : function(url, params, loadOnce){
29297         if(this.refreshDelegate){
29298             this.un('activate', this.refreshDelegate);
29299         }
29300         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
29301         this.on("activate", this.refreshDelegate);
29302         return this.bodyEl.getUpdateManager();
29303     },
29304
29305     /** @private */
29306     _handleRefresh : function(url, params, loadOnce){
29307         if(!loadOnce || !this.loaded){
29308             var updater = this.bodyEl.getUpdateManager();
29309             updater.update(url, params, this._setLoaded.createDelegate(this));
29310         }
29311     },
29312
29313     /**
29314      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
29315      *   Will fail silently if the setUrl method has not been called.
29316      *   This does not activate the panel, just updates its content.
29317      */
29318     refresh : function(){
29319         if(this.refreshDelegate){
29320            this.loaded = false;
29321            this.refreshDelegate();
29322         }
29323     },
29324
29325     /** @private */
29326     _setLoaded : function(){
29327         this.loaded = true;
29328     },
29329
29330     /** @private */
29331     closeClick : function(e){
29332         var o = {};
29333         e.stopEvent();
29334         this.fireEvent("beforeclose", this, o);
29335         if(o.cancel !== true){
29336             this.tabPanel.removeTab(this.id);
29337         }
29338     },
29339     /**
29340      * The text displayed in the tooltip for the close icon.
29341      * @type String
29342      */
29343     closeText : "Close this tab"
29344 });
29345
29346 /** @private */
29347 Roo.TabPanel.prototype.createStrip = function(container){
29348     var strip = document.createElement("div");
29349     strip.className = "x-tabs-wrap";
29350     container.appendChild(strip);
29351     return strip;
29352 };
29353 /** @private */
29354 Roo.TabPanel.prototype.createStripList = function(strip){
29355     // div wrapper for retard IE
29356     // returns the "tr" element.
29357     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
29358         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
29359         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
29360     return strip.firstChild.firstChild.firstChild.firstChild;
29361 };
29362 /** @private */
29363 Roo.TabPanel.prototype.createBody = function(container){
29364     var body = document.createElement("div");
29365     Roo.id(body, "tab-body");
29366     Roo.fly(body).addClass("x-tabs-body");
29367     container.appendChild(body);
29368     return body;
29369 };
29370 /** @private */
29371 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
29372     var body = Roo.getDom(id);
29373     if(!body){
29374         body = document.createElement("div");
29375         body.id = id;
29376     }
29377     Roo.fly(body).addClass("x-tabs-item-body");
29378     bodyEl.insertBefore(body, bodyEl.firstChild);
29379     return body;
29380 };
29381 /** @private */
29382 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
29383     var td = document.createElement("td");
29384     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
29385     //stripEl.appendChild(td);
29386     if(closable){
29387         td.className = "x-tabs-closable";
29388         if(!this.closeTpl){
29389             this.closeTpl = new Roo.Template(
29390                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29391                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
29392                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
29393             );
29394         }
29395         var el = this.closeTpl.overwrite(td, {"text": text});
29396         var close = el.getElementsByTagName("div")[0];
29397         var inner = el.getElementsByTagName("em")[0];
29398         return {"el": el, "close": close, "inner": inner};
29399     } else {
29400         if(!this.tabTpl){
29401             this.tabTpl = new Roo.Template(
29402                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
29403                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
29404             );
29405         }
29406         var el = this.tabTpl.overwrite(td, {"text": text});
29407         var inner = el.getElementsByTagName("em")[0];
29408         return {"el": el, "inner": inner};
29409     }
29410 };/*
29411  * Based on:
29412  * Ext JS Library 1.1.1
29413  * Copyright(c) 2006-2007, Ext JS, LLC.
29414  *
29415  * Originally Released Under LGPL - original licence link has changed is not relivant.
29416  *
29417  * Fork - LGPL
29418  * <script type="text/javascript">
29419  */
29420
29421 /**
29422  * @class Roo.Button
29423  * @extends Roo.util.Observable
29424  * Simple Button class
29425  * @cfg {String} text The button text
29426  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
29427  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
29428  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
29429  * @cfg {Object} scope The scope of the handler
29430  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
29431  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
29432  * @cfg {Boolean} hidden True to start hidden (defaults to false)
29433  * @cfg {Boolean} disabled True to start disabled (defaults to false)
29434  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
29435  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
29436    applies if enableToggle = true)
29437  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
29438  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
29439   an {@link Roo.util.ClickRepeater} config object (defaults to false).
29440  * @constructor
29441  * Create a new button
29442  * @param {Object} config The config object
29443  */
29444 Roo.Button = function(renderTo, config)
29445 {
29446     if (!config) {
29447         config = renderTo;
29448         renderTo = config.renderTo || false;
29449     }
29450     
29451     Roo.apply(this, config);
29452     this.addEvents({
29453         /**
29454              * @event click
29455              * Fires when this button is clicked
29456              * @param {Button} this
29457              * @param {EventObject} e The click event
29458              */
29459             "click" : true,
29460         /**
29461              * @event toggle
29462              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
29463              * @param {Button} this
29464              * @param {Boolean} pressed
29465              */
29466             "toggle" : true,
29467         /**
29468              * @event mouseover
29469              * Fires when the mouse hovers over the button
29470              * @param {Button} this
29471              * @param {Event} e The event object
29472              */
29473         'mouseover' : true,
29474         /**
29475              * @event mouseout
29476              * Fires when the mouse exits the button
29477              * @param {Button} this
29478              * @param {Event} e The event object
29479              */
29480         'mouseout': true,
29481          /**
29482              * @event render
29483              * Fires when the button is rendered
29484              * @param {Button} this
29485              */
29486         'render': true
29487     });
29488     if(this.menu){
29489         this.menu = Roo.menu.MenuMgr.get(this.menu);
29490     }
29491     // register listeners first!!  - so render can be captured..
29492     Roo.util.Observable.call(this);
29493     if(renderTo){
29494         this.render(renderTo);
29495     }
29496     
29497   
29498 };
29499
29500 Roo.extend(Roo.Button, Roo.util.Observable, {
29501     /**
29502      * 
29503      */
29504     
29505     /**
29506      * Read-only. True if this button is hidden
29507      * @type Boolean
29508      */
29509     hidden : false,
29510     /**
29511      * Read-only. True if this button is disabled
29512      * @type Boolean
29513      */
29514     disabled : false,
29515     /**
29516      * Read-only. True if this button is pressed (only if enableToggle = true)
29517      * @type Boolean
29518      */
29519     pressed : false,
29520
29521     /**
29522      * @cfg {Number} tabIndex 
29523      * The DOM tabIndex for this button (defaults to undefined)
29524      */
29525     tabIndex : undefined,
29526
29527     /**
29528      * @cfg {Boolean} enableToggle
29529      * True to enable pressed/not pressed toggling (defaults to false)
29530      */
29531     enableToggle: false,
29532     /**
29533      * @cfg {Mixed} menu
29534      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
29535      */
29536     menu : undefined,
29537     /**
29538      * @cfg {String} menuAlign
29539      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
29540      */
29541     menuAlign : "tl-bl?",
29542
29543     /**
29544      * @cfg {String} iconCls
29545      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
29546      */
29547     iconCls : undefined,
29548     /**
29549      * @cfg {String} type
29550      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
29551      */
29552     type : 'button',
29553
29554     // private
29555     menuClassTarget: 'tr',
29556
29557     /**
29558      * @cfg {String} clickEvent
29559      * The type of event to map to the button's event handler (defaults to 'click')
29560      */
29561     clickEvent : 'click',
29562
29563     /**
29564      * @cfg {Boolean} handleMouseEvents
29565      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
29566      */
29567     handleMouseEvents : true,
29568
29569     /**
29570      * @cfg {String} tooltipType
29571      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
29572      */
29573     tooltipType : 'qtip',
29574
29575     /**
29576      * @cfg {String} cls
29577      * A CSS class to apply to the button's main element.
29578      */
29579     
29580     /**
29581      * @cfg {Roo.Template} template (Optional)
29582      * An {@link Roo.Template} with which to create the Button's main element. This Template must
29583      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
29584      * require code modifications if required elements (e.g. a button) aren't present.
29585      */
29586
29587     // private
29588     render : function(renderTo){
29589         var btn;
29590         if(this.hideParent){
29591             this.parentEl = Roo.get(renderTo);
29592         }
29593         if(!this.dhconfig){
29594             if(!this.template){
29595                 if(!Roo.Button.buttonTemplate){
29596                     // hideous table template
29597                     Roo.Button.buttonTemplate = new Roo.Template(
29598                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
29599                         '<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>',
29600                         "</tr></tbody></table>");
29601                 }
29602                 this.template = Roo.Button.buttonTemplate;
29603             }
29604             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
29605             var btnEl = btn.child("button:first");
29606             btnEl.on('focus', this.onFocus, this);
29607             btnEl.on('blur', this.onBlur, this);
29608             if(this.cls){
29609                 btn.addClass(this.cls);
29610             }
29611             if(this.icon){
29612                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
29613             }
29614             if(this.iconCls){
29615                 btnEl.addClass(this.iconCls);
29616                 if(!this.cls){
29617                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
29618                 }
29619             }
29620             if(this.tabIndex !== undefined){
29621                 btnEl.dom.tabIndex = this.tabIndex;
29622             }
29623             if(this.tooltip){
29624                 if(typeof this.tooltip == 'object'){
29625                     Roo.QuickTips.tips(Roo.apply({
29626                           target: btnEl.id
29627                     }, this.tooltip));
29628                 } else {
29629                     btnEl.dom[this.tooltipType] = this.tooltip;
29630                 }
29631             }
29632         }else{
29633             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
29634         }
29635         this.el = btn;
29636         if(this.id){
29637             this.el.dom.id = this.el.id = this.id;
29638         }
29639         if(this.menu){
29640             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
29641             this.menu.on("show", this.onMenuShow, this);
29642             this.menu.on("hide", this.onMenuHide, this);
29643         }
29644         btn.addClass("x-btn");
29645         if(Roo.isIE && !Roo.isIE7){
29646             this.autoWidth.defer(1, this);
29647         }else{
29648             this.autoWidth();
29649         }
29650         if(this.handleMouseEvents){
29651             btn.on("mouseover", this.onMouseOver, this);
29652             btn.on("mouseout", this.onMouseOut, this);
29653             btn.on("mousedown", this.onMouseDown, this);
29654         }
29655         btn.on(this.clickEvent, this.onClick, this);
29656         //btn.on("mouseup", this.onMouseUp, this);
29657         if(this.hidden){
29658             this.hide();
29659         }
29660         if(this.disabled){
29661             this.disable();
29662         }
29663         Roo.ButtonToggleMgr.register(this);
29664         if(this.pressed){
29665             this.el.addClass("x-btn-pressed");
29666         }
29667         if(this.repeat){
29668             var repeater = new Roo.util.ClickRepeater(btn,
29669                 typeof this.repeat == "object" ? this.repeat : {}
29670             );
29671             repeater.on("click", this.onClick,  this);
29672         }
29673         
29674         this.fireEvent('render', this);
29675         
29676     },
29677     /**
29678      * Returns the button's underlying element
29679      * @return {Roo.Element} The element
29680      */
29681     getEl : function(){
29682         return this.el;  
29683     },
29684     
29685     /**
29686      * Destroys this Button and removes any listeners.
29687      */
29688     destroy : function(){
29689         Roo.ButtonToggleMgr.unregister(this);
29690         this.el.removeAllListeners();
29691         this.purgeListeners();
29692         this.el.remove();
29693     },
29694
29695     // private
29696     autoWidth : function(){
29697         if(this.el){
29698             this.el.setWidth("auto");
29699             if(Roo.isIE7 && Roo.isStrict){
29700                 var ib = this.el.child('button');
29701                 if(ib && ib.getWidth() > 20){
29702                     ib.clip();
29703                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
29704                 }
29705             }
29706             if(this.minWidth){
29707                 if(this.hidden){
29708                     this.el.beginMeasure();
29709                 }
29710                 if(this.el.getWidth() < this.minWidth){
29711                     this.el.setWidth(this.minWidth);
29712                 }
29713                 if(this.hidden){
29714                     this.el.endMeasure();
29715                 }
29716             }
29717         }
29718     },
29719
29720     /**
29721      * Assigns this button's click handler
29722      * @param {Function} handler The function to call when the button is clicked
29723      * @param {Object} scope (optional) Scope for the function passed in
29724      */
29725     setHandler : function(handler, scope){
29726         this.handler = handler;
29727         this.scope = scope;  
29728     },
29729     
29730     /**
29731      * Sets this button's text
29732      * @param {String} text The button text
29733      */
29734     setText : function(text){
29735         this.text = text;
29736         if(this.el){
29737             this.el.child("td.x-btn-center button.x-btn-text").update(text);
29738         }
29739         this.autoWidth();
29740     },
29741     
29742     /**
29743      * Gets the text for this button
29744      * @return {String} The button text
29745      */
29746     getText : function(){
29747         return this.text;  
29748     },
29749     
29750     /**
29751      * Show this button
29752      */
29753     show: function(){
29754         this.hidden = false;
29755         if(this.el){
29756             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
29757         }
29758     },
29759     
29760     /**
29761      * Hide this button
29762      */
29763     hide: function(){
29764         this.hidden = true;
29765         if(this.el){
29766             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
29767         }
29768     },
29769     
29770     /**
29771      * Convenience function for boolean show/hide
29772      * @param {Boolean} visible True to show, false to hide
29773      */
29774     setVisible: function(visible){
29775         if(visible) {
29776             this.show();
29777         }else{
29778             this.hide();
29779         }
29780     },
29781     
29782     /**
29783      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
29784      * @param {Boolean} state (optional) Force a particular state
29785      */
29786     toggle : function(state){
29787         state = state === undefined ? !this.pressed : state;
29788         if(state != this.pressed){
29789             if(state){
29790                 this.el.addClass("x-btn-pressed");
29791                 this.pressed = true;
29792                 this.fireEvent("toggle", this, true);
29793             }else{
29794                 this.el.removeClass("x-btn-pressed");
29795                 this.pressed = false;
29796                 this.fireEvent("toggle", this, false);
29797             }
29798             if(this.toggleHandler){
29799                 this.toggleHandler.call(this.scope || this, this, state);
29800             }
29801         }
29802     },
29803     
29804     /**
29805      * Focus the button
29806      */
29807     focus : function(){
29808         this.el.child('button:first').focus();
29809     },
29810     
29811     /**
29812      * Disable this button
29813      */
29814     disable : function(){
29815         if(this.el){
29816             this.el.addClass("x-btn-disabled");
29817         }
29818         this.disabled = true;
29819     },
29820     
29821     /**
29822      * Enable this button
29823      */
29824     enable : function(){
29825         if(this.el){
29826             this.el.removeClass("x-btn-disabled");
29827         }
29828         this.disabled = false;
29829     },
29830
29831     /**
29832      * Convenience function for boolean enable/disable
29833      * @param {Boolean} enabled True to enable, false to disable
29834      */
29835     setDisabled : function(v){
29836         this[v !== true ? "enable" : "disable"]();
29837     },
29838
29839     // private
29840     onClick : function(e)
29841     {
29842         if(e){
29843             e.preventDefault();
29844         }
29845         if(e.button != 0){
29846             return;
29847         }
29848         if(!this.disabled){
29849             if(this.enableToggle){
29850                 this.toggle();
29851             }
29852             if(this.menu && !this.menu.isVisible()){
29853                 this.menu.show(this.el, this.menuAlign);
29854             }
29855             this.fireEvent("click", this, e);
29856             if(this.handler){
29857                 this.el.removeClass("x-btn-over");
29858                 this.handler.call(this.scope || this, this, e);
29859             }
29860         }
29861     },
29862     // private
29863     onMouseOver : function(e){
29864         if(!this.disabled){
29865             this.el.addClass("x-btn-over");
29866             this.fireEvent('mouseover', this, e);
29867         }
29868     },
29869     // private
29870     onMouseOut : function(e){
29871         if(!e.within(this.el,  true)){
29872             this.el.removeClass("x-btn-over");
29873             this.fireEvent('mouseout', this, e);
29874         }
29875     },
29876     // private
29877     onFocus : function(e){
29878         if(!this.disabled){
29879             this.el.addClass("x-btn-focus");
29880         }
29881     },
29882     // private
29883     onBlur : function(e){
29884         this.el.removeClass("x-btn-focus");
29885     },
29886     // private
29887     onMouseDown : function(e){
29888         if(!this.disabled && e.button == 0){
29889             this.el.addClass("x-btn-click");
29890             Roo.get(document).on('mouseup', this.onMouseUp, this);
29891         }
29892     },
29893     // private
29894     onMouseUp : function(e){
29895         if(e.button == 0){
29896             this.el.removeClass("x-btn-click");
29897             Roo.get(document).un('mouseup', this.onMouseUp, this);
29898         }
29899     },
29900     // private
29901     onMenuShow : function(e){
29902         this.el.addClass("x-btn-menu-active");
29903     },
29904     // private
29905     onMenuHide : function(e){
29906         this.el.removeClass("x-btn-menu-active");
29907     }   
29908 });
29909
29910 // Private utility class used by Button
29911 Roo.ButtonToggleMgr = function(){
29912    var groups = {};
29913    
29914    function toggleGroup(btn, state){
29915        if(state){
29916            var g = groups[btn.toggleGroup];
29917            for(var i = 0, l = g.length; i < l; i++){
29918                if(g[i] != btn){
29919                    g[i].toggle(false);
29920                }
29921            }
29922        }
29923    }
29924    
29925    return {
29926        register : function(btn){
29927            if(!btn.toggleGroup){
29928                return;
29929            }
29930            var g = groups[btn.toggleGroup];
29931            if(!g){
29932                g = groups[btn.toggleGroup] = [];
29933            }
29934            g.push(btn);
29935            btn.on("toggle", toggleGroup);
29936        },
29937        
29938        unregister : function(btn){
29939            if(!btn.toggleGroup){
29940                return;
29941            }
29942            var g = groups[btn.toggleGroup];
29943            if(g){
29944                g.remove(btn);
29945                btn.un("toggle", toggleGroup);
29946            }
29947        }
29948    };
29949 }();/*
29950  * Based on:
29951  * Ext JS Library 1.1.1
29952  * Copyright(c) 2006-2007, Ext JS, LLC.
29953  *
29954  * Originally Released Under LGPL - original licence link has changed is not relivant.
29955  *
29956  * Fork - LGPL
29957  * <script type="text/javascript">
29958  */
29959  
29960 /**
29961  * @class Roo.SplitButton
29962  * @extends Roo.Button
29963  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
29964  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
29965  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
29966  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
29967  * @cfg {String} arrowTooltip The title attribute of the arrow
29968  * @constructor
29969  * Create a new menu button
29970  * @param {String/HTMLElement/Element} renderTo The element to append the button to
29971  * @param {Object} config The config object
29972  */
29973 Roo.SplitButton = function(renderTo, config){
29974     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
29975     /**
29976      * @event arrowclick
29977      * Fires when this button's arrow is clicked
29978      * @param {SplitButton} this
29979      * @param {EventObject} e The click event
29980      */
29981     this.addEvents({"arrowclick":true});
29982 };
29983
29984 Roo.extend(Roo.SplitButton, Roo.Button, {
29985     render : function(renderTo){
29986         // this is one sweet looking template!
29987         var tpl = new Roo.Template(
29988             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
29989             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
29990             '<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>',
29991             "</tbody></table></td><td>",
29992             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
29993             '<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>',
29994             "</tbody></table></td></tr></table>"
29995         );
29996         var btn = tpl.append(renderTo, [this.text, this.type], true);
29997         var btnEl = btn.child("button");
29998         if(this.cls){
29999             btn.addClass(this.cls);
30000         }
30001         if(this.icon){
30002             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30003         }
30004         if(this.iconCls){
30005             btnEl.addClass(this.iconCls);
30006             if(!this.cls){
30007                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30008             }
30009         }
30010         this.el = btn;
30011         if(this.handleMouseEvents){
30012             btn.on("mouseover", this.onMouseOver, this);
30013             btn.on("mouseout", this.onMouseOut, this);
30014             btn.on("mousedown", this.onMouseDown, this);
30015             btn.on("mouseup", this.onMouseUp, this);
30016         }
30017         btn.on(this.clickEvent, this.onClick, this);
30018         if(this.tooltip){
30019             if(typeof this.tooltip == 'object'){
30020                 Roo.QuickTips.tips(Roo.apply({
30021                       target: btnEl.id
30022                 }, this.tooltip));
30023             } else {
30024                 btnEl.dom[this.tooltipType] = this.tooltip;
30025             }
30026         }
30027         if(this.arrowTooltip){
30028             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30029         }
30030         if(this.hidden){
30031             this.hide();
30032         }
30033         if(this.disabled){
30034             this.disable();
30035         }
30036         if(this.pressed){
30037             this.el.addClass("x-btn-pressed");
30038         }
30039         if(Roo.isIE && !Roo.isIE7){
30040             this.autoWidth.defer(1, this);
30041         }else{
30042             this.autoWidth();
30043         }
30044         if(this.menu){
30045             this.menu.on("show", this.onMenuShow, this);
30046             this.menu.on("hide", this.onMenuHide, this);
30047         }
30048         this.fireEvent('render', this);
30049     },
30050
30051     // private
30052     autoWidth : function(){
30053         if(this.el){
30054             var tbl = this.el.child("table:first");
30055             var tbl2 = this.el.child("table:last");
30056             this.el.setWidth("auto");
30057             tbl.setWidth("auto");
30058             if(Roo.isIE7 && Roo.isStrict){
30059                 var ib = this.el.child('button:first');
30060                 if(ib && ib.getWidth() > 20){
30061                     ib.clip();
30062                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30063                 }
30064             }
30065             if(this.minWidth){
30066                 if(this.hidden){
30067                     this.el.beginMeasure();
30068                 }
30069                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30070                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30071                 }
30072                 if(this.hidden){
30073                     this.el.endMeasure();
30074                 }
30075             }
30076             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30077         } 
30078     },
30079     /**
30080      * Sets this button's click handler
30081      * @param {Function} handler The function to call when the button is clicked
30082      * @param {Object} scope (optional) Scope for the function passed above
30083      */
30084     setHandler : function(handler, scope){
30085         this.handler = handler;
30086         this.scope = scope;  
30087     },
30088     
30089     /**
30090      * Sets this button's arrow click handler
30091      * @param {Function} handler The function to call when the arrow is clicked
30092      * @param {Object} scope (optional) Scope for the function passed above
30093      */
30094     setArrowHandler : function(handler, scope){
30095         this.arrowHandler = handler;
30096         this.scope = scope;  
30097     },
30098     
30099     /**
30100      * Focus the button
30101      */
30102     focus : function(){
30103         if(this.el){
30104             this.el.child("button:first").focus();
30105         }
30106     },
30107
30108     // private
30109     onClick : function(e){
30110         e.preventDefault();
30111         if(!this.disabled){
30112             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30113                 if(this.menu && !this.menu.isVisible()){
30114                     this.menu.show(this.el, this.menuAlign);
30115                 }
30116                 this.fireEvent("arrowclick", this, e);
30117                 if(this.arrowHandler){
30118                     this.arrowHandler.call(this.scope || this, this, e);
30119                 }
30120             }else{
30121                 this.fireEvent("click", this, e);
30122                 if(this.handler){
30123                     this.handler.call(this.scope || this, this, e);
30124                 }
30125             }
30126         }
30127     },
30128     // private
30129     onMouseDown : function(e){
30130         if(!this.disabled){
30131             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30132         }
30133     },
30134     // private
30135     onMouseUp : function(e){
30136         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30137     }   
30138 });
30139
30140
30141 // backwards compat
30142 Roo.MenuButton = Roo.SplitButton;/*
30143  * Based on:
30144  * Ext JS Library 1.1.1
30145  * Copyright(c) 2006-2007, Ext JS, LLC.
30146  *
30147  * Originally Released Under LGPL - original licence link has changed is not relivant.
30148  *
30149  * Fork - LGPL
30150  * <script type="text/javascript">
30151  */
30152
30153 /**
30154  * @class Roo.Toolbar
30155  * Basic Toolbar class.
30156  * @constructor
30157  * Creates a new Toolbar
30158  * @param {Object} container The config object
30159  */ 
30160 Roo.Toolbar = function(container, buttons, config)
30161 {
30162     /// old consturctor format still supported..
30163     if(container instanceof Array){ // omit the container for later rendering
30164         buttons = container;
30165         config = buttons;
30166         container = null;
30167     }
30168     if (typeof(container) == 'object' && container.xtype) {
30169         config = container;
30170         container = config.container;
30171         buttons = config.buttons || []; // not really - use items!!
30172     }
30173     var xitems = [];
30174     if (config && config.items) {
30175         xitems = config.items;
30176         delete config.items;
30177     }
30178     Roo.apply(this, config);
30179     this.buttons = buttons;
30180     
30181     if(container){
30182         this.render(container);
30183     }
30184     this.xitems = xitems;
30185     Roo.each(xitems, function(b) {
30186         this.add(b);
30187     }, this);
30188     
30189 };
30190
30191 Roo.Toolbar.prototype = {
30192     /**
30193      * @cfg {Array} items
30194      * array of button configs or elements to add (will be converted to a MixedCollection)
30195      */
30196     
30197     /**
30198      * @cfg {String/HTMLElement/Element} container
30199      * The id or element that will contain the toolbar
30200      */
30201     // private
30202     render : function(ct){
30203         this.el = Roo.get(ct);
30204         if(this.cls){
30205             this.el.addClass(this.cls);
30206         }
30207         // using a table allows for vertical alignment
30208         // 100% width is needed by Safari...
30209         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30210         this.tr = this.el.child("tr", true);
30211         var autoId = 0;
30212         this.items = new Roo.util.MixedCollection(false, function(o){
30213             return o.id || ("item" + (++autoId));
30214         });
30215         if(this.buttons){
30216             this.add.apply(this, this.buttons);
30217             delete this.buttons;
30218         }
30219     },
30220
30221     /**
30222      * Adds element(s) to the toolbar -- this function takes a variable number of 
30223      * arguments of mixed type and adds them to the toolbar.
30224      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30225      * <ul>
30226      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30227      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30228      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30229      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30230      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30231      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30232      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30233      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30234      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30235      * </ul>
30236      * @param {Mixed} arg2
30237      * @param {Mixed} etc.
30238      */
30239     add : function(){
30240         var a = arguments, l = a.length;
30241         for(var i = 0; i < l; i++){
30242             this._add(a[i]);
30243         }
30244     },
30245     // private..
30246     _add : function(el) {
30247         
30248         if (el.xtype) {
30249             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30250         }
30251         
30252         if (el.applyTo){ // some kind of form field
30253             return this.addField(el);
30254         } 
30255         if (el.render){ // some kind of Toolbar.Item
30256             return this.addItem(el);
30257         }
30258         if (typeof el == "string"){ // string
30259             if(el == "separator" || el == "-"){
30260                 return this.addSeparator();
30261             }
30262             if (el == " "){
30263                 return this.addSpacer();
30264             }
30265             if(el == "->"){
30266                 return this.addFill();
30267             }
30268             return this.addText(el);
30269             
30270         }
30271         if(el.tagName){ // element
30272             return this.addElement(el);
30273         }
30274         if(typeof el == "object"){ // must be button config?
30275             return this.addButton(el);
30276         }
30277         // and now what?!?!
30278         return false;
30279         
30280     },
30281     
30282     /**
30283      * Add an Xtype element
30284      * @param {Object} xtype Xtype Object
30285      * @return {Object} created Object
30286      */
30287     addxtype : function(e){
30288         return this.add(e);  
30289     },
30290     
30291     /**
30292      * Returns the Element for this toolbar.
30293      * @return {Roo.Element}
30294      */
30295     getEl : function(){
30296         return this.el;  
30297     },
30298     
30299     /**
30300      * Adds a separator
30301      * @return {Roo.Toolbar.Item} The separator item
30302      */
30303     addSeparator : function(){
30304         return this.addItem(new Roo.Toolbar.Separator());
30305     },
30306
30307     /**
30308      * Adds a spacer element
30309      * @return {Roo.Toolbar.Spacer} The spacer item
30310      */
30311     addSpacer : function(){
30312         return this.addItem(new Roo.Toolbar.Spacer());
30313     },
30314
30315     /**
30316      * Adds a fill element that forces subsequent additions to the right side of the toolbar
30317      * @return {Roo.Toolbar.Fill} The fill item
30318      */
30319     addFill : function(){
30320         return this.addItem(new Roo.Toolbar.Fill());
30321     },
30322
30323     /**
30324      * Adds any standard HTML element to the toolbar
30325      * @param {String/HTMLElement/Element} el The element or id of the element to add
30326      * @return {Roo.Toolbar.Item} The element's item
30327      */
30328     addElement : function(el){
30329         return this.addItem(new Roo.Toolbar.Item(el));
30330     },
30331     /**
30332      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
30333      * @type Roo.util.MixedCollection  
30334      */
30335     items : false,
30336      
30337     /**
30338      * Adds any Toolbar.Item or subclass
30339      * @param {Roo.Toolbar.Item} item
30340      * @return {Roo.Toolbar.Item} The item
30341      */
30342     addItem : function(item){
30343         var td = this.nextBlock();
30344         item.render(td);
30345         this.items.add(item);
30346         return item;
30347     },
30348     
30349     /**
30350      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
30351      * @param {Object/Array} config A button config or array of configs
30352      * @return {Roo.Toolbar.Button/Array}
30353      */
30354     addButton : function(config){
30355         if(config instanceof Array){
30356             var buttons = [];
30357             for(var i = 0, len = config.length; i < len; i++) {
30358                 buttons.push(this.addButton(config[i]));
30359             }
30360             return buttons;
30361         }
30362         var b = config;
30363         if(!(config instanceof Roo.Toolbar.Button)){
30364             b = config.split ?
30365                 new Roo.Toolbar.SplitButton(config) :
30366                 new Roo.Toolbar.Button(config);
30367         }
30368         var td = this.nextBlock();
30369         b.render(td);
30370         this.items.add(b);
30371         return b;
30372     },
30373     
30374     /**
30375      * Adds text to the toolbar
30376      * @param {String} text The text to add
30377      * @return {Roo.Toolbar.Item} The element's item
30378      */
30379     addText : function(text){
30380         return this.addItem(new Roo.Toolbar.TextItem(text));
30381     },
30382     
30383     /**
30384      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
30385      * @param {Number} index The index where the item is to be inserted
30386      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
30387      * @return {Roo.Toolbar.Button/Item}
30388      */
30389     insertButton : function(index, item){
30390         if(item instanceof Array){
30391             var buttons = [];
30392             for(var i = 0, len = item.length; i < len; i++) {
30393                buttons.push(this.insertButton(index + i, item[i]));
30394             }
30395             return buttons;
30396         }
30397         if (!(item instanceof Roo.Toolbar.Button)){
30398            item = new Roo.Toolbar.Button(item);
30399         }
30400         var td = document.createElement("td");
30401         this.tr.insertBefore(td, this.tr.childNodes[index]);
30402         item.render(td);
30403         this.items.insert(index, item);
30404         return item;
30405     },
30406     
30407     /**
30408      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
30409      * @param {Object} config
30410      * @return {Roo.Toolbar.Item} The element's item
30411      */
30412     addDom : function(config, returnEl){
30413         var td = this.nextBlock();
30414         Roo.DomHelper.overwrite(td, config);
30415         var ti = new Roo.Toolbar.Item(td.firstChild);
30416         ti.render(td);
30417         this.items.add(ti);
30418         return ti;
30419     },
30420
30421     /**
30422      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
30423      * @type Roo.util.MixedCollection  
30424      */
30425     fields : false,
30426     
30427     /**
30428      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
30429      * Note: the field should not have been rendered yet. For a field that has already been
30430      * rendered, use {@link #addElement}.
30431      * @param {Roo.form.Field} field
30432      * @return {Roo.ToolbarItem}
30433      */
30434      
30435       
30436     addField : function(field) {
30437         if (!this.fields) {
30438             var autoId = 0;
30439             this.fields = new Roo.util.MixedCollection(false, function(o){
30440                 return o.id || ("item" + (++autoId));
30441             });
30442
30443         }
30444         
30445         var td = this.nextBlock();
30446         field.render(td);
30447         var ti = new Roo.Toolbar.Item(td.firstChild);
30448         ti.render(td);
30449         this.items.add(ti);
30450         this.fields.add(field);
30451         return ti;
30452     },
30453     /**
30454      * Hide the toolbar
30455      * @method hide
30456      */
30457      
30458       
30459     hide : function()
30460     {
30461         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
30462         this.el.child('div').hide();
30463     },
30464     /**
30465      * Show the toolbar
30466      * @method show
30467      */
30468     show : function()
30469     {
30470         this.el.child('div').show();
30471     },
30472       
30473     // private
30474     nextBlock : function(){
30475         var td = document.createElement("td");
30476         this.tr.appendChild(td);
30477         return td;
30478     },
30479
30480     // private
30481     destroy : function(){
30482         if(this.items){ // rendered?
30483             Roo.destroy.apply(Roo, this.items.items);
30484         }
30485         if(this.fields){ // rendered?
30486             Roo.destroy.apply(Roo, this.fields.items);
30487         }
30488         Roo.Element.uncache(this.el, this.tr);
30489     }
30490 };
30491
30492 /**
30493  * @class Roo.Toolbar.Item
30494  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
30495  * @constructor
30496  * Creates a new Item
30497  * @param {HTMLElement} el 
30498  */
30499 Roo.Toolbar.Item = function(el){
30500     var cfg = {};
30501     if (typeof (el.xtype) != 'undefined') {
30502         cfg = el;
30503         el = cfg.el;
30504     }
30505     
30506     this.el = Roo.getDom(el);
30507     this.id = Roo.id(this.el);
30508     this.hidden = false;
30509     
30510     this.addEvents({
30511          /**
30512              * @event render
30513              * Fires when the button is rendered
30514              * @param {Button} this
30515              */
30516         'render': true
30517     });
30518     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
30519 };
30520 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
30521 //Roo.Toolbar.Item.prototype = {
30522     
30523     /**
30524      * Get this item's HTML Element
30525      * @return {HTMLElement}
30526      */
30527     getEl : function(){
30528        return this.el;  
30529     },
30530
30531     // private
30532     render : function(td){
30533         
30534          this.td = td;
30535         td.appendChild(this.el);
30536         
30537         this.fireEvent('render', this);
30538     },
30539     
30540     /**
30541      * Removes and destroys this item.
30542      */
30543     destroy : function(){
30544         this.td.parentNode.removeChild(this.td);
30545     },
30546     
30547     /**
30548      * Shows this item.
30549      */
30550     show: function(){
30551         this.hidden = false;
30552         this.td.style.display = "";
30553     },
30554     
30555     /**
30556      * Hides this item.
30557      */
30558     hide: function(){
30559         this.hidden = true;
30560         this.td.style.display = "none";
30561     },
30562     
30563     /**
30564      * Convenience function for boolean show/hide.
30565      * @param {Boolean} visible true to show/false to hide
30566      */
30567     setVisible: function(visible){
30568         if(visible) {
30569             this.show();
30570         }else{
30571             this.hide();
30572         }
30573     },
30574     
30575     /**
30576      * Try to focus this item.
30577      */
30578     focus : function(){
30579         Roo.fly(this.el).focus();
30580     },
30581     
30582     /**
30583      * Disables this item.
30584      */
30585     disable : function(){
30586         Roo.fly(this.td).addClass("x-item-disabled");
30587         this.disabled = true;
30588         this.el.disabled = true;
30589     },
30590     
30591     /**
30592      * Enables this item.
30593      */
30594     enable : function(){
30595         Roo.fly(this.td).removeClass("x-item-disabled");
30596         this.disabled = false;
30597         this.el.disabled = false;
30598     }
30599 });
30600
30601
30602 /**
30603  * @class Roo.Toolbar.Separator
30604  * @extends Roo.Toolbar.Item
30605  * A simple toolbar separator class
30606  * @constructor
30607  * Creates a new Separator
30608  */
30609 Roo.Toolbar.Separator = function(cfg){
30610     
30611     var s = document.createElement("span");
30612     s.className = "ytb-sep";
30613     if (cfg) {
30614         cfg.el = s;
30615     }
30616     
30617     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
30618 };
30619 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
30620     enable:Roo.emptyFn,
30621     disable:Roo.emptyFn,
30622     focus:Roo.emptyFn
30623 });
30624
30625 /**
30626  * @class Roo.Toolbar.Spacer
30627  * @extends Roo.Toolbar.Item
30628  * A simple element that adds extra horizontal space to a toolbar.
30629  * @constructor
30630  * Creates a new Spacer
30631  */
30632 Roo.Toolbar.Spacer = function(cfg){
30633     var s = document.createElement("div");
30634     s.className = "ytb-spacer";
30635     if (cfg) {
30636         cfg.el = s;
30637     }
30638     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
30639 };
30640 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
30641     enable:Roo.emptyFn,
30642     disable:Roo.emptyFn,
30643     focus:Roo.emptyFn
30644 });
30645
30646 /**
30647  * @class Roo.Toolbar.Fill
30648  * @extends Roo.Toolbar.Spacer
30649  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
30650  * @constructor
30651  * Creates a new Spacer
30652  */
30653 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
30654     // private
30655     render : function(td){
30656         td.style.width = '100%';
30657         Roo.Toolbar.Fill.superclass.render.call(this, td);
30658     }
30659 });
30660
30661 /**
30662  * @class Roo.Toolbar.TextItem
30663  * @extends Roo.Toolbar.Item
30664  * A simple class that renders text directly into a toolbar.
30665  * @constructor
30666  * Creates a new TextItem
30667  * @cfg {string} text 
30668  */
30669 Roo.Toolbar.TextItem = function(cfg){
30670     var  text = cfg || "";
30671     if (typeof(cfg) == 'object') {
30672         text = cfg.text || "";
30673     }  else {
30674         cfg = null;
30675     }
30676     var s = document.createElement("span");
30677     s.className = "ytb-text";
30678     s.innerHTML = text;
30679     if (cfg) {
30680         cfg.el  = s;
30681     }
30682     
30683     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
30684 };
30685 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
30686     
30687      
30688     enable:Roo.emptyFn,
30689     disable:Roo.emptyFn,
30690     focus:Roo.emptyFn
30691 });
30692
30693 /**
30694  * @class Roo.Toolbar.Button
30695  * @extends Roo.Button
30696  * A button that renders into a toolbar.
30697  * @constructor
30698  * Creates a new Button
30699  * @param {Object} config A standard {@link Roo.Button} config object
30700  */
30701 Roo.Toolbar.Button = function(config){
30702     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
30703 };
30704 Roo.extend(Roo.Toolbar.Button, Roo.Button, {
30705     render : function(td){
30706         this.td = td;
30707         Roo.Toolbar.Button.superclass.render.call(this, td);
30708     },
30709     
30710     /**
30711      * Removes and destroys this button
30712      */
30713     destroy : function(){
30714         Roo.Toolbar.Button.superclass.destroy.call(this);
30715         this.td.parentNode.removeChild(this.td);
30716     },
30717     
30718     /**
30719      * Shows this button
30720      */
30721     show: function(){
30722         this.hidden = false;
30723         this.td.style.display = "";
30724     },
30725     
30726     /**
30727      * Hides this button
30728      */
30729     hide: function(){
30730         this.hidden = true;
30731         this.td.style.display = "none";
30732     },
30733
30734     /**
30735      * Disables this item
30736      */
30737     disable : function(){
30738         Roo.fly(this.td).addClass("x-item-disabled");
30739         this.disabled = true;
30740     },
30741
30742     /**
30743      * Enables this item
30744      */
30745     enable : function(){
30746         Roo.fly(this.td).removeClass("x-item-disabled");
30747         this.disabled = false;
30748     }
30749 });
30750 // backwards compat
30751 Roo.ToolbarButton = Roo.Toolbar.Button;
30752
30753 /**
30754  * @class Roo.Toolbar.SplitButton
30755  * @extends Roo.SplitButton
30756  * A menu button that renders into a toolbar.
30757  * @constructor
30758  * Creates a new SplitButton
30759  * @param {Object} config A standard {@link Roo.SplitButton} config object
30760  */
30761 Roo.Toolbar.SplitButton = function(config){
30762     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
30763 };
30764 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
30765     render : function(td){
30766         this.td = td;
30767         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
30768     },
30769     
30770     /**
30771      * Removes and destroys this button
30772      */
30773     destroy : function(){
30774         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
30775         this.td.parentNode.removeChild(this.td);
30776     },
30777     
30778     /**
30779      * Shows this button
30780      */
30781     show: function(){
30782         this.hidden = false;
30783         this.td.style.display = "";
30784     },
30785     
30786     /**
30787      * Hides this button
30788      */
30789     hide: function(){
30790         this.hidden = true;
30791         this.td.style.display = "none";
30792     }
30793 });
30794
30795 // backwards compat
30796 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
30797  * Based on:
30798  * Ext JS Library 1.1.1
30799  * Copyright(c) 2006-2007, Ext JS, LLC.
30800  *
30801  * Originally Released Under LGPL - original licence link has changed is not relivant.
30802  *
30803  * Fork - LGPL
30804  * <script type="text/javascript">
30805  */
30806  
30807 /**
30808  * @class Roo.PagingToolbar
30809  * @extends Roo.Toolbar
30810  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
30811  * @constructor
30812  * Create a new PagingToolbar
30813  * @param {Object} config The config object
30814  */
30815 Roo.PagingToolbar = function(el, ds, config)
30816 {
30817     // old args format still supported... - xtype is prefered..
30818     if (typeof(el) == 'object' && el.xtype) {
30819         // created from xtype...
30820         config = el;
30821         ds = el.dataSource;
30822         el = config.container;
30823     }
30824     var items = [];
30825     if (config.items) {
30826         items = config.items;
30827         config.items = [];
30828     }
30829     
30830     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
30831     this.ds = ds;
30832     this.cursor = 0;
30833     this.renderButtons(this.el);
30834     this.bind(ds);
30835     
30836     // supprot items array.
30837    
30838     Roo.each(items, function(e) {
30839         this.add(Roo.factory(e));
30840     },this);
30841     
30842 };
30843
30844 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
30845     /**
30846      * @cfg {Roo.data.Store} dataSource
30847      * The underlying data store providing the paged data
30848      */
30849     /**
30850      * @cfg {String/HTMLElement/Element} container
30851      * container The id or element that will contain the toolbar
30852      */
30853     /**
30854      * @cfg {Boolean} displayInfo
30855      * True to display the displayMsg (defaults to false)
30856      */
30857     /**
30858      * @cfg {Number} pageSize
30859      * The number of records to display per page (defaults to 20)
30860      */
30861     pageSize: 20,
30862     /**
30863      * @cfg {String} displayMsg
30864      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
30865      */
30866     displayMsg : 'Displaying {0} - {1} of {2}',
30867     /**
30868      * @cfg {String} emptyMsg
30869      * The message to display when no records are found (defaults to "No data to display")
30870      */
30871     emptyMsg : 'No data to display',
30872     /**
30873      * Customizable piece of the default paging text (defaults to "Page")
30874      * @type String
30875      */
30876     beforePageText : "Page",
30877     /**
30878      * Customizable piece of the default paging text (defaults to "of %0")
30879      * @type String
30880      */
30881     afterPageText : "of {0}",
30882     /**
30883      * Customizable piece of the default paging text (defaults to "First Page")
30884      * @type String
30885      */
30886     firstText : "First Page",
30887     /**
30888      * Customizable piece of the default paging text (defaults to "Previous Page")
30889      * @type String
30890      */
30891     prevText : "Previous Page",
30892     /**
30893      * Customizable piece of the default paging text (defaults to "Next Page")
30894      * @type String
30895      */
30896     nextText : "Next Page",
30897     /**
30898      * Customizable piece of the default paging text (defaults to "Last Page")
30899      * @type String
30900      */
30901     lastText : "Last Page",
30902     /**
30903      * Customizable piece of the default paging text (defaults to "Refresh")
30904      * @type String
30905      */
30906     refreshText : "Refresh",
30907
30908     // private
30909     renderButtons : function(el){
30910         Roo.PagingToolbar.superclass.render.call(this, el);
30911         this.first = this.addButton({
30912             tooltip: this.firstText,
30913             cls: "x-btn-icon x-grid-page-first",
30914             disabled: true,
30915             handler: this.onClick.createDelegate(this, ["first"])
30916         });
30917         this.prev = this.addButton({
30918             tooltip: this.prevText,
30919             cls: "x-btn-icon x-grid-page-prev",
30920             disabled: true,
30921             handler: this.onClick.createDelegate(this, ["prev"])
30922         });
30923         //this.addSeparator();
30924         this.add(this.beforePageText);
30925         this.field = Roo.get(this.addDom({
30926            tag: "input",
30927            type: "text",
30928            size: "3",
30929            value: "1",
30930            cls: "x-grid-page-number"
30931         }).el);
30932         this.field.on("keydown", this.onPagingKeydown, this);
30933         this.field.on("focus", function(){this.dom.select();});
30934         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
30935         this.field.setHeight(18);
30936         //this.addSeparator();
30937         this.next = this.addButton({
30938             tooltip: this.nextText,
30939             cls: "x-btn-icon x-grid-page-next",
30940             disabled: true,
30941             handler: this.onClick.createDelegate(this, ["next"])
30942         });
30943         this.last = this.addButton({
30944             tooltip: this.lastText,
30945             cls: "x-btn-icon x-grid-page-last",
30946             disabled: true,
30947             handler: this.onClick.createDelegate(this, ["last"])
30948         });
30949         //this.addSeparator();
30950         this.loading = this.addButton({
30951             tooltip: this.refreshText,
30952             cls: "x-btn-icon x-grid-loading",
30953             handler: this.onClick.createDelegate(this, ["refresh"])
30954         });
30955
30956         if(this.displayInfo){
30957             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
30958         }
30959     },
30960
30961     // private
30962     updateInfo : function(){
30963         if(this.displayEl){
30964             var count = this.ds.getCount();
30965             var msg = count == 0 ?
30966                 this.emptyMsg :
30967                 String.format(
30968                     this.displayMsg,
30969                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
30970                 );
30971             this.displayEl.update(msg);
30972         }
30973     },
30974
30975     // private
30976     onLoad : function(ds, r, o){
30977        this.cursor = o.params ? o.params.start : 0;
30978        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
30979
30980        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
30981        this.field.dom.value = ap;
30982        this.first.setDisabled(ap == 1);
30983        this.prev.setDisabled(ap == 1);
30984        this.next.setDisabled(ap == ps);
30985        this.last.setDisabled(ap == ps);
30986        this.loading.enable();
30987        this.updateInfo();
30988     },
30989
30990     // private
30991     getPageData : function(){
30992         var total = this.ds.getTotalCount();
30993         return {
30994             total : total,
30995             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
30996             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
30997         };
30998     },
30999
31000     // private
31001     onLoadError : function(){
31002         this.loading.enable();
31003     },
31004
31005     // private
31006     onPagingKeydown : function(e){
31007         var k = e.getKey();
31008         var d = this.getPageData();
31009         if(k == e.RETURN){
31010             var v = this.field.dom.value, pageNum;
31011             if(!v || isNaN(pageNum = parseInt(v, 10))){
31012                 this.field.dom.value = d.activePage;
31013                 return;
31014             }
31015             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31016             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31017             e.stopEvent();
31018         }
31019         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))
31020         {
31021           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31022           this.field.dom.value = pageNum;
31023           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31024           e.stopEvent();
31025         }
31026         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31027         {
31028           var v = this.field.dom.value, pageNum; 
31029           var increment = (e.shiftKey) ? 10 : 1;
31030           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31031             increment *= -1;
31032           }
31033           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31034             this.field.dom.value = d.activePage;
31035             return;
31036           }
31037           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31038           {
31039             this.field.dom.value = parseInt(v, 10) + increment;
31040             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31041             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31042           }
31043           e.stopEvent();
31044         }
31045     },
31046
31047     // private
31048     beforeLoad : function(){
31049         if(this.loading){
31050             this.loading.disable();
31051         }
31052     },
31053
31054     // private
31055     onClick : function(which){
31056         var ds = this.ds;
31057         switch(which){
31058             case "first":
31059                 ds.load({params:{start: 0, limit: this.pageSize}});
31060             break;
31061             case "prev":
31062                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31063             break;
31064             case "next":
31065                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31066             break;
31067             case "last":
31068                 var total = ds.getTotalCount();
31069                 var extra = total % this.pageSize;
31070                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31071                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31072             break;
31073             case "refresh":
31074                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31075             break;
31076         }
31077     },
31078
31079     /**
31080      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31081      * @param {Roo.data.Store} store The data store to unbind
31082      */
31083     unbind : function(ds){
31084         ds.un("beforeload", this.beforeLoad, this);
31085         ds.un("load", this.onLoad, this);
31086         ds.un("loadexception", this.onLoadError, this);
31087         ds.un("remove", this.updateInfo, this);
31088         ds.un("add", this.updateInfo, this);
31089         this.ds = undefined;
31090     },
31091
31092     /**
31093      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31094      * @param {Roo.data.Store} store The data store to bind
31095      */
31096     bind : function(ds){
31097         ds.on("beforeload", this.beforeLoad, this);
31098         ds.on("load", this.onLoad, this);
31099         ds.on("loadexception", this.onLoadError, this);
31100         ds.on("remove", this.updateInfo, this);
31101         ds.on("add", this.updateInfo, this);
31102         this.ds = ds;
31103     }
31104 });/*
31105  * Based on:
31106  * Ext JS Library 1.1.1
31107  * Copyright(c) 2006-2007, Ext JS, LLC.
31108  *
31109  * Originally Released Under LGPL - original licence link has changed is not relivant.
31110  *
31111  * Fork - LGPL
31112  * <script type="text/javascript">
31113  */
31114
31115 /**
31116  * @class Roo.Resizable
31117  * @extends Roo.util.Observable
31118  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31119  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31120  * 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
31121  * the element will be wrapped for you automatically.</p>
31122  * <p>Here is the list of valid resize handles:</p>
31123  * <pre>
31124 Value   Description
31125 ------  -------------------
31126  'n'     north
31127  's'     south
31128  'e'     east
31129  'w'     west
31130  'nw'    northwest
31131  'sw'    southwest
31132  'se'    southeast
31133  'ne'    northeast
31134  'hd'    horizontal drag
31135  'all'   all
31136 </pre>
31137  * <p>Here's an example showing the creation of a typical Resizable:</p>
31138  * <pre><code>
31139 var resizer = new Roo.Resizable("element-id", {
31140     handles: 'all',
31141     minWidth: 200,
31142     minHeight: 100,
31143     maxWidth: 500,
31144     maxHeight: 400,
31145     pinned: true
31146 });
31147 resizer.on("resize", myHandler);
31148 </code></pre>
31149  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31150  * resizer.east.setDisplayed(false);</p>
31151  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31152  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31153  * resize operation's new size (defaults to [0, 0])
31154  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31155  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31156  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31157  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31158  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31159  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31160  * @cfg {Number} width The width of the element in pixels (defaults to null)
31161  * @cfg {Number} height The height of the element in pixels (defaults to null)
31162  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31163  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31164  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31165  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31166  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31167  * in favor of the handles config option (defaults to false)
31168  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31169  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31170  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31171  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31172  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31173  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31174  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31175  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31176  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31177  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31178  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31179  * @constructor
31180  * Create a new resizable component
31181  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31182  * @param {Object} config configuration options
31183   */
31184 Roo.Resizable = function(el, config)
31185 {
31186     this.el = Roo.get(el);
31187
31188     if(config && config.wrap){
31189         config.resizeChild = this.el;
31190         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31191         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31192         this.el.setStyle("overflow", "hidden");
31193         this.el.setPositioning(config.resizeChild.getPositioning());
31194         config.resizeChild.clearPositioning();
31195         if(!config.width || !config.height){
31196             var csize = config.resizeChild.getSize();
31197             this.el.setSize(csize.width, csize.height);
31198         }
31199         if(config.pinned && !config.adjustments){
31200             config.adjustments = "auto";
31201         }
31202     }
31203
31204     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31205     this.proxy.unselectable();
31206     this.proxy.enableDisplayMode('block');
31207
31208     Roo.apply(this, config);
31209
31210     if(this.pinned){
31211         this.disableTrackOver = true;
31212         this.el.addClass("x-resizable-pinned");
31213     }
31214     // if the element isn't positioned, make it relative
31215     var position = this.el.getStyle("position");
31216     if(position != "absolute" && position != "fixed"){
31217         this.el.setStyle("position", "relative");
31218     }
31219     if(!this.handles){ // no handles passed, must be legacy style
31220         this.handles = 's,e,se';
31221         if(this.multiDirectional){
31222             this.handles += ',n,w';
31223         }
31224     }
31225     if(this.handles == "all"){
31226         this.handles = "n s e w ne nw se sw";
31227     }
31228     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31229     var ps = Roo.Resizable.positions;
31230     for(var i = 0, len = hs.length; i < len; i++){
31231         if(hs[i] && ps[hs[i]]){
31232             var pos = ps[hs[i]];
31233             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31234         }
31235     }
31236     // legacy
31237     this.corner = this.southeast;
31238     
31239     // updateBox = the box can move..
31240     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31241         this.updateBox = true;
31242     }
31243
31244     this.activeHandle = null;
31245
31246     if(this.resizeChild){
31247         if(typeof this.resizeChild == "boolean"){
31248             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31249         }else{
31250             this.resizeChild = Roo.get(this.resizeChild, true);
31251         }
31252     }
31253     
31254     if(this.adjustments == "auto"){
31255         var rc = this.resizeChild;
31256         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
31257         if(rc && (hw || hn)){
31258             rc.position("relative");
31259             rc.setLeft(hw ? hw.el.getWidth() : 0);
31260             rc.setTop(hn ? hn.el.getHeight() : 0);
31261         }
31262         this.adjustments = [
31263             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
31264             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
31265         ];
31266     }
31267
31268     if(this.draggable){
31269         this.dd = this.dynamic ?
31270             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
31271         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
31272     }
31273
31274     // public events
31275     this.addEvents({
31276         /**
31277          * @event beforeresize
31278          * Fired before resize is allowed. Set enabled to false to cancel resize.
31279          * @param {Roo.Resizable} this
31280          * @param {Roo.EventObject} e The mousedown event
31281          */
31282         "beforeresize" : true,
31283         /**
31284          * @event resizing
31285          * Fired a resizing.
31286          * @param {Roo.Resizable} this
31287          * @param {Number} x The new x position
31288          * @param {Number} y The new y position
31289          * @param {Number} w The new w width
31290          * @param {Number} h The new h hight
31291          * @param {Roo.EventObject} e The mouseup event
31292          */
31293         "resizing" : true,
31294         /**
31295          * @event resize
31296          * Fired after a resize.
31297          * @param {Roo.Resizable} this
31298          * @param {Number} width The new width
31299          * @param {Number} height The new height
31300          * @param {Roo.EventObject} e The mouseup event
31301          */
31302         "resize" : true
31303     });
31304
31305     if(this.width !== null && this.height !== null){
31306         this.resizeTo(this.width, this.height);
31307     }else{
31308         this.updateChildSize();
31309     }
31310     if(Roo.isIE){
31311         this.el.dom.style.zoom = 1;
31312     }
31313     Roo.Resizable.superclass.constructor.call(this);
31314 };
31315
31316 Roo.extend(Roo.Resizable, Roo.util.Observable, {
31317         resizeChild : false,
31318         adjustments : [0, 0],
31319         minWidth : 5,
31320         minHeight : 5,
31321         maxWidth : 10000,
31322         maxHeight : 10000,
31323         enabled : true,
31324         animate : false,
31325         duration : .35,
31326         dynamic : false,
31327         handles : false,
31328         multiDirectional : false,
31329         disableTrackOver : false,
31330         easing : 'easeOutStrong',
31331         widthIncrement : 0,
31332         heightIncrement : 0,
31333         pinned : false,
31334         width : null,
31335         height : null,
31336         preserveRatio : false,
31337         transparent: false,
31338         minX: 0,
31339         minY: 0,
31340         draggable: false,
31341
31342         /**
31343          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
31344          */
31345         constrainTo: undefined,
31346         /**
31347          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
31348          */
31349         resizeRegion: undefined,
31350
31351
31352     /**
31353      * Perform a manual resize
31354      * @param {Number} width
31355      * @param {Number} height
31356      */
31357     resizeTo : function(width, height){
31358         this.el.setSize(width, height);
31359         this.updateChildSize();
31360         this.fireEvent("resize", this, width, height, null);
31361     },
31362
31363     // private
31364     startSizing : function(e, handle){
31365         this.fireEvent("beforeresize", this, e);
31366         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
31367
31368             if(!this.overlay){
31369                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
31370                 this.overlay.unselectable();
31371                 this.overlay.enableDisplayMode("block");
31372                 this.overlay.on("mousemove", this.onMouseMove, this);
31373                 this.overlay.on("mouseup", this.onMouseUp, this);
31374             }
31375             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
31376
31377             this.resizing = true;
31378             this.startBox = this.el.getBox();
31379             this.startPoint = e.getXY();
31380             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
31381                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
31382
31383             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
31384             this.overlay.show();
31385
31386             if(this.constrainTo) {
31387                 var ct = Roo.get(this.constrainTo);
31388                 this.resizeRegion = ct.getRegion().adjust(
31389                     ct.getFrameWidth('t'),
31390                     ct.getFrameWidth('l'),
31391                     -ct.getFrameWidth('b'),
31392                     -ct.getFrameWidth('r')
31393                 );
31394             }
31395
31396             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
31397             this.proxy.show();
31398             this.proxy.setBox(this.startBox);
31399             if(!this.dynamic){
31400                 this.proxy.setStyle('visibility', 'visible');
31401             }
31402         }
31403     },
31404
31405     // private
31406     onMouseDown : function(handle, e){
31407         if(this.enabled){
31408             e.stopEvent();
31409             this.activeHandle = handle;
31410             this.startSizing(e, handle);
31411         }
31412     },
31413
31414     // private
31415     onMouseUp : function(e){
31416         var size = this.resizeElement();
31417         this.resizing = false;
31418         this.handleOut();
31419         this.overlay.hide();
31420         this.proxy.hide();
31421         this.fireEvent("resize", this, size.width, size.height, e);
31422     },
31423
31424     // private
31425     updateChildSize : function(){
31426         
31427         if(this.resizeChild){
31428             var el = this.el;
31429             var child = this.resizeChild;
31430             var adj = this.adjustments;
31431             if(el.dom.offsetWidth){
31432                 var b = el.getSize(true);
31433                 child.setSize(b.width+adj[0], b.height+adj[1]);
31434             }
31435             // Second call here for IE
31436             // The first call enables instant resizing and
31437             // the second call corrects scroll bars if they
31438             // exist
31439             if(Roo.isIE){
31440                 setTimeout(function(){
31441                     if(el.dom.offsetWidth){
31442                         var b = el.getSize(true);
31443                         child.setSize(b.width+adj[0], b.height+adj[1]);
31444                     }
31445                 }, 10);
31446             }
31447         }
31448     },
31449
31450     // private
31451     snap : function(value, inc, min){
31452         if(!inc || !value) {
31453             return value;
31454         }
31455         var newValue = value;
31456         var m = value % inc;
31457         if(m > 0){
31458             if(m > (inc/2)){
31459                 newValue = value + (inc-m);
31460             }else{
31461                 newValue = value - m;
31462             }
31463         }
31464         return Math.max(min, newValue);
31465     },
31466
31467     // private
31468     resizeElement : function(){
31469         var box = this.proxy.getBox();
31470         if(this.updateBox){
31471             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
31472         }else{
31473             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
31474         }
31475         this.updateChildSize();
31476         if(!this.dynamic){
31477             this.proxy.hide();
31478         }
31479         return box;
31480     },
31481
31482     // private
31483     constrain : function(v, diff, m, mx){
31484         if(v - diff < m){
31485             diff = v - m;
31486         }else if(v - diff > mx){
31487             diff = mx - v;
31488         }
31489         return diff;
31490     },
31491
31492     // private
31493     onMouseMove : function(e){
31494         
31495         if(this.enabled){
31496             try{// try catch so if something goes wrong the user doesn't get hung
31497
31498             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
31499                 return;
31500             }
31501
31502             //var curXY = this.startPoint;
31503             var curSize = this.curSize || this.startBox;
31504             var x = this.startBox.x, y = this.startBox.y;
31505             var ox = x, oy = y;
31506             var w = curSize.width, h = curSize.height;
31507             var ow = w, oh = h;
31508             var mw = this.minWidth, mh = this.minHeight;
31509             var mxw = this.maxWidth, mxh = this.maxHeight;
31510             var wi = this.widthIncrement;
31511             var hi = this.heightIncrement;
31512
31513             var eventXY = e.getXY();
31514             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
31515             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
31516
31517             var pos = this.activeHandle.position;
31518
31519             switch(pos){
31520                 case "east":
31521                     w += diffX;
31522                     w = Math.min(Math.max(mw, w), mxw);
31523                     break;
31524              
31525                 case "south":
31526                     h += diffY;
31527                     h = Math.min(Math.max(mh, h), mxh);
31528                     break;
31529                 case "southeast":
31530                     w += diffX;
31531                     h += diffY;
31532                     w = Math.min(Math.max(mw, w), mxw);
31533                     h = Math.min(Math.max(mh, h), mxh);
31534                     break;
31535                 case "north":
31536                     diffY = this.constrain(h, diffY, mh, mxh);
31537                     y += diffY;
31538                     h -= diffY;
31539                     break;
31540                 case "hdrag":
31541                     
31542                     if (wi) {
31543                         var adiffX = Math.abs(diffX);
31544                         var sub = (adiffX % wi); // how much 
31545                         if (sub > (wi/2)) { // far enough to snap
31546                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
31547                         } else {
31548                             // remove difference.. 
31549                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
31550                         }
31551                     }
31552                     x += diffX;
31553                     x = Math.max(this.minX, x);
31554                     break;
31555                 case "west":
31556                     diffX = this.constrain(w, diffX, mw, mxw);
31557                     x += diffX;
31558                     w -= diffX;
31559                     break;
31560                 case "northeast":
31561                     w += diffX;
31562                     w = Math.min(Math.max(mw, w), mxw);
31563                     diffY = this.constrain(h, diffY, mh, mxh);
31564                     y += diffY;
31565                     h -= diffY;
31566                     break;
31567                 case "northwest":
31568                     diffX = this.constrain(w, diffX, mw, mxw);
31569                     diffY = this.constrain(h, diffY, mh, mxh);
31570                     y += diffY;
31571                     h -= diffY;
31572                     x += diffX;
31573                     w -= diffX;
31574                     break;
31575                case "southwest":
31576                     diffX = this.constrain(w, diffX, mw, mxw);
31577                     h += diffY;
31578                     h = Math.min(Math.max(mh, h), mxh);
31579                     x += diffX;
31580                     w -= diffX;
31581                     break;
31582             }
31583
31584             var sw = this.snap(w, wi, mw);
31585             var sh = this.snap(h, hi, mh);
31586             if(sw != w || sh != h){
31587                 switch(pos){
31588                     case "northeast":
31589                         y -= sh - h;
31590                     break;
31591                     case "north":
31592                         y -= sh - h;
31593                         break;
31594                     case "southwest":
31595                         x -= sw - w;
31596                     break;
31597                     case "west":
31598                         x -= sw - w;
31599                         break;
31600                     case "northwest":
31601                         x -= sw - w;
31602                         y -= sh - h;
31603                     break;
31604                 }
31605                 w = sw;
31606                 h = sh;
31607             }
31608
31609             if(this.preserveRatio){
31610                 switch(pos){
31611                     case "southeast":
31612                     case "east":
31613                         h = oh * (w/ow);
31614                         h = Math.min(Math.max(mh, h), mxh);
31615                         w = ow * (h/oh);
31616                        break;
31617                     case "south":
31618                         w = ow * (h/oh);
31619                         w = Math.min(Math.max(mw, w), mxw);
31620                         h = oh * (w/ow);
31621                         break;
31622                     case "northeast":
31623                         w = ow * (h/oh);
31624                         w = Math.min(Math.max(mw, w), mxw);
31625                         h = oh * (w/ow);
31626                     break;
31627                     case "north":
31628                         var tw = w;
31629                         w = ow * (h/oh);
31630                         w = Math.min(Math.max(mw, w), mxw);
31631                         h = oh * (w/ow);
31632                         x += (tw - w) / 2;
31633                         break;
31634                     case "southwest":
31635                         h = oh * (w/ow);
31636                         h = Math.min(Math.max(mh, h), mxh);
31637                         var tw = w;
31638                         w = ow * (h/oh);
31639                         x += tw - w;
31640                         break;
31641                     case "west":
31642                         var th = h;
31643                         h = oh * (w/ow);
31644                         h = Math.min(Math.max(mh, h), mxh);
31645                         y += (th - h) / 2;
31646                         var tw = w;
31647                         w = ow * (h/oh);
31648                         x += tw - w;
31649                        break;
31650                     case "northwest":
31651                         var tw = w;
31652                         var th = h;
31653                         h = oh * (w/ow);
31654                         h = Math.min(Math.max(mh, h), mxh);
31655                         w = ow * (h/oh);
31656                         y += th - h;
31657                         x += tw - w;
31658                        break;
31659
31660                 }
31661             }
31662             if (pos == 'hdrag') {
31663                 w = ow;
31664             }
31665             this.proxy.setBounds(x, y, w, h);
31666             if(this.dynamic){
31667                 this.resizeElement();
31668             }
31669             }catch(e){}
31670         }
31671         this.fireEvent("resizing", this, x, y, w, h, e);
31672     },
31673
31674     // private
31675     handleOver : function(){
31676         if(this.enabled){
31677             this.el.addClass("x-resizable-over");
31678         }
31679     },
31680
31681     // private
31682     handleOut : function(){
31683         if(!this.resizing){
31684             this.el.removeClass("x-resizable-over");
31685         }
31686     },
31687
31688     /**
31689      * Returns the element this component is bound to.
31690      * @return {Roo.Element}
31691      */
31692     getEl : function(){
31693         return this.el;
31694     },
31695
31696     /**
31697      * Returns the resizeChild element (or null).
31698      * @return {Roo.Element}
31699      */
31700     getResizeChild : function(){
31701         return this.resizeChild;
31702     },
31703     groupHandler : function()
31704     {
31705         
31706     },
31707     /**
31708      * Destroys this resizable. If the element was wrapped and
31709      * removeEl is not true then the element remains.
31710      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
31711      */
31712     destroy : function(removeEl){
31713         this.proxy.remove();
31714         if(this.overlay){
31715             this.overlay.removeAllListeners();
31716             this.overlay.remove();
31717         }
31718         var ps = Roo.Resizable.positions;
31719         for(var k in ps){
31720             if(typeof ps[k] != "function" && this[ps[k]]){
31721                 var h = this[ps[k]];
31722                 h.el.removeAllListeners();
31723                 h.el.remove();
31724             }
31725         }
31726         if(removeEl){
31727             this.el.update("");
31728             this.el.remove();
31729         }
31730     }
31731 });
31732
31733 // private
31734 // hash to map config positions to true positions
31735 Roo.Resizable.positions = {
31736     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
31737     hd: "hdrag"
31738 };
31739
31740 // private
31741 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
31742     if(!this.tpl){
31743         // only initialize the template if resizable is used
31744         var tpl = Roo.DomHelper.createTemplate(
31745             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
31746         );
31747         tpl.compile();
31748         Roo.Resizable.Handle.prototype.tpl = tpl;
31749     }
31750     this.position = pos;
31751     this.rz = rz;
31752     // show north drag fro topdra
31753     var handlepos = pos == 'hdrag' ? 'north' : pos;
31754     
31755     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
31756     if (pos == 'hdrag') {
31757         this.el.setStyle('cursor', 'pointer');
31758     }
31759     this.el.unselectable();
31760     if(transparent){
31761         this.el.setOpacity(0);
31762     }
31763     this.el.on("mousedown", this.onMouseDown, this);
31764     if(!disableTrackOver){
31765         this.el.on("mouseover", this.onMouseOver, this);
31766         this.el.on("mouseout", this.onMouseOut, this);
31767     }
31768 };
31769
31770 // private
31771 Roo.Resizable.Handle.prototype = {
31772     afterResize : function(rz){
31773         Roo.log('after?');
31774         // do nothing
31775     },
31776     // private
31777     onMouseDown : function(e){
31778         this.rz.onMouseDown(this, e);
31779     },
31780     // private
31781     onMouseOver : function(e){
31782         this.rz.handleOver(this, e);
31783     },
31784     // private
31785     onMouseOut : function(e){
31786         this.rz.handleOut(this, e);
31787     }
31788 };/*
31789  * Based on:
31790  * Ext JS Library 1.1.1
31791  * Copyright(c) 2006-2007, Ext JS, LLC.
31792  *
31793  * Originally Released Under LGPL - original licence link has changed is not relivant.
31794  *
31795  * Fork - LGPL
31796  * <script type="text/javascript">
31797  */
31798
31799 /**
31800  * @class Roo.Editor
31801  * @extends Roo.Component
31802  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
31803  * @constructor
31804  * Create a new Editor
31805  * @param {Roo.form.Field} field The Field object (or descendant)
31806  * @param {Object} config The config object
31807  */
31808 Roo.Editor = function(field, config){
31809     Roo.Editor.superclass.constructor.call(this, config);
31810     this.field = field;
31811     this.addEvents({
31812         /**
31813              * @event beforestartedit
31814              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
31815              * false from the handler of this event.
31816              * @param {Editor} this
31817              * @param {Roo.Element} boundEl The underlying element bound to this editor
31818              * @param {Mixed} value The field value being set
31819              */
31820         "beforestartedit" : true,
31821         /**
31822              * @event startedit
31823              * Fires when this editor is displayed
31824              * @param {Roo.Element} boundEl The underlying element bound to this editor
31825              * @param {Mixed} value The starting field value
31826              */
31827         "startedit" : true,
31828         /**
31829              * @event beforecomplete
31830              * Fires after a change has been made to the field, but before the change is reflected in the underlying
31831              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
31832              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
31833              * event will not fire since no edit actually occurred.
31834              * @param {Editor} this
31835              * @param {Mixed} value The current field value
31836              * @param {Mixed} startValue The original field value
31837              */
31838         "beforecomplete" : true,
31839         /**
31840              * @event complete
31841              * Fires after editing is complete and any changed value has been written to the underlying field.
31842              * @param {Editor} this
31843              * @param {Mixed} value The current field value
31844              * @param {Mixed} startValue The original field value
31845              */
31846         "complete" : true,
31847         /**
31848          * @event specialkey
31849          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
31850          * {@link Roo.EventObject#getKey} to determine which key was pressed.
31851          * @param {Roo.form.Field} this
31852          * @param {Roo.EventObject} e The event object
31853          */
31854         "specialkey" : true
31855     });
31856 };
31857
31858 Roo.extend(Roo.Editor, Roo.Component, {
31859     /**
31860      * @cfg {Boolean/String} autosize
31861      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
31862      * or "height" to adopt the height only (defaults to false)
31863      */
31864     /**
31865      * @cfg {Boolean} revertInvalid
31866      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
31867      * validation fails (defaults to true)
31868      */
31869     /**
31870      * @cfg {Boolean} ignoreNoChange
31871      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
31872      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
31873      * will never be ignored.
31874      */
31875     /**
31876      * @cfg {Boolean} hideEl
31877      * False to keep the bound element visible while the editor is displayed (defaults to true)
31878      */
31879     /**
31880      * @cfg {Mixed} value
31881      * The data value of the underlying field (defaults to "")
31882      */
31883     value : "",
31884     /**
31885      * @cfg {String} alignment
31886      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
31887      */
31888     alignment: "c-c?",
31889     /**
31890      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
31891      * for bottom-right shadow (defaults to "frame")
31892      */
31893     shadow : "frame",
31894     /**
31895      * @cfg {Boolean} constrain True to constrain the editor to the viewport
31896      */
31897     constrain : false,
31898     /**
31899      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
31900      */
31901     completeOnEnter : false,
31902     /**
31903      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
31904      */
31905     cancelOnEsc : false,
31906     /**
31907      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
31908      */
31909     updateEl : false,
31910
31911     // private
31912     onRender : function(ct, position){
31913         this.el = new Roo.Layer({
31914             shadow: this.shadow,
31915             cls: "x-editor",
31916             parentEl : ct,
31917             shim : this.shim,
31918             shadowOffset:4,
31919             id: this.id,
31920             constrain: this.constrain
31921         });
31922         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
31923         if(this.field.msgTarget != 'title'){
31924             this.field.msgTarget = 'qtip';
31925         }
31926         this.field.render(this.el);
31927         if(Roo.isGecko){
31928             this.field.el.dom.setAttribute('autocomplete', 'off');
31929         }
31930         this.field.on("specialkey", this.onSpecialKey, this);
31931         if(this.swallowKeys){
31932             this.field.el.swallowEvent(['keydown','keypress']);
31933         }
31934         this.field.show();
31935         this.field.on("blur", this.onBlur, this);
31936         if(this.field.grow){
31937             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
31938         }
31939     },
31940
31941     onSpecialKey : function(field, e)
31942     {
31943         //Roo.log('editor onSpecialKey');
31944         if(this.completeOnEnter && e.getKey() == e.ENTER){
31945             e.stopEvent();
31946             this.completeEdit();
31947             return;
31948         }
31949         // do not fire special key otherwise it might hide close the editor...
31950         if(e.getKey() == e.ENTER){    
31951             return;
31952         }
31953         if(this.cancelOnEsc && e.getKey() == e.ESC){
31954             this.cancelEdit();
31955             return;
31956         } 
31957         this.fireEvent('specialkey', field, e);
31958     
31959     },
31960
31961     /**
31962      * Starts the editing process and shows the editor.
31963      * @param {String/HTMLElement/Element} el The element to edit
31964      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
31965       * to the innerHTML of el.
31966      */
31967     startEdit : function(el, value){
31968         if(this.editing){
31969             this.completeEdit();
31970         }
31971         this.boundEl = Roo.get(el);
31972         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
31973         if(!this.rendered){
31974             this.render(this.parentEl || document.body);
31975         }
31976         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
31977             return;
31978         }
31979         this.startValue = v;
31980         this.field.setValue(v);
31981         if(this.autoSize){
31982             var sz = this.boundEl.getSize();
31983             switch(this.autoSize){
31984                 case "width":
31985                 this.setSize(sz.width,  "");
31986                 break;
31987                 case "height":
31988                 this.setSize("",  sz.height);
31989                 break;
31990                 default:
31991                 this.setSize(sz.width,  sz.height);
31992             }
31993         }
31994         this.el.alignTo(this.boundEl, this.alignment);
31995         this.editing = true;
31996         if(Roo.QuickTips){
31997             Roo.QuickTips.disable();
31998         }
31999         this.show();
32000     },
32001
32002     /**
32003      * Sets the height and width of this editor.
32004      * @param {Number} width The new width
32005      * @param {Number} height The new height
32006      */
32007     setSize : function(w, h){
32008         this.field.setSize(w, h);
32009         if(this.el){
32010             this.el.sync();
32011         }
32012     },
32013
32014     /**
32015      * Realigns the editor to the bound field based on the current alignment config value.
32016      */
32017     realign : function(){
32018         this.el.alignTo(this.boundEl, this.alignment);
32019     },
32020
32021     /**
32022      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32023      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32024      */
32025     completeEdit : function(remainVisible){
32026         if(!this.editing){
32027             return;
32028         }
32029         var v = this.getValue();
32030         if(this.revertInvalid !== false && !this.field.isValid()){
32031             v = this.startValue;
32032             this.cancelEdit(true);
32033         }
32034         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32035             this.editing = false;
32036             this.hide();
32037             return;
32038         }
32039         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32040             this.editing = false;
32041             if(this.updateEl && this.boundEl){
32042                 this.boundEl.update(v);
32043             }
32044             if(remainVisible !== true){
32045                 this.hide();
32046             }
32047             this.fireEvent("complete", this, v, this.startValue);
32048         }
32049     },
32050
32051     // private
32052     onShow : function(){
32053         this.el.show();
32054         if(this.hideEl !== false){
32055             this.boundEl.hide();
32056         }
32057         this.field.show();
32058         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32059             this.fixIEFocus = true;
32060             this.deferredFocus.defer(50, this);
32061         }else{
32062             this.field.focus();
32063         }
32064         this.fireEvent("startedit", this.boundEl, this.startValue);
32065     },
32066
32067     deferredFocus : function(){
32068         if(this.editing){
32069             this.field.focus();
32070         }
32071     },
32072
32073     /**
32074      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32075      * reverted to the original starting value.
32076      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32077      * cancel (defaults to false)
32078      */
32079     cancelEdit : function(remainVisible){
32080         if(this.editing){
32081             this.setValue(this.startValue);
32082             if(remainVisible !== true){
32083                 this.hide();
32084             }
32085         }
32086     },
32087
32088     // private
32089     onBlur : function(){
32090         if(this.allowBlur !== true && this.editing){
32091             this.completeEdit();
32092         }
32093     },
32094
32095     // private
32096     onHide : function(){
32097         if(this.editing){
32098             this.completeEdit();
32099             return;
32100         }
32101         this.field.blur();
32102         if(this.field.collapse){
32103             this.field.collapse();
32104         }
32105         this.el.hide();
32106         if(this.hideEl !== false){
32107             this.boundEl.show();
32108         }
32109         if(Roo.QuickTips){
32110             Roo.QuickTips.enable();
32111         }
32112     },
32113
32114     /**
32115      * Sets the data value of the editor
32116      * @param {Mixed} value Any valid value supported by the underlying field
32117      */
32118     setValue : function(v){
32119         this.field.setValue(v);
32120     },
32121
32122     /**
32123      * Gets the data value of the editor
32124      * @return {Mixed} The data value
32125      */
32126     getValue : function(){
32127         return this.field.getValue();
32128     }
32129 });/*
32130  * Based on:
32131  * Ext JS Library 1.1.1
32132  * Copyright(c) 2006-2007, Ext JS, LLC.
32133  *
32134  * Originally Released Under LGPL - original licence link has changed is not relivant.
32135  *
32136  * Fork - LGPL
32137  * <script type="text/javascript">
32138  */
32139  
32140 /**
32141  * @class Roo.BasicDialog
32142  * @extends Roo.util.Observable
32143  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32144  * <pre><code>
32145 var dlg = new Roo.BasicDialog("my-dlg", {
32146     height: 200,
32147     width: 300,
32148     minHeight: 100,
32149     minWidth: 150,
32150     modal: true,
32151     proxyDrag: true,
32152     shadow: true
32153 });
32154 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32155 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32156 dlg.addButton('Cancel', dlg.hide, dlg);
32157 dlg.show();
32158 </code></pre>
32159   <b>A Dialog should always be a direct child of the body element.</b>
32160  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32161  * @cfg {String} title Default text to display in the title bar (defaults to null)
32162  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32163  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32164  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32165  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32166  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32167  * (defaults to null with no animation)
32168  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32169  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32170  * property for valid values (defaults to 'all')
32171  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32172  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32173  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32174  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32175  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32176  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32177  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32178  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32179  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32180  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32181  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32182  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32183  * draggable = true (defaults to false)
32184  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32185  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32186  * shadow (defaults to false)
32187  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32188  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32189  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32190  * @cfg {Array} buttons Array of buttons
32191  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32192  * @constructor
32193  * Create a new BasicDialog.
32194  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32195  * @param {Object} config Configuration options
32196  */
32197 Roo.BasicDialog = function(el, config){
32198     this.el = Roo.get(el);
32199     var dh = Roo.DomHelper;
32200     if(!this.el && config && config.autoCreate){
32201         if(typeof config.autoCreate == "object"){
32202             if(!config.autoCreate.id){
32203                 config.autoCreate.id = el;
32204             }
32205             this.el = dh.append(document.body,
32206                         config.autoCreate, true);
32207         }else{
32208             this.el = dh.append(document.body,
32209                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32210         }
32211     }
32212     el = this.el;
32213     el.setDisplayed(true);
32214     el.hide = this.hideAction;
32215     this.id = el.id;
32216     el.addClass("x-dlg");
32217
32218     Roo.apply(this, config);
32219
32220     this.proxy = el.createProxy("x-dlg-proxy");
32221     this.proxy.hide = this.hideAction;
32222     this.proxy.setOpacity(.5);
32223     this.proxy.hide();
32224
32225     if(config.width){
32226         el.setWidth(config.width);
32227     }
32228     if(config.height){
32229         el.setHeight(config.height);
32230     }
32231     this.size = el.getSize();
32232     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32233         this.xy = [config.x,config.y];
32234     }else{
32235         this.xy = el.getCenterXY(true);
32236     }
32237     /** The header element @type Roo.Element */
32238     this.header = el.child("> .x-dlg-hd");
32239     /** The body element @type Roo.Element */
32240     this.body = el.child("> .x-dlg-bd");
32241     /** The footer element @type Roo.Element */
32242     this.footer = el.child("> .x-dlg-ft");
32243
32244     if(!this.header){
32245         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32246     }
32247     if(!this.body){
32248         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32249     }
32250
32251     this.header.unselectable();
32252     if(this.title){
32253         this.header.update(this.title);
32254     }
32255     // this element allows the dialog to be focused for keyboard event
32256     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
32257     this.focusEl.swallowEvent("click", true);
32258
32259     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
32260
32261     // wrap the body and footer for special rendering
32262     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
32263     if(this.footer){
32264         this.bwrap.dom.appendChild(this.footer.dom);
32265     }
32266
32267     this.bg = this.el.createChild({
32268         tag: "div", cls:"x-dlg-bg",
32269         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
32270     });
32271     this.centerBg = this.bg.child("div.x-dlg-bg-center");
32272
32273
32274     if(this.autoScroll !== false && !this.autoTabs){
32275         this.body.setStyle("overflow", "auto");
32276     }
32277
32278     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
32279
32280     if(this.closable !== false){
32281         this.el.addClass("x-dlg-closable");
32282         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
32283         this.close.on("click", this.closeClick, this);
32284         this.close.addClassOnOver("x-dlg-close-over");
32285     }
32286     if(this.collapsible !== false){
32287         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
32288         this.collapseBtn.on("click", this.collapseClick, this);
32289         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
32290         this.header.on("dblclick", this.collapseClick, this);
32291     }
32292     if(this.resizable !== false){
32293         this.el.addClass("x-dlg-resizable");
32294         this.resizer = new Roo.Resizable(el, {
32295             minWidth: this.minWidth || 80,
32296             minHeight:this.minHeight || 80,
32297             handles: this.resizeHandles || "all",
32298             pinned: true
32299         });
32300         this.resizer.on("beforeresize", this.beforeResize, this);
32301         this.resizer.on("resize", this.onResize, this);
32302     }
32303     if(this.draggable !== false){
32304         el.addClass("x-dlg-draggable");
32305         if (!this.proxyDrag) {
32306             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
32307         }
32308         else {
32309             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
32310         }
32311         dd.setHandleElId(this.header.id);
32312         dd.endDrag = this.endMove.createDelegate(this);
32313         dd.startDrag = this.startMove.createDelegate(this);
32314         dd.onDrag = this.onDrag.createDelegate(this);
32315         dd.scroll = false;
32316         this.dd = dd;
32317     }
32318     if(this.modal){
32319         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
32320         this.mask.enableDisplayMode("block");
32321         this.mask.hide();
32322         this.el.addClass("x-dlg-modal");
32323     }
32324     if(this.shadow){
32325         this.shadow = new Roo.Shadow({
32326             mode : typeof this.shadow == "string" ? this.shadow : "sides",
32327             offset : this.shadowOffset
32328         });
32329     }else{
32330         this.shadowOffset = 0;
32331     }
32332     if(Roo.useShims && this.shim !== false){
32333         this.shim = this.el.createShim();
32334         this.shim.hide = this.hideAction;
32335         this.shim.hide();
32336     }else{
32337         this.shim = false;
32338     }
32339     if(this.autoTabs){
32340         this.initTabs();
32341     }
32342     if (this.buttons) { 
32343         var bts= this.buttons;
32344         this.buttons = [];
32345         Roo.each(bts, function(b) {
32346             this.addButton(b);
32347         }, this);
32348     }
32349     
32350     
32351     this.addEvents({
32352         /**
32353          * @event keydown
32354          * Fires when a key is pressed
32355          * @param {Roo.BasicDialog} this
32356          * @param {Roo.EventObject} e
32357          */
32358         "keydown" : true,
32359         /**
32360          * @event move
32361          * Fires when this dialog is moved by the user.
32362          * @param {Roo.BasicDialog} this
32363          * @param {Number} x The new page X
32364          * @param {Number} y The new page Y
32365          */
32366         "move" : true,
32367         /**
32368          * @event resize
32369          * Fires when this dialog is resized by the user.
32370          * @param {Roo.BasicDialog} this
32371          * @param {Number} width The new width
32372          * @param {Number} height The new height
32373          */
32374         "resize" : true,
32375         /**
32376          * @event beforehide
32377          * Fires before this dialog is hidden.
32378          * @param {Roo.BasicDialog} this
32379          */
32380         "beforehide" : true,
32381         /**
32382          * @event hide
32383          * Fires when this dialog is hidden.
32384          * @param {Roo.BasicDialog} this
32385          */
32386         "hide" : true,
32387         /**
32388          * @event beforeshow
32389          * Fires before this dialog is shown.
32390          * @param {Roo.BasicDialog} this
32391          */
32392         "beforeshow" : true,
32393         /**
32394          * @event show
32395          * Fires when this dialog is shown.
32396          * @param {Roo.BasicDialog} this
32397          */
32398         "show" : true
32399     });
32400     el.on("keydown", this.onKeyDown, this);
32401     el.on("mousedown", this.toFront, this);
32402     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
32403     this.el.hide();
32404     Roo.DialogManager.register(this);
32405     Roo.BasicDialog.superclass.constructor.call(this);
32406 };
32407
32408 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
32409     shadowOffset: Roo.isIE ? 6 : 5,
32410     minHeight: 80,
32411     minWidth: 200,
32412     minButtonWidth: 75,
32413     defaultButton: null,
32414     buttonAlign: "right",
32415     tabTag: 'div',
32416     firstShow: true,
32417
32418     /**
32419      * Sets the dialog title text
32420      * @param {String} text The title text to display
32421      * @return {Roo.BasicDialog} this
32422      */
32423     setTitle : function(text){
32424         this.header.update(text);
32425         return this;
32426     },
32427
32428     // private
32429     closeClick : function(){
32430         this.hide();
32431     },
32432
32433     // private
32434     collapseClick : function(){
32435         this[this.collapsed ? "expand" : "collapse"]();
32436     },
32437
32438     /**
32439      * Collapses the dialog to its minimized state (only the title bar is visible).
32440      * Equivalent to the user clicking the collapse dialog button.
32441      */
32442     collapse : function(){
32443         if(!this.collapsed){
32444             this.collapsed = true;
32445             this.el.addClass("x-dlg-collapsed");
32446             this.restoreHeight = this.el.getHeight();
32447             this.resizeTo(this.el.getWidth(), this.header.getHeight());
32448         }
32449     },
32450
32451     /**
32452      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
32453      * clicking the expand dialog button.
32454      */
32455     expand : function(){
32456         if(this.collapsed){
32457             this.collapsed = false;
32458             this.el.removeClass("x-dlg-collapsed");
32459             this.resizeTo(this.el.getWidth(), this.restoreHeight);
32460         }
32461     },
32462
32463     /**
32464      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
32465      * @return {Roo.TabPanel} The tabs component
32466      */
32467     initTabs : function(){
32468         var tabs = this.getTabs();
32469         while(tabs.getTab(0)){
32470             tabs.removeTab(0);
32471         }
32472         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
32473             var dom = el.dom;
32474             tabs.addTab(Roo.id(dom), dom.title);
32475             dom.title = "";
32476         });
32477         tabs.activate(0);
32478         return tabs;
32479     },
32480
32481     // private
32482     beforeResize : function(){
32483         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
32484     },
32485
32486     // private
32487     onResize : function(){
32488         this.refreshSize();
32489         this.syncBodyHeight();
32490         this.adjustAssets();
32491         this.focus();
32492         this.fireEvent("resize", this, this.size.width, this.size.height);
32493     },
32494
32495     // private
32496     onKeyDown : function(e){
32497         if(this.isVisible()){
32498             this.fireEvent("keydown", this, e);
32499         }
32500     },
32501
32502     /**
32503      * Resizes the dialog.
32504      * @param {Number} width
32505      * @param {Number} height
32506      * @return {Roo.BasicDialog} this
32507      */
32508     resizeTo : function(width, height){
32509         this.el.setSize(width, height);
32510         this.size = {width: width, height: height};
32511         this.syncBodyHeight();
32512         if(this.fixedcenter){
32513             this.center();
32514         }
32515         if(this.isVisible()){
32516             this.constrainXY();
32517             this.adjustAssets();
32518         }
32519         this.fireEvent("resize", this, width, height);
32520         return this;
32521     },
32522
32523
32524     /**
32525      * Resizes the dialog to fit the specified content size.
32526      * @param {Number} width
32527      * @param {Number} height
32528      * @return {Roo.BasicDialog} this
32529      */
32530     setContentSize : function(w, h){
32531         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
32532         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
32533         //if(!this.el.isBorderBox()){
32534             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
32535             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
32536         //}
32537         if(this.tabs){
32538             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
32539             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
32540         }
32541         this.resizeTo(w, h);
32542         return this;
32543     },
32544
32545     /**
32546      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
32547      * executed in response to a particular key being pressed while the dialog is active.
32548      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
32549      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
32550      * @param {Function} fn The function to call
32551      * @param {Object} scope (optional) The scope of the function
32552      * @return {Roo.BasicDialog} this
32553      */
32554     addKeyListener : function(key, fn, scope){
32555         var keyCode, shift, ctrl, alt;
32556         if(typeof key == "object" && !(key instanceof Array)){
32557             keyCode = key["key"];
32558             shift = key["shift"];
32559             ctrl = key["ctrl"];
32560             alt = key["alt"];
32561         }else{
32562             keyCode = key;
32563         }
32564         var handler = function(dlg, e){
32565             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
32566                 var k = e.getKey();
32567                 if(keyCode instanceof Array){
32568                     for(var i = 0, len = keyCode.length; i < len; i++){
32569                         if(keyCode[i] == k){
32570                           fn.call(scope || window, dlg, k, e);
32571                           return;
32572                         }
32573                     }
32574                 }else{
32575                     if(k == keyCode){
32576                         fn.call(scope || window, dlg, k, e);
32577                     }
32578                 }
32579             }
32580         };
32581         this.on("keydown", handler);
32582         return this;
32583     },
32584
32585     /**
32586      * Returns the TabPanel component (creates it if it doesn't exist).
32587      * Note: If you wish to simply check for the existence of tabs without creating them,
32588      * check for a null 'tabs' property.
32589      * @return {Roo.TabPanel} The tabs component
32590      */
32591     getTabs : function(){
32592         if(!this.tabs){
32593             this.el.addClass("x-dlg-auto-tabs");
32594             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
32595             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
32596         }
32597         return this.tabs;
32598     },
32599
32600     /**
32601      * Adds a button to the footer section of the dialog.
32602      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
32603      * object or a valid Roo.DomHelper element config
32604      * @param {Function} handler The function called when the button is clicked
32605      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
32606      * @return {Roo.Button} The new button
32607      */
32608     addButton : function(config, handler, scope){
32609         var dh = Roo.DomHelper;
32610         if(!this.footer){
32611             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
32612         }
32613         if(!this.btnContainer){
32614             var tb = this.footer.createChild({
32615
32616                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
32617                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
32618             }, null, true);
32619             this.btnContainer = tb.firstChild.firstChild.firstChild;
32620         }
32621         var bconfig = {
32622             handler: handler,
32623             scope: scope,
32624             minWidth: this.minButtonWidth,
32625             hideParent:true
32626         };
32627         if(typeof config == "string"){
32628             bconfig.text = config;
32629         }else{
32630             if(config.tag){
32631                 bconfig.dhconfig = config;
32632             }else{
32633                 Roo.apply(bconfig, config);
32634             }
32635         }
32636         var fc = false;
32637         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
32638             bconfig.position = Math.max(0, bconfig.position);
32639             fc = this.btnContainer.childNodes[bconfig.position];
32640         }
32641          
32642         var btn = new Roo.Button(
32643             fc ? 
32644                 this.btnContainer.insertBefore(document.createElement("td"),fc)
32645                 : this.btnContainer.appendChild(document.createElement("td")),
32646             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
32647             bconfig
32648         );
32649         this.syncBodyHeight();
32650         if(!this.buttons){
32651             /**
32652              * Array of all the buttons that have been added to this dialog via addButton
32653              * @type Array
32654              */
32655             this.buttons = [];
32656         }
32657         this.buttons.push(btn);
32658         return btn;
32659     },
32660
32661     /**
32662      * Sets the default button to be focused when the dialog is displayed.
32663      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
32664      * @return {Roo.BasicDialog} this
32665      */
32666     setDefaultButton : function(btn){
32667         this.defaultButton = btn;
32668         return this;
32669     },
32670
32671     // private
32672     getHeaderFooterHeight : function(safe){
32673         var height = 0;
32674         if(this.header){
32675            height += this.header.getHeight();
32676         }
32677         if(this.footer){
32678            var fm = this.footer.getMargins();
32679             height += (this.footer.getHeight()+fm.top+fm.bottom);
32680         }
32681         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
32682         height += this.centerBg.getPadding("tb");
32683         return height;
32684     },
32685
32686     // private
32687     syncBodyHeight : function()
32688     {
32689         var bd = this.body, // the text
32690             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
32691             bw = this.bwrap;
32692         var height = this.size.height - this.getHeaderFooterHeight(false);
32693         bd.setHeight(height-bd.getMargins("tb"));
32694         var hh = this.header.getHeight();
32695         var h = this.size.height-hh;
32696         cb.setHeight(h);
32697         
32698         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
32699         bw.setHeight(h-cb.getPadding("tb"));
32700         
32701         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
32702         bd.setWidth(bw.getWidth(true));
32703         if(this.tabs){
32704             this.tabs.syncHeight();
32705             if(Roo.isIE){
32706                 this.tabs.el.repaint();
32707             }
32708         }
32709     },
32710
32711     /**
32712      * Restores the previous state of the dialog if Roo.state is configured.
32713      * @return {Roo.BasicDialog} this
32714      */
32715     restoreState : function(){
32716         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
32717         if(box && box.width){
32718             this.xy = [box.x, box.y];
32719             this.resizeTo(box.width, box.height);
32720         }
32721         return this;
32722     },
32723
32724     // private
32725     beforeShow : function(){
32726         this.expand();
32727         if(this.fixedcenter){
32728             this.xy = this.el.getCenterXY(true);
32729         }
32730         if(this.modal){
32731             Roo.get(document.body).addClass("x-body-masked");
32732             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32733             this.mask.show();
32734         }
32735         this.constrainXY();
32736     },
32737
32738     // private
32739     animShow : function(){
32740         var b = Roo.get(this.animateTarget).getBox();
32741         this.proxy.setSize(b.width, b.height);
32742         this.proxy.setLocation(b.x, b.y);
32743         this.proxy.show();
32744         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
32745                     true, .35, this.showEl.createDelegate(this));
32746     },
32747
32748     /**
32749      * Shows the dialog.
32750      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
32751      * @return {Roo.BasicDialog} this
32752      */
32753     show : function(animateTarget){
32754         if (this.fireEvent("beforeshow", this) === false){
32755             return;
32756         }
32757         if(this.syncHeightBeforeShow){
32758             this.syncBodyHeight();
32759         }else if(this.firstShow){
32760             this.firstShow = false;
32761             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
32762         }
32763         this.animateTarget = animateTarget || this.animateTarget;
32764         if(!this.el.isVisible()){
32765             this.beforeShow();
32766             if(this.animateTarget && Roo.get(this.animateTarget)){
32767                 this.animShow();
32768             }else{
32769                 this.showEl();
32770             }
32771         }
32772         return this;
32773     },
32774
32775     // private
32776     showEl : function(){
32777         this.proxy.hide();
32778         this.el.setXY(this.xy);
32779         this.el.show();
32780         this.adjustAssets(true);
32781         this.toFront();
32782         this.focus();
32783         // IE peekaboo bug - fix found by Dave Fenwick
32784         if(Roo.isIE){
32785             this.el.repaint();
32786         }
32787         this.fireEvent("show", this);
32788     },
32789
32790     /**
32791      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
32792      * dialog itself will receive focus.
32793      */
32794     focus : function(){
32795         if(this.defaultButton){
32796             this.defaultButton.focus();
32797         }else{
32798             this.focusEl.focus();
32799         }
32800     },
32801
32802     // private
32803     constrainXY : function(){
32804         if(this.constraintoviewport !== false){
32805             if(!this.viewSize){
32806                 if(this.container){
32807                     var s = this.container.getSize();
32808                     this.viewSize = [s.width, s.height];
32809                 }else{
32810                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
32811                 }
32812             }
32813             var s = Roo.get(this.container||document).getScroll();
32814
32815             var x = this.xy[0], y = this.xy[1];
32816             var w = this.size.width, h = this.size.height;
32817             var vw = this.viewSize[0], vh = this.viewSize[1];
32818             // only move it if it needs it
32819             var moved = false;
32820             // first validate right/bottom
32821             if(x + w > vw+s.left){
32822                 x = vw - w;
32823                 moved = true;
32824             }
32825             if(y + h > vh+s.top){
32826                 y = vh - h;
32827                 moved = true;
32828             }
32829             // then make sure top/left isn't negative
32830             if(x < s.left){
32831                 x = s.left;
32832                 moved = true;
32833             }
32834             if(y < s.top){
32835                 y = s.top;
32836                 moved = true;
32837             }
32838             if(moved){
32839                 // cache xy
32840                 this.xy = [x, y];
32841                 if(this.isVisible()){
32842                     this.el.setLocation(x, y);
32843                     this.adjustAssets();
32844                 }
32845             }
32846         }
32847     },
32848
32849     // private
32850     onDrag : function(){
32851         if(!this.proxyDrag){
32852             this.xy = this.el.getXY();
32853             this.adjustAssets();
32854         }
32855     },
32856
32857     // private
32858     adjustAssets : function(doShow){
32859         var x = this.xy[0], y = this.xy[1];
32860         var w = this.size.width, h = this.size.height;
32861         if(doShow === true){
32862             if(this.shadow){
32863                 this.shadow.show(this.el);
32864             }
32865             if(this.shim){
32866                 this.shim.show();
32867             }
32868         }
32869         if(this.shadow && this.shadow.isVisible()){
32870             this.shadow.show(this.el);
32871         }
32872         if(this.shim && this.shim.isVisible()){
32873             this.shim.setBounds(x, y, w, h);
32874         }
32875     },
32876
32877     // private
32878     adjustViewport : function(w, h){
32879         if(!w || !h){
32880             w = Roo.lib.Dom.getViewWidth();
32881             h = Roo.lib.Dom.getViewHeight();
32882         }
32883         // cache the size
32884         this.viewSize = [w, h];
32885         if(this.modal && this.mask.isVisible()){
32886             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
32887             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32888         }
32889         if(this.isVisible()){
32890             this.constrainXY();
32891         }
32892     },
32893
32894     /**
32895      * Destroys this dialog and all its supporting elements (including any tabs, shim,
32896      * shadow, proxy, mask, etc.)  Also removes all event listeners.
32897      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32898      */
32899     destroy : function(removeEl){
32900         if(this.isVisible()){
32901             this.animateTarget = null;
32902             this.hide();
32903         }
32904         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
32905         if(this.tabs){
32906             this.tabs.destroy(removeEl);
32907         }
32908         Roo.destroy(
32909              this.shim,
32910              this.proxy,
32911              this.resizer,
32912              this.close,
32913              this.mask
32914         );
32915         if(this.dd){
32916             this.dd.unreg();
32917         }
32918         if(this.buttons){
32919            for(var i = 0, len = this.buttons.length; i < len; i++){
32920                this.buttons[i].destroy();
32921            }
32922         }
32923         this.el.removeAllListeners();
32924         if(removeEl === true){
32925             this.el.update("");
32926             this.el.remove();
32927         }
32928         Roo.DialogManager.unregister(this);
32929     },
32930
32931     // private
32932     startMove : function(){
32933         if(this.proxyDrag){
32934             this.proxy.show();
32935         }
32936         if(this.constraintoviewport !== false){
32937             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
32938         }
32939     },
32940
32941     // private
32942     endMove : function(){
32943         if(!this.proxyDrag){
32944             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
32945         }else{
32946             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
32947             this.proxy.hide();
32948         }
32949         this.refreshSize();
32950         this.adjustAssets();
32951         this.focus();
32952         this.fireEvent("move", this, this.xy[0], this.xy[1]);
32953     },
32954
32955     /**
32956      * Brings this dialog to the front of any other visible dialogs
32957      * @return {Roo.BasicDialog} this
32958      */
32959     toFront : function(){
32960         Roo.DialogManager.bringToFront(this);
32961         return this;
32962     },
32963
32964     /**
32965      * Sends this dialog to the back (under) of any other visible dialogs
32966      * @return {Roo.BasicDialog} this
32967      */
32968     toBack : function(){
32969         Roo.DialogManager.sendToBack(this);
32970         return this;
32971     },
32972
32973     /**
32974      * Centers this dialog in the viewport
32975      * @return {Roo.BasicDialog} this
32976      */
32977     center : function(){
32978         var xy = this.el.getCenterXY(true);
32979         this.moveTo(xy[0], xy[1]);
32980         return this;
32981     },
32982
32983     /**
32984      * Moves the dialog's top-left corner to the specified point
32985      * @param {Number} x
32986      * @param {Number} y
32987      * @return {Roo.BasicDialog} this
32988      */
32989     moveTo : function(x, y){
32990         this.xy = [x,y];
32991         if(this.isVisible()){
32992             this.el.setXY(this.xy);
32993             this.adjustAssets();
32994         }
32995         return this;
32996     },
32997
32998     /**
32999      * Aligns the dialog to the specified element
33000      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33001      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33002      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33003      * @return {Roo.BasicDialog} this
33004      */
33005     alignTo : function(element, position, offsets){
33006         this.xy = this.el.getAlignToXY(element, position, offsets);
33007         if(this.isVisible()){
33008             this.el.setXY(this.xy);
33009             this.adjustAssets();
33010         }
33011         return this;
33012     },
33013
33014     /**
33015      * Anchors an element to another element and realigns it when the window is resized.
33016      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33017      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33018      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33019      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33020      * is a number, it is used as the buffer delay (defaults to 50ms).
33021      * @return {Roo.BasicDialog} this
33022      */
33023     anchorTo : function(el, alignment, offsets, monitorScroll){
33024         var action = function(){
33025             this.alignTo(el, alignment, offsets);
33026         };
33027         Roo.EventManager.onWindowResize(action, this);
33028         var tm = typeof monitorScroll;
33029         if(tm != 'undefined'){
33030             Roo.EventManager.on(window, 'scroll', action, this,
33031                 {buffer: tm == 'number' ? monitorScroll : 50});
33032         }
33033         action.call(this);
33034         return this;
33035     },
33036
33037     /**
33038      * Returns true if the dialog is visible
33039      * @return {Boolean}
33040      */
33041     isVisible : function(){
33042         return this.el.isVisible();
33043     },
33044
33045     // private
33046     animHide : function(callback){
33047         var b = Roo.get(this.animateTarget).getBox();
33048         this.proxy.show();
33049         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33050         this.el.hide();
33051         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33052                     this.hideEl.createDelegate(this, [callback]));
33053     },
33054
33055     /**
33056      * Hides the dialog.
33057      * @param {Function} callback (optional) Function to call when the dialog is hidden
33058      * @return {Roo.BasicDialog} this
33059      */
33060     hide : function(callback){
33061         if (this.fireEvent("beforehide", this) === false){
33062             return;
33063         }
33064         if(this.shadow){
33065             this.shadow.hide();
33066         }
33067         if(this.shim) {
33068           this.shim.hide();
33069         }
33070         // sometimes animateTarget seems to get set.. causing problems...
33071         // this just double checks..
33072         if(this.animateTarget && Roo.get(this.animateTarget)) {
33073            this.animHide(callback);
33074         }else{
33075             this.el.hide();
33076             this.hideEl(callback);
33077         }
33078         return this;
33079     },
33080
33081     // private
33082     hideEl : function(callback){
33083         this.proxy.hide();
33084         if(this.modal){
33085             this.mask.hide();
33086             Roo.get(document.body).removeClass("x-body-masked");
33087         }
33088         this.fireEvent("hide", this);
33089         if(typeof callback == "function"){
33090             callback();
33091         }
33092     },
33093
33094     // private
33095     hideAction : function(){
33096         this.setLeft("-10000px");
33097         this.setTop("-10000px");
33098         this.setStyle("visibility", "hidden");
33099     },
33100
33101     // private
33102     refreshSize : function(){
33103         this.size = this.el.getSize();
33104         this.xy = this.el.getXY();
33105         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33106     },
33107
33108     // private
33109     // z-index is managed by the DialogManager and may be overwritten at any time
33110     setZIndex : function(index){
33111         if(this.modal){
33112             this.mask.setStyle("z-index", index);
33113         }
33114         if(this.shim){
33115             this.shim.setStyle("z-index", ++index);
33116         }
33117         if(this.shadow){
33118             this.shadow.setZIndex(++index);
33119         }
33120         this.el.setStyle("z-index", ++index);
33121         if(this.proxy){
33122             this.proxy.setStyle("z-index", ++index);
33123         }
33124         if(this.resizer){
33125             this.resizer.proxy.setStyle("z-index", ++index);
33126         }
33127
33128         this.lastZIndex = index;
33129     },
33130
33131     /**
33132      * Returns the element for this dialog
33133      * @return {Roo.Element} The underlying dialog Element
33134      */
33135     getEl : function(){
33136         return this.el;
33137     }
33138 });
33139
33140 /**
33141  * @class Roo.DialogManager
33142  * Provides global access to BasicDialogs that have been created and
33143  * support for z-indexing (layering) multiple open dialogs.
33144  */
33145 Roo.DialogManager = function(){
33146     var list = {};
33147     var accessList = [];
33148     var front = null;
33149
33150     // private
33151     var sortDialogs = function(d1, d2){
33152         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33153     };
33154
33155     // private
33156     var orderDialogs = function(){
33157         accessList.sort(sortDialogs);
33158         var seed = Roo.DialogManager.zseed;
33159         for(var i = 0, len = accessList.length; i < len; i++){
33160             var dlg = accessList[i];
33161             if(dlg){
33162                 dlg.setZIndex(seed + (i*10));
33163             }
33164         }
33165     };
33166
33167     return {
33168         /**
33169          * The starting z-index for BasicDialogs (defaults to 9000)
33170          * @type Number The z-index value
33171          */
33172         zseed : 9000,
33173
33174         // private
33175         register : function(dlg){
33176             list[dlg.id] = dlg;
33177             accessList.push(dlg);
33178         },
33179
33180         // private
33181         unregister : function(dlg){
33182             delete list[dlg.id];
33183             var i=0;
33184             var len=0;
33185             if(!accessList.indexOf){
33186                 for(  i = 0, len = accessList.length; i < len; i++){
33187                     if(accessList[i] == dlg){
33188                         accessList.splice(i, 1);
33189                         return;
33190                     }
33191                 }
33192             }else{
33193                  i = accessList.indexOf(dlg);
33194                 if(i != -1){
33195                     accessList.splice(i, 1);
33196                 }
33197             }
33198         },
33199
33200         /**
33201          * Gets a registered dialog by id
33202          * @param {String/Object} id The id of the dialog or a dialog
33203          * @return {Roo.BasicDialog} this
33204          */
33205         get : function(id){
33206             return typeof id == "object" ? id : list[id];
33207         },
33208
33209         /**
33210          * Brings the specified dialog to the front
33211          * @param {String/Object} dlg The id of the dialog or a dialog
33212          * @return {Roo.BasicDialog} this
33213          */
33214         bringToFront : function(dlg){
33215             dlg = this.get(dlg);
33216             if(dlg != front){
33217                 front = dlg;
33218                 dlg._lastAccess = new Date().getTime();
33219                 orderDialogs();
33220             }
33221             return dlg;
33222         },
33223
33224         /**
33225          * Sends the specified dialog to the back
33226          * @param {String/Object} dlg The id of the dialog or a dialog
33227          * @return {Roo.BasicDialog} this
33228          */
33229         sendToBack : function(dlg){
33230             dlg = this.get(dlg);
33231             dlg._lastAccess = -(new Date().getTime());
33232             orderDialogs();
33233             return dlg;
33234         },
33235
33236         /**
33237          * Hides all dialogs
33238          */
33239         hideAll : function(){
33240             for(var id in list){
33241                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33242                     list[id].hide();
33243                 }
33244             }
33245         }
33246     };
33247 }();
33248
33249 /**
33250  * @class Roo.LayoutDialog
33251  * @extends Roo.BasicDialog
33252  * Dialog which provides adjustments for working with a layout in a Dialog.
33253  * Add your necessary layout config options to the dialog's config.<br>
33254  * Example usage (including a nested layout):
33255  * <pre><code>
33256 if(!dialog){
33257     dialog = new Roo.LayoutDialog("download-dlg", {
33258         modal: true,
33259         width:600,
33260         height:450,
33261         shadow:true,
33262         minWidth:500,
33263         minHeight:350,
33264         autoTabs:true,
33265         proxyDrag:true,
33266         // layout config merges with the dialog config
33267         center:{
33268             tabPosition: "top",
33269             alwaysShowTabs: true
33270         }
33271     });
33272     dialog.addKeyListener(27, dialog.hide, dialog);
33273     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
33274     dialog.addButton("Build It!", this.getDownload, this);
33275
33276     // we can even add nested layouts
33277     var innerLayout = new Roo.BorderLayout("dl-inner", {
33278         east: {
33279             initialSize: 200,
33280             autoScroll:true,
33281             split:true
33282         },
33283         center: {
33284             autoScroll:true
33285         }
33286     });
33287     innerLayout.beginUpdate();
33288     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
33289     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
33290     innerLayout.endUpdate(true);
33291
33292     var layout = dialog.getLayout();
33293     layout.beginUpdate();
33294     layout.add("center", new Roo.ContentPanel("standard-panel",
33295                         {title: "Download the Source", fitToFrame:true}));
33296     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
33297                {title: "Build your own roo.js"}));
33298     layout.getRegion("center").showPanel(sp);
33299     layout.endUpdate();
33300 }
33301 </code></pre>
33302     * @constructor
33303     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
33304     * @param {Object} config configuration options
33305   */
33306 Roo.LayoutDialog = function(el, cfg){
33307     
33308     var config=  cfg;
33309     if (typeof(cfg) == 'undefined') {
33310         config = Roo.apply({}, el);
33311         // not sure why we use documentElement here.. - it should always be body.
33312         // IE7 borks horribly if we use documentElement.
33313         // webkit also does not like documentElement - it creates a body element...
33314         el = Roo.get( document.body || document.documentElement ).createChild();
33315         //config.autoCreate = true;
33316     }
33317     
33318     
33319     config.autoTabs = false;
33320     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
33321     this.body.setStyle({overflow:"hidden", position:"relative"});
33322     this.layout = new Roo.BorderLayout(this.body.dom, config);
33323     this.layout.monitorWindowResize = false;
33324     this.el.addClass("x-dlg-auto-layout");
33325     // fix case when center region overwrites center function
33326     this.center = Roo.BasicDialog.prototype.center;
33327     this.on("show", this.layout.layout, this.layout, true);
33328     if (config.items) {
33329         var xitems = config.items;
33330         delete config.items;
33331         Roo.each(xitems, this.addxtype, this);
33332     }
33333     
33334     
33335 };
33336 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
33337     /**
33338      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
33339      * @deprecated
33340      */
33341     endUpdate : function(){
33342         this.layout.endUpdate();
33343     },
33344
33345     /**
33346      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
33347      *  @deprecated
33348      */
33349     beginUpdate : function(){
33350         this.layout.beginUpdate();
33351     },
33352
33353     /**
33354      * Get the BorderLayout for this dialog
33355      * @return {Roo.BorderLayout}
33356      */
33357     getLayout : function(){
33358         return this.layout;
33359     },
33360
33361     showEl : function(){
33362         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
33363         if(Roo.isIE7){
33364             this.layout.layout();
33365         }
33366     },
33367
33368     // private
33369     // Use the syncHeightBeforeShow config option to control this automatically
33370     syncBodyHeight : function(){
33371         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
33372         if(this.layout){this.layout.layout();}
33373     },
33374     
33375       /**
33376      * Add an xtype element (actually adds to the layout.)
33377      * @return {Object} xdata xtype object data.
33378      */
33379     
33380     addxtype : function(c) {
33381         return this.layout.addxtype(c);
33382     }
33383 });/*
33384  * Based on:
33385  * Ext JS Library 1.1.1
33386  * Copyright(c) 2006-2007, Ext JS, LLC.
33387  *
33388  * Originally Released Under LGPL - original licence link has changed is not relivant.
33389  *
33390  * Fork - LGPL
33391  * <script type="text/javascript">
33392  */
33393  
33394 /**
33395  * @class Roo.MessageBox
33396  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
33397  * Example usage:
33398  *<pre><code>
33399 // Basic alert:
33400 Roo.Msg.alert('Status', 'Changes saved successfully.');
33401
33402 // Prompt for user data:
33403 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
33404     if (btn == 'ok'){
33405         // process text value...
33406     }
33407 });
33408
33409 // Show a dialog using config options:
33410 Roo.Msg.show({
33411    title:'Save Changes?',
33412    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
33413    buttons: Roo.Msg.YESNOCANCEL,
33414    fn: processResult,
33415    animEl: 'elId'
33416 });
33417 </code></pre>
33418  * @singleton
33419  */
33420 Roo.MessageBox = function(){
33421     var dlg, opt, mask, waitTimer;
33422     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
33423     var buttons, activeTextEl, bwidth;
33424
33425     // private
33426     var handleButton = function(button){
33427         dlg.hide();
33428         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
33429     };
33430
33431     // private
33432     var handleHide = function(){
33433         if(opt && opt.cls){
33434             dlg.el.removeClass(opt.cls);
33435         }
33436         if(waitTimer){
33437             Roo.TaskMgr.stop(waitTimer);
33438             waitTimer = null;
33439         }
33440     };
33441
33442     // private
33443     var updateButtons = function(b){
33444         var width = 0;
33445         if(!b){
33446             buttons["ok"].hide();
33447             buttons["cancel"].hide();
33448             buttons["yes"].hide();
33449             buttons["no"].hide();
33450             dlg.footer.dom.style.display = 'none';
33451             return width;
33452         }
33453         dlg.footer.dom.style.display = '';
33454         for(var k in buttons){
33455             if(typeof buttons[k] != "function"){
33456                 if(b[k]){
33457                     buttons[k].show();
33458                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
33459                     width += buttons[k].el.getWidth()+15;
33460                 }else{
33461                     buttons[k].hide();
33462                 }
33463             }
33464         }
33465         return width;
33466     };
33467
33468     // private
33469     var handleEsc = function(d, k, e){
33470         if(opt && opt.closable !== false){
33471             dlg.hide();
33472         }
33473         if(e){
33474             e.stopEvent();
33475         }
33476     };
33477
33478     return {
33479         /**
33480          * Returns a reference to the underlying {@link Roo.BasicDialog} element
33481          * @return {Roo.BasicDialog} The BasicDialog element
33482          */
33483         getDialog : function(){
33484            if(!dlg){
33485                 dlg = new Roo.BasicDialog("x-msg-box", {
33486                     autoCreate : true,
33487                     shadow: true,
33488                     draggable: true,
33489                     resizable:false,
33490                     constraintoviewport:false,
33491                     fixedcenter:true,
33492                     collapsible : false,
33493                     shim:true,
33494                     modal: true,
33495                     width:400, height:100,
33496                     buttonAlign:"center",
33497                     closeClick : function(){
33498                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
33499                             handleButton("no");
33500                         }else{
33501                             handleButton("cancel");
33502                         }
33503                     }
33504                 });
33505                 dlg.on("hide", handleHide);
33506                 mask = dlg.mask;
33507                 dlg.addKeyListener(27, handleEsc);
33508                 buttons = {};
33509                 var bt = this.buttonText;
33510                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
33511                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
33512                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
33513                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
33514                 bodyEl = dlg.body.createChild({
33515
33516                     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>'
33517                 });
33518                 msgEl = bodyEl.dom.firstChild;
33519                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
33520                 textboxEl.enableDisplayMode();
33521                 textboxEl.addKeyListener([10,13], function(){
33522                     if(dlg.isVisible() && opt && opt.buttons){
33523                         if(opt.buttons.ok){
33524                             handleButton("ok");
33525                         }else if(opt.buttons.yes){
33526                             handleButton("yes");
33527                         }
33528                     }
33529                 });
33530                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
33531                 textareaEl.enableDisplayMode();
33532                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
33533                 progressEl.enableDisplayMode();
33534                 var pf = progressEl.dom.firstChild;
33535                 if (pf) {
33536                     pp = Roo.get(pf.firstChild);
33537                     pp.setHeight(pf.offsetHeight);
33538                 }
33539                 
33540             }
33541             return dlg;
33542         },
33543
33544         /**
33545          * Updates the message box body text
33546          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
33547          * the XHTML-compliant non-breaking space character '&amp;#160;')
33548          * @return {Roo.MessageBox} This message box
33549          */
33550         updateText : function(text){
33551             if(!dlg.isVisible() && !opt.width){
33552                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
33553             }
33554             msgEl.innerHTML = text || '&#160;';
33555       
33556             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
33557             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
33558             var w = Math.max(
33559                     Math.min(opt.width || cw , this.maxWidth), 
33560                     Math.max(opt.minWidth || this.minWidth, bwidth)
33561             );
33562             if(opt.prompt){
33563                 activeTextEl.setWidth(w);
33564             }
33565             if(dlg.isVisible()){
33566                 dlg.fixedcenter = false;
33567             }
33568             // to big, make it scroll. = But as usual stupid IE does not support
33569             // !important..
33570             
33571             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
33572                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
33573                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
33574             } else {
33575                 bodyEl.dom.style.height = '';
33576                 bodyEl.dom.style.overflowY = '';
33577             }
33578             if (cw > w) {
33579                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
33580             } else {
33581                 bodyEl.dom.style.overflowX = '';
33582             }
33583             
33584             dlg.setContentSize(w, bodyEl.getHeight());
33585             if(dlg.isVisible()){
33586                 dlg.fixedcenter = true;
33587             }
33588             return this;
33589         },
33590
33591         /**
33592          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
33593          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
33594          * @param {Number} value Any number between 0 and 1 (e.g., .5)
33595          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
33596          * @return {Roo.MessageBox} This message box
33597          */
33598         updateProgress : function(value, text){
33599             if(text){
33600                 this.updateText(text);
33601             }
33602             if (pp) { // weird bug on my firefox - for some reason this is not defined
33603                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
33604             }
33605             return this;
33606         },        
33607
33608         /**
33609          * Returns true if the message box is currently displayed
33610          * @return {Boolean} True if the message box is visible, else false
33611          */
33612         isVisible : function(){
33613             return dlg && dlg.isVisible();  
33614         },
33615
33616         /**
33617          * Hides the message box if it is displayed
33618          */
33619         hide : function(){
33620             if(this.isVisible()){
33621                 dlg.hide();
33622             }  
33623         },
33624
33625         /**
33626          * Displays a new message box, or reinitializes an existing message box, based on the config options
33627          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
33628          * The following config object properties are supported:
33629          * <pre>
33630 Property    Type             Description
33631 ----------  ---------------  ------------------------------------------------------------------------------------
33632 animEl            String/Element   An id or Element from which the message box should animate as it opens and
33633                                    closes (defaults to undefined)
33634 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
33635                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
33636 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
33637                                    progress and wait dialogs will ignore this property and always hide the
33638                                    close button as they can only be closed programmatically.
33639 cls               String           A custom CSS class to apply to the message box element
33640 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
33641                                    displayed (defaults to 75)
33642 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
33643                                    function will be btn (the name of the button that was clicked, if applicable,
33644                                    e.g. "ok"), and text (the value of the active text field, if applicable).
33645                                    Progress and wait dialogs will ignore this option since they do not respond to
33646                                    user actions and can only be closed programmatically, so any required function
33647                                    should be called by the same code after it closes the dialog.
33648 icon              String           A CSS class that provides a background image to be used as an icon for
33649                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
33650 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
33651 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
33652 modal             Boolean          False to allow user interaction with the page while the message box is
33653                                    displayed (defaults to true)
33654 msg               String           A string that will replace the existing message box body text (defaults
33655                                    to the XHTML-compliant non-breaking space character '&#160;')
33656 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
33657 progress          Boolean          True to display a progress bar (defaults to false)
33658 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
33659 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
33660 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
33661 title             String           The title text
33662 value             String           The string value to set into the active textbox element if displayed
33663 wait              Boolean          True to display a progress bar (defaults to false)
33664 width             Number           The width of the dialog in pixels
33665 </pre>
33666          *
33667          * Example usage:
33668          * <pre><code>
33669 Roo.Msg.show({
33670    title: 'Address',
33671    msg: 'Please enter your address:',
33672    width: 300,
33673    buttons: Roo.MessageBox.OKCANCEL,
33674    multiline: true,
33675    fn: saveAddress,
33676    animEl: 'addAddressBtn'
33677 });
33678 </code></pre>
33679          * @param {Object} config Configuration options
33680          * @return {Roo.MessageBox} This message box
33681          */
33682         show : function(options)
33683         {
33684             
33685             // this causes nightmares if you show one dialog after another
33686             // especially on callbacks..
33687              
33688             if(this.isVisible()){
33689                 
33690                 this.hide();
33691                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
33692                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
33693                 Roo.log("New Dialog Message:" +  options.msg )
33694                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
33695                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
33696                 
33697             }
33698             var d = this.getDialog();
33699             opt = options;
33700             d.setTitle(opt.title || "&#160;");
33701             d.close.setDisplayed(opt.closable !== false);
33702             activeTextEl = textboxEl;
33703             opt.prompt = opt.prompt || (opt.multiline ? true : false);
33704             if(opt.prompt){
33705                 if(opt.multiline){
33706                     textboxEl.hide();
33707                     textareaEl.show();
33708                     textareaEl.setHeight(typeof opt.multiline == "number" ?
33709                         opt.multiline : this.defaultTextHeight);
33710                     activeTextEl = textareaEl;
33711                 }else{
33712                     textboxEl.show();
33713                     textareaEl.hide();
33714                 }
33715             }else{
33716                 textboxEl.hide();
33717                 textareaEl.hide();
33718             }
33719             progressEl.setDisplayed(opt.progress === true);
33720             this.updateProgress(0);
33721             activeTextEl.dom.value = opt.value || "";
33722             if(opt.prompt){
33723                 dlg.setDefaultButton(activeTextEl);
33724             }else{
33725                 var bs = opt.buttons;
33726                 var db = null;
33727                 if(bs && bs.ok){
33728                     db = buttons["ok"];
33729                 }else if(bs && bs.yes){
33730                     db = buttons["yes"];
33731                 }
33732                 dlg.setDefaultButton(db);
33733             }
33734             bwidth = updateButtons(opt.buttons);
33735             this.updateText(opt.msg);
33736             if(opt.cls){
33737                 d.el.addClass(opt.cls);
33738             }
33739             d.proxyDrag = opt.proxyDrag === true;
33740             d.modal = opt.modal !== false;
33741             d.mask = opt.modal !== false ? mask : false;
33742             if(!d.isVisible()){
33743                 // force it to the end of the z-index stack so it gets a cursor in FF
33744                 document.body.appendChild(dlg.el.dom);
33745                 d.animateTarget = null;
33746                 d.show(options.animEl);
33747             }
33748             return this;
33749         },
33750
33751         /**
33752          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
33753          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
33754          * and closing the message box when the process is complete.
33755          * @param {String} title The title bar text
33756          * @param {String} msg The message box body text
33757          * @return {Roo.MessageBox} This message box
33758          */
33759         progress : function(title, msg){
33760             this.show({
33761                 title : title,
33762                 msg : msg,
33763                 buttons: false,
33764                 progress:true,
33765                 closable:false,
33766                 minWidth: this.minProgressWidth,
33767                 modal : true
33768             });
33769             return this;
33770         },
33771
33772         /**
33773          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
33774          * If a callback function is passed it will be called after the user clicks the button, and the
33775          * id of the button that was clicked will be passed as the only parameter to the callback
33776          * (could also be the top-right close button).
33777          * @param {String} title The title bar text
33778          * @param {String} msg The message box body text
33779          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33780          * @param {Object} scope (optional) The scope of the callback function
33781          * @return {Roo.MessageBox} This message box
33782          */
33783         alert : function(title, msg, fn, scope){
33784             this.show({
33785                 title : title,
33786                 msg : msg,
33787                 buttons: this.OK,
33788                 fn: fn,
33789                 scope : scope,
33790                 modal : true
33791             });
33792             return this;
33793         },
33794
33795         /**
33796          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
33797          * interaction while waiting for a long-running process to complete that does not have defined intervals.
33798          * You are responsible for closing the message box when the process is complete.
33799          * @param {String} msg The message box body text
33800          * @param {String} title (optional) The title bar text
33801          * @return {Roo.MessageBox} This message box
33802          */
33803         wait : function(msg, title){
33804             this.show({
33805                 title : title,
33806                 msg : msg,
33807                 buttons: false,
33808                 closable:false,
33809                 progress:true,
33810                 modal:true,
33811                 width:300,
33812                 wait:true
33813             });
33814             waitTimer = Roo.TaskMgr.start({
33815                 run: function(i){
33816                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
33817                 },
33818                 interval: 1000
33819             });
33820             return this;
33821         },
33822
33823         /**
33824          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
33825          * If a callback function is passed it will be called after the user clicks either button, and the id of the
33826          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
33827          * @param {String} title The title bar text
33828          * @param {String} msg The message box body text
33829          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33830          * @param {Object} scope (optional) The scope of the callback function
33831          * @return {Roo.MessageBox} This message box
33832          */
33833         confirm : function(title, msg, fn, scope){
33834             this.show({
33835                 title : title,
33836                 msg : msg,
33837                 buttons: this.YESNO,
33838                 fn: fn,
33839                 scope : scope,
33840                 modal : true
33841             });
33842             return this;
33843         },
33844
33845         /**
33846          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
33847          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
33848          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
33849          * (could also be the top-right close button) and the text that was entered will be passed as the two
33850          * parameters to the callback.
33851          * @param {String} title The title bar text
33852          * @param {String} msg The message box body text
33853          * @param {Function} fn (optional) The callback function invoked after the message box is closed
33854          * @param {Object} scope (optional) The scope of the callback function
33855          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
33856          * property, or the height in pixels to create the textbox (defaults to false / single-line)
33857          * @return {Roo.MessageBox} This message box
33858          */
33859         prompt : function(title, msg, fn, scope, multiline){
33860             this.show({
33861                 title : title,
33862                 msg : msg,
33863                 buttons: this.OKCANCEL,
33864                 fn: fn,
33865                 minWidth:250,
33866                 scope : scope,
33867                 prompt:true,
33868                 multiline: multiline,
33869                 modal : true
33870             });
33871             return this;
33872         },
33873
33874         /**
33875          * Button config that displays a single OK button
33876          * @type Object
33877          */
33878         OK : {ok:true},
33879         /**
33880          * Button config that displays Yes and No buttons
33881          * @type Object
33882          */
33883         YESNO : {yes:true, no:true},
33884         /**
33885          * Button config that displays OK and Cancel buttons
33886          * @type Object
33887          */
33888         OKCANCEL : {ok:true, cancel:true},
33889         /**
33890          * Button config that displays Yes, No and Cancel buttons
33891          * @type Object
33892          */
33893         YESNOCANCEL : {yes:true, no:true, cancel:true},
33894
33895         /**
33896          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
33897          * @type Number
33898          */
33899         defaultTextHeight : 75,
33900         /**
33901          * The maximum width in pixels of the message box (defaults to 600)
33902          * @type Number
33903          */
33904         maxWidth : 600,
33905         /**
33906          * The minimum width in pixels of the message box (defaults to 100)
33907          * @type Number
33908          */
33909         minWidth : 100,
33910         /**
33911          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
33912          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
33913          * @type Number
33914          */
33915         minProgressWidth : 250,
33916         /**
33917          * An object containing the default button text strings that can be overriden for localized language support.
33918          * Supported properties are: ok, cancel, yes and no.
33919          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
33920          * @type Object
33921          */
33922         buttonText : {
33923             ok : "OK",
33924             cancel : "Cancel",
33925             yes : "Yes",
33926             no : "No"
33927         }
33928     };
33929 }();
33930
33931 /**
33932  * Shorthand for {@link Roo.MessageBox}
33933  */
33934 Roo.Msg = Roo.MessageBox;/*
33935  * Based on:
33936  * Ext JS Library 1.1.1
33937  * Copyright(c) 2006-2007, Ext JS, LLC.
33938  *
33939  * Originally Released Under LGPL - original licence link has changed is not relivant.
33940  *
33941  * Fork - LGPL
33942  * <script type="text/javascript">
33943  */
33944 /**
33945  * @class Roo.QuickTips
33946  * Provides attractive and customizable tooltips for any element.
33947  * @singleton
33948  */
33949 Roo.QuickTips = function(){
33950     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
33951     var ce, bd, xy, dd;
33952     var visible = false, disabled = true, inited = false;
33953     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
33954     
33955     var onOver = function(e){
33956         if(disabled){
33957             return;
33958         }
33959         var t = e.getTarget();
33960         if(!t || t.nodeType !== 1 || t == document || t == document.body){
33961             return;
33962         }
33963         if(ce && t == ce.el){
33964             clearTimeout(hideProc);
33965             return;
33966         }
33967         if(t && tagEls[t.id]){
33968             tagEls[t.id].el = t;
33969             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
33970             return;
33971         }
33972         var ttp, et = Roo.fly(t);
33973         var ns = cfg.namespace;
33974         if(tm.interceptTitles && t.title){
33975             ttp = t.title;
33976             t.qtip = ttp;
33977             t.removeAttribute("title");
33978             e.preventDefault();
33979         }else{
33980             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
33981         }
33982         if(ttp){
33983             showProc = show.defer(tm.showDelay, tm, [{
33984                 el: t, 
33985                 text: ttp.replace(/\\n/g,'<br/>'),
33986                 width: et.getAttributeNS(ns, cfg.width),
33987                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
33988                 title: et.getAttributeNS(ns, cfg.title),
33989                     cls: et.getAttributeNS(ns, cfg.cls)
33990             }]);
33991         }
33992     };
33993     
33994     var onOut = function(e){
33995         clearTimeout(showProc);
33996         var t = e.getTarget();
33997         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
33998             hideProc = setTimeout(hide, tm.hideDelay);
33999         }
34000     };
34001     
34002     var onMove = function(e){
34003         if(disabled){
34004             return;
34005         }
34006         xy = e.getXY();
34007         xy[1] += 18;
34008         if(tm.trackMouse && ce){
34009             el.setXY(xy);
34010         }
34011     };
34012     
34013     var onDown = function(e){
34014         clearTimeout(showProc);
34015         clearTimeout(hideProc);
34016         if(!e.within(el)){
34017             if(tm.hideOnClick){
34018                 hide();
34019                 tm.disable();
34020                 tm.enable.defer(100, tm);
34021             }
34022         }
34023     };
34024     
34025     var getPad = function(){
34026         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34027     };
34028
34029     var show = function(o){
34030         if(disabled){
34031             return;
34032         }
34033         clearTimeout(dismissProc);
34034         ce = o;
34035         if(removeCls){ // in case manually hidden
34036             el.removeClass(removeCls);
34037             removeCls = null;
34038         }
34039         if(ce.cls){
34040             el.addClass(ce.cls);
34041             removeCls = ce.cls;
34042         }
34043         if(ce.title){
34044             tipTitle.update(ce.title);
34045             tipTitle.show();
34046         }else{
34047             tipTitle.update('');
34048             tipTitle.hide();
34049         }
34050         el.dom.style.width  = tm.maxWidth+'px';
34051         //tipBody.dom.style.width = '';
34052         tipBodyText.update(o.text);
34053         var p = getPad(), w = ce.width;
34054         if(!w){
34055             var td = tipBodyText.dom;
34056             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34057             if(aw > tm.maxWidth){
34058                 w = tm.maxWidth;
34059             }else if(aw < tm.minWidth){
34060                 w = tm.minWidth;
34061             }else{
34062                 w = aw;
34063             }
34064         }
34065         //tipBody.setWidth(w);
34066         el.setWidth(parseInt(w, 10) + p);
34067         if(ce.autoHide === false){
34068             close.setDisplayed(true);
34069             if(dd){
34070                 dd.unlock();
34071             }
34072         }else{
34073             close.setDisplayed(false);
34074             if(dd){
34075                 dd.lock();
34076             }
34077         }
34078         if(xy){
34079             el.avoidY = xy[1]-18;
34080             el.setXY(xy);
34081         }
34082         if(tm.animate){
34083             el.setOpacity(.1);
34084             el.setStyle("visibility", "visible");
34085             el.fadeIn({callback: afterShow});
34086         }else{
34087             afterShow();
34088         }
34089     };
34090     
34091     var afterShow = function(){
34092         if(ce){
34093             el.show();
34094             esc.enable();
34095             if(tm.autoDismiss && ce.autoHide !== false){
34096                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34097             }
34098         }
34099     };
34100     
34101     var hide = function(noanim){
34102         clearTimeout(dismissProc);
34103         clearTimeout(hideProc);
34104         ce = null;
34105         if(el.isVisible()){
34106             esc.disable();
34107             if(noanim !== true && tm.animate){
34108                 el.fadeOut({callback: afterHide});
34109             }else{
34110                 afterHide();
34111             } 
34112         }
34113     };
34114     
34115     var afterHide = function(){
34116         el.hide();
34117         if(removeCls){
34118             el.removeClass(removeCls);
34119             removeCls = null;
34120         }
34121     };
34122     
34123     return {
34124         /**
34125         * @cfg {Number} minWidth
34126         * The minimum width of the quick tip (defaults to 40)
34127         */
34128        minWidth : 40,
34129         /**
34130         * @cfg {Number} maxWidth
34131         * The maximum width of the quick tip (defaults to 300)
34132         */
34133        maxWidth : 300,
34134         /**
34135         * @cfg {Boolean} interceptTitles
34136         * True to automatically use the element's DOM title value if available (defaults to false)
34137         */
34138        interceptTitles : false,
34139         /**
34140         * @cfg {Boolean} trackMouse
34141         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34142         */
34143        trackMouse : false,
34144         /**
34145         * @cfg {Boolean} hideOnClick
34146         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34147         */
34148        hideOnClick : true,
34149         /**
34150         * @cfg {Number} showDelay
34151         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34152         */
34153        showDelay : 500,
34154         /**
34155         * @cfg {Number} hideDelay
34156         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34157         */
34158        hideDelay : 200,
34159         /**
34160         * @cfg {Boolean} autoHide
34161         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34162         * Used in conjunction with hideDelay.
34163         */
34164        autoHide : true,
34165         /**
34166         * @cfg {Boolean}
34167         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34168         * (defaults to true).  Used in conjunction with autoDismissDelay.
34169         */
34170        autoDismiss : true,
34171         /**
34172         * @cfg {Number}
34173         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34174         */
34175        autoDismissDelay : 5000,
34176        /**
34177         * @cfg {Boolean} animate
34178         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34179         */
34180        animate : false,
34181
34182        /**
34183         * @cfg {String} title
34184         * Title text to display (defaults to '').  This can be any valid HTML markup.
34185         */
34186         title: '',
34187        /**
34188         * @cfg {String} text
34189         * Body text to display (defaults to '').  This can be any valid HTML markup.
34190         */
34191         text : '',
34192        /**
34193         * @cfg {String} cls
34194         * A CSS class to apply to the base quick tip element (defaults to '').
34195         */
34196         cls : '',
34197        /**
34198         * @cfg {Number} width
34199         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34200         * minWidth or maxWidth.
34201         */
34202         width : null,
34203
34204     /**
34205      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34206      * or display QuickTips in a page.
34207      */
34208        init : function(){
34209           tm = Roo.QuickTips;
34210           cfg = tm.tagConfig;
34211           if(!inited){
34212               if(!Roo.isReady){ // allow calling of init() before onReady
34213                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34214                   return;
34215               }
34216               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34217               el.fxDefaults = {stopFx: true};
34218               // maximum custom styling
34219               //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>');
34220               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>');              
34221               tipTitle = el.child('h3');
34222               tipTitle.enableDisplayMode("block");
34223               tipBody = el.child('div.x-tip-bd');
34224               tipBodyText = el.child('div.x-tip-bd-inner');
34225               //bdLeft = el.child('div.x-tip-bd-left');
34226               //bdRight = el.child('div.x-tip-bd-right');
34227               close = el.child('div.x-tip-close');
34228               close.enableDisplayMode("block");
34229               close.on("click", hide);
34230               var d = Roo.get(document);
34231               d.on("mousedown", onDown);
34232               d.on("mouseover", onOver);
34233               d.on("mouseout", onOut);
34234               d.on("mousemove", onMove);
34235               esc = d.addKeyListener(27, hide);
34236               esc.disable();
34237               if(Roo.dd.DD){
34238                   dd = el.initDD("default", null, {
34239                       onDrag : function(){
34240                           el.sync();  
34241                       }
34242                   });
34243                   dd.setHandleElId(tipTitle.id);
34244                   dd.lock();
34245               }
34246               inited = true;
34247           }
34248           this.enable(); 
34249        },
34250
34251     /**
34252      * Configures a new quick tip instance and assigns it to a target element.  The following config options
34253      * are supported:
34254      * <pre>
34255 Property    Type                   Description
34256 ----------  ---------------------  ------------------------------------------------------------------------
34257 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
34258      * </ul>
34259      * @param {Object} config The config object
34260      */
34261        register : function(config){
34262            var cs = config instanceof Array ? config : arguments;
34263            for(var i = 0, len = cs.length; i < len; i++) {
34264                var c = cs[i];
34265                var target = c.target;
34266                if(target){
34267                    if(target instanceof Array){
34268                        for(var j = 0, jlen = target.length; j < jlen; j++){
34269                            tagEls[target[j]] = c;
34270                        }
34271                    }else{
34272                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
34273                    }
34274                }
34275            }
34276        },
34277
34278     /**
34279      * Removes this quick tip from its element and destroys it.
34280      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
34281      */
34282        unregister : function(el){
34283            delete tagEls[Roo.id(el)];
34284        },
34285
34286     /**
34287      * Enable this quick tip.
34288      */
34289        enable : function(){
34290            if(inited && disabled){
34291                locks.pop();
34292                if(locks.length < 1){
34293                    disabled = false;
34294                }
34295            }
34296        },
34297
34298     /**
34299      * Disable this quick tip.
34300      */
34301        disable : function(){
34302           disabled = true;
34303           clearTimeout(showProc);
34304           clearTimeout(hideProc);
34305           clearTimeout(dismissProc);
34306           if(ce){
34307               hide(true);
34308           }
34309           locks.push(1);
34310        },
34311
34312     /**
34313      * Returns true if the quick tip is enabled, else false.
34314      */
34315        isEnabled : function(){
34316             return !disabled;
34317        },
34318
34319         // private
34320        tagConfig : {
34321            namespace : "roo", // was ext?? this may break..
34322            alt_namespace : "ext",
34323            attribute : "qtip",
34324            width : "width",
34325            target : "target",
34326            title : "qtitle",
34327            hide : "hide",
34328            cls : "qclass"
34329        }
34330    };
34331 }();
34332
34333 // backwards compat
34334 Roo.QuickTips.tips = Roo.QuickTips.register;/*
34335  * Based on:
34336  * Ext JS Library 1.1.1
34337  * Copyright(c) 2006-2007, Ext JS, LLC.
34338  *
34339  * Originally Released Under LGPL - original licence link has changed is not relivant.
34340  *
34341  * Fork - LGPL
34342  * <script type="text/javascript">
34343  */
34344  
34345
34346 /**
34347  * @class Roo.tree.TreePanel
34348  * @extends Roo.data.Tree
34349
34350  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
34351  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
34352  * @cfg {Boolean} enableDD true to enable drag and drop
34353  * @cfg {Boolean} enableDrag true to enable just drag
34354  * @cfg {Boolean} enableDrop true to enable just drop
34355  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
34356  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
34357  * @cfg {String} ddGroup The DD group this TreePanel belongs to
34358  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
34359  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
34360  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
34361  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
34362  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
34363  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
34364  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
34365  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
34366  * @cfg {Boolean} loader A TreeLoader for use with this TreePanel
34367  * @cfg {Object|Roo.tree.TreeEditor} editor The TreeEditor or xtype data to display when clicked.
34368  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
34369  * @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>
34370  * @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>
34371  * 
34372  * @constructor
34373  * @param {String/HTMLElement/Element} el The container element
34374  * @param {Object} config
34375  */
34376 Roo.tree.TreePanel = function(el, config){
34377     var root = false;
34378     var loader = false;
34379     if (config.root) {
34380         root = config.root;
34381         delete config.root;
34382     }
34383     if (config.loader) {
34384         loader = config.loader;
34385         delete config.loader;
34386     }
34387     
34388     Roo.apply(this, config);
34389     Roo.tree.TreePanel.superclass.constructor.call(this);
34390     this.el = Roo.get(el);
34391     this.el.addClass('x-tree');
34392     //console.log(root);
34393     if (root) {
34394         this.setRootNode( Roo.factory(root, Roo.tree));
34395     }
34396     if (loader) {
34397         this.loader = Roo.factory(loader, Roo.tree);
34398     }
34399    /**
34400     * Read-only. The id of the container element becomes this TreePanel's id.
34401     */
34402     this.id = this.el.id;
34403     this.addEvents({
34404         /**
34405         * @event beforeload
34406         * Fires before a node is loaded, return false to cancel
34407         * @param {Node} node The node being loaded
34408         */
34409         "beforeload" : true,
34410         /**
34411         * @event load
34412         * Fires when a node is loaded
34413         * @param {Node} node The node that was loaded
34414         */
34415         "load" : true,
34416         /**
34417         * @event textchange
34418         * Fires when the text for a node is changed
34419         * @param {Node} node The node
34420         * @param {String} text The new text
34421         * @param {String} oldText The old text
34422         */
34423         "textchange" : true,
34424         /**
34425         * @event beforeexpand
34426         * Fires before a node is expanded, return false to cancel.
34427         * @param {Node} node The node
34428         * @param {Boolean} deep
34429         * @param {Boolean} anim
34430         */
34431         "beforeexpand" : true,
34432         /**
34433         * @event beforecollapse
34434         * Fires before a node is collapsed, return false to cancel.
34435         * @param {Node} node The node
34436         * @param {Boolean} deep
34437         * @param {Boolean} anim
34438         */
34439         "beforecollapse" : true,
34440         /**
34441         * @event expand
34442         * Fires when a node is expanded
34443         * @param {Node} node The node
34444         */
34445         "expand" : true,
34446         /**
34447         * @event disabledchange
34448         * Fires when the disabled status of a node changes
34449         * @param {Node} node The node
34450         * @param {Boolean} disabled
34451         */
34452         "disabledchange" : true,
34453         /**
34454         * @event collapse
34455         * Fires when a node is collapsed
34456         * @param {Node} node The node
34457         */
34458         "collapse" : true,
34459         /**
34460         * @event beforeclick
34461         * Fires before click processing on a node. Return false to cancel the default action.
34462         * @param {Node} node The node
34463         * @param {Roo.EventObject} e The event object
34464         */
34465         "beforeclick":true,
34466         /**
34467         * @event checkchange
34468         * Fires when a node with a checkbox's checked property changes
34469         * @param {Node} this This node
34470         * @param {Boolean} checked
34471         */
34472         "checkchange":true,
34473         /**
34474         * @event click
34475         * Fires when a node is clicked
34476         * @param {Node} node The node
34477         * @param {Roo.EventObject} e The event object
34478         */
34479         "click":true,
34480         /**
34481         * @event dblclick
34482         * Fires when a node is double clicked
34483         * @param {Node} node The node
34484         * @param {Roo.EventObject} e The event object
34485         */
34486         "dblclick":true,
34487         /**
34488         * @event contextmenu
34489         * Fires when a node is right clicked
34490         * @param {Node} node The node
34491         * @param {Roo.EventObject} e The event object
34492         */
34493         "contextmenu":true,
34494         /**
34495         * @event beforechildrenrendered
34496         * Fires right before the child nodes for a node are rendered
34497         * @param {Node} node The node
34498         */
34499         "beforechildrenrendered":true,
34500         /**
34501         * @event startdrag
34502         * Fires when a node starts being dragged
34503         * @param {Roo.tree.TreePanel} this
34504         * @param {Roo.tree.TreeNode} node
34505         * @param {event} e The raw browser event
34506         */ 
34507        "startdrag" : true,
34508        /**
34509         * @event enddrag
34510         * Fires when a drag operation is complete
34511         * @param {Roo.tree.TreePanel} this
34512         * @param {Roo.tree.TreeNode} node
34513         * @param {event} e The raw browser event
34514         */
34515        "enddrag" : true,
34516        /**
34517         * @event dragdrop
34518         * Fires when a dragged node is dropped on a valid DD target
34519         * @param {Roo.tree.TreePanel} this
34520         * @param {Roo.tree.TreeNode} node
34521         * @param {DD} dd The dd it was dropped on
34522         * @param {event} e The raw browser event
34523         */
34524        "dragdrop" : true,
34525        /**
34526         * @event beforenodedrop
34527         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
34528         * passed to handlers has the following properties:<br />
34529         * <ul style="padding:5px;padding-left:16px;">
34530         * <li>tree - The TreePanel</li>
34531         * <li>target - The node being targeted for the drop</li>
34532         * <li>data - The drag data from the drag source</li>
34533         * <li>point - The point of the drop - append, above or below</li>
34534         * <li>source - The drag source</li>
34535         * <li>rawEvent - Raw mouse event</li>
34536         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
34537         * to be inserted by setting them on this object.</li>
34538         * <li>cancel - Set this to true to cancel the drop.</li>
34539         * </ul>
34540         * @param {Object} dropEvent
34541         */
34542        "beforenodedrop" : true,
34543        /**
34544         * @event nodedrop
34545         * Fires after a DD object is dropped on a node in this tree. The dropEvent
34546         * passed to handlers has the following properties:<br />
34547         * <ul style="padding:5px;padding-left:16px;">
34548         * <li>tree - The TreePanel</li>
34549         * <li>target - The node being targeted for the drop</li>
34550         * <li>data - The drag data from the drag source</li>
34551         * <li>point - The point of the drop - append, above or below</li>
34552         * <li>source - The drag source</li>
34553         * <li>rawEvent - Raw mouse event</li>
34554         * <li>dropNode - Dropped node(s).</li>
34555         * </ul>
34556         * @param {Object} dropEvent
34557         */
34558        "nodedrop" : true,
34559         /**
34560         * @event nodedragover
34561         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
34562         * passed to handlers has the following properties:<br />
34563         * <ul style="padding:5px;padding-left:16px;">
34564         * <li>tree - The TreePanel</li>
34565         * <li>target - The node being targeted for the drop</li>
34566         * <li>data - The drag data from the drag source</li>
34567         * <li>point - The point of the drop - append, above or below</li>
34568         * <li>source - The drag source</li>
34569         * <li>rawEvent - Raw mouse event</li>
34570         * <li>dropNode - Drop node(s) provided by the source.</li>
34571         * <li>cancel - Set this to true to signal drop not allowed.</li>
34572         * </ul>
34573         * @param {Object} dragOverEvent
34574         */
34575        "nodedragover" : true,
34576        /**
34577         * @event appendnode
34578         * Fires when append node to the tree
34579         * @param {Roo.tree.TreePanel} this
34580         * @param {Roo.tree.TreeNode} node
34581         * @param {Number} index The index of the newly appended node
34582         */
34583        "appendnode" : true
34584         
34585     });
34586     if(this.singleExpand){
34587        this.on("beforeexpand", this.restrictExpand, this);
34588     }
34589     if (this.editor) {
34590         this.editor.tree = this;
34591         this.editor = Roo.factory(this.editor, Roo.tree);
34592     }
34593     
34594     if (this.selModel) {
34595         this.selModel = Roo.factory(this.selModel, Roo.tree);
34596     }
34597    
34598 };
34599 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
34600     rootVisible : true,
34601     animate: Roo.enableFx,
34602     lines : true,
34603     enableDD : false,
34604     hlDrop : Roo.enableFx,
34605   
34606     renderer: false,
34607     
34608     rendererTip: false,
34609     // private
34610     restrictExpand : function(node){
34611         var p = node.parentNode;
34612         if(p){
34613             if(p.expandedChild && p.expandedChild.parentNode == p){
34614                 p.expandedChild.collapse();
34615             }
34616             p.expandedChild = node;
34617         }
34618     },
34619
34620     // private override
34621     setRootNode : function(node){
34622         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
34623         if(!this.rootVisible){
34624             node.ui = new Roo.tree.RootTreeNodeUI(node);
34625         }
34626         return node;
34627     },
34628
34629     /**
34630      * Returns the container element for this TreePanel
34631      */
34632     getEl : function(){
34633         return this.el;
34634     },
34635
34636     /**
34637      * Returns the default TreeLoader for this TreePanel
34638      */
34639     getLoader : function(){
34640         return this.loader;
34641     },
34642
34643     /**
34644      * Expand all nodes
34645      */
34646     expandAll : function(){
34647         this.root.expand(true);
34648     },
34649
34650     /**
34651      * Collapse all nodes
34652      */
34653     collapseAll : function(){
34654         this.root.collapse(true);
34655     },
34656
34657     /**
34658      * Returns the selection model used by this TreePanel
34659      */
34660     getSelectionModel : function(){
34661         if(!this.selModel){
34662             this.selModel = new Roo.tree.DefaultSelectionModel();
34663         }
34664         return this.selModel;
34665     },
34666
34667     /**
34668      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
34669      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
34670      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
34671      * @return {Array}
34672      */
34673     getChecked : function(a, startNode){
34674         startNode = startNode || this.root;
34675         var r = [];
34676         var f = function(){
34677             if(this.attributes.checked){
34678                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
34679             }
34680         }
34681         startNode.cascade(f);
34682         return r;
34683     },
34684
34685     /**
34686      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34687      * @param {String} path
34688      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34689      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
34690      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
34691      */
34692     expandPath : function(path, attr, callback){
34693         attr = attr || "id";
34694         var keys = path.split(this.pathSeparator);
34695         var curNode = this.root;
34696         if(curNode.attributes[attr] != keys[1]){ // invalid root
34697             if(callback){
34698                 callback(false, null);
34699             }
34700             return;
34701         }
34702         var index = 1;
34703         var f = function(){
34704             if(++index == keys.length){
34705                 if(callback){
34706                     callback(true, curNode);
34707                 }
34708                 return;
34709             }
34710             var c = curNode.findChild(attr, keys[index]);
34711             if(!c){
34712                 if(callback){
34713                     callback(false, curNode);
34714                 }
34715                 return;
34716             }
34717             curNode = c;
34718             c.expand(false, false, f);
34719         };
34720         curNode.expand(false, false, f);
34721     },
34722
34723     /**
34724      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
34725      * @param {String} path
34726      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
34727      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
34728      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
34729      */
34730     selectPath : function(path, attr, callback){
34731         attr = attr || "id";
34732         var keys = path.split(this.pathSeparator);
34733         var v = keys.pop();
34734         if(keys.length > 0){
34735             var f = function(success, node){
34736                 if(success && node){
34737                     var n = node.findChild(attr, v);
34738                     if(n){
34739                         n.select();
34740                         if(callback){
34741                             callback(true, n);
34742                         }
34743                     }else if(callback){
34744                         callback(false, n);
34745                     }
34746                 }else{
34747                     if(callback){
34748                         callback(false, n);
34749                     }
34750                 }
34751             };
34752             this.expandPath(keys.join(this.pathSeparator), attr, f);
34753         }else{
34754             this.root.select();
34755             if(callback){
34756                 callback(true, this.root);
34757             }
34758         }
34759     },
34760
34761     getTreeEl : function(){
34762         return this.el;
34763     },
34764
34765     /**
34766      * Trigger rendering of this TreePanel
34767      */
34768     render : function(){
34769         if (this.innerCt) {
34770             return this; // stop it rendering more than once!!
34771         }
34772         
34773         this.innerCt = this.el.createChild({tag:"ul",
34774                cls:"x-tree-root-ct " +
34775                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
34776
34777         if(this.containerScroll){
34778             Roo.dd.ScrollManager.register(this.el);
34779         }
34780         if((this.enableDD || this.enableDrop) && !this.dropZone){
34781            /**
34782             * The dropZone used by this tree if drop is enabled
34783             * @type Roo.tree.TreeDropZone
34784             */
34785              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
34786                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
34787            });
34788         }
34789         if((this.enableDD || this.enableDrag) && !this.dragZone){
34790            /**
34791             * The dragZone used by this tree if drag is enabled
34792             * @type Roo.tree.TreeDragZone
34793             */
34794             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
34795                ddGroup: this.ddGroup || "TreeDD",
34796                scroll: this.ddScroll
34797            });
34798         }
34799         this.getSelectionModel().init(this);
34800         if (!this.root) {
34801             Roo.log("ROOT not set in tree");
34802             return this;
34803         }
34804         this.root.render();
34805         if(!this.rootVisible){
34806             this.root.renderChildren();
34807         }
34808         return this;
34809     }
34810 });/*
34811  * Based on:
34812  * Ext JS Library 1.1.1
34813  * Copyright(c) 2006-2007, Ext JS, LLC.
34814  *
34815  * Originally Released Under LGPL - original licence link has changed is not relivant.
34816  *
34817  * Fork - LGPL
34818  * <script type="text/javascript">
34819  */
34820  
34821
34822 /**
34823  * @class Roo.tree.DefaultSelectionModel
34824  * @extends Roo.util.Observable
34825  * The default single selection for a TreePanel.
34826  * @param {Object} cfg Configuration
34827  */
34828 Roo.tree.DefaultSelectionModel = function(cfg){
34829    this.selNode = null;
34830    
34831    
34832    
34833    this.addEvents({
34834        /**
34835         * @event selectionchange
34836         * Fires when the selected node changes
34837         * @param {DefaultSelectionModel} this
34838         * @param {TreeNode} node the new selection
34839         */
34840        "selectionchange" : true,
34841
34842        /**
34843         * @event beforeselect
34844         * Fires before the selected node changes, return false to cancel the change
34845         * @param {DefaultSelectionModel} this
34846         * @param {TreeNode} node the new selection
34847         * @param {TreeNode} node the old selection
34848         */
34849        "beforeselect" : true
34850    });
34851    
34852     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
34853 };
34854
34855 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
34856     init : function(tree){
34857         this.tree = tree;
34858         tree.getTreeEl().on("keydown", this.onKeyDown, this);
34859         tree.on("click", this.onNodeClick, this);
34860     },
34861     
34862     onNodeClick : function(node, e){
34863         if (e.ctrlKey && this.selNode == node)  {
34864             this.unselect(node);
34865             return;
34866         }
34867         this.select(node);
34868     },
34869     
34870     /**
34871      * Select a node.
34872      * @param {TreeNode} node The node to select
34873      * @return {TreeNode} The selected node
34874      */
34875     select : function(node){
34876         var last = this.selNode;
34877         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
34878             if(last){
34879                 last.ui.onSelectedChange(false);
34880             }
34881             this.selNode = node;
34882             node.ui.onSelectedChange(true);
34883             this.fireEvent("selectionchange", this, node, last);
34884         }
34885         return node;
34886     },
34887     
34888     /**
34889      * Deselect a node.
34890      * @param {TreeNode} node The node to unselect
34891      */
34892     unselect : function(node){
34893         if(this.selNode == node){
34894             this.clearSelections();
34895         }    
34896     },
34897     
34898     /**
34899      * Clear all selections
34900      */
34901     clearSelections : function(){
34902         var n = this.selNode;
34903         if(n){
34904             n.ui.onSelectedChange(false);
34905             this.selNode = null;
34906             this.fireEvent("selectionchange", this, null);
34907         }
34908         return n;
34909     },
34910     
34911     /**
34912      * Get the selected node
34913      * @return {TreeNode} The selected node
34914      */
34915     getSelectedNode : function(){
34916         return this.selNode;    
34917     },
34918     
34919     /**
34920      * Returns true if the node is selected
34921      * @param {TreeNode} node The node to check
34922      * @return {Boolean}
34923      */
34924     isSelected : function(node){
34925         return this.selNode == node;  
34926     },
34927
34928     /**
34929      * Selects the node above the selected node in the tree, intelligently walking the nodes
34930      * @return TreeNode The new selection
34931      */
34932     selectPrevious : function(){
34933         var s = this.selNode || this.lastSelNode;
34934         if(!s){
34935             return null;
34936         }
34937         var ps = s.previousSibling;
34938         if(ps){
34939             if(!ps.isExpanded() || ps.childNodes.length < 1){
34940                 return this.select(ps);
34941             } else{
34942                 var lc = ps.lastChild;
34943                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
34944                     lc = lc.lastChild;
34945                 }
34946                 return this.select(lc);
34947             }
34948         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
34949             return this.select(s.parentNode);
34950         }
34951         return null;
34952     },
34953
34954     /**
34955      * Selects the node above the selected node in the tree, intelligently walking the nodes
34956      * @return TreeNode The new selection
34957      */
34958     selectNext : function(){
34959         var s = this.selNode || this.lastSelNode;
34960         if(!s){
34961             return null;
34962         }
34963         if(s.firstChild && s.isExpanded()){
34964              return this.select(s.firstChild);
34965          }else if(s.nextSibling){
34966              return this.select(s.nextSibling);
34967          }else if(s.parentNode){
34968             var newS = null;
34969             s.parentNode.bubble(function(){
34970                 if(this.nextSibling){
34971                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
34972                     return false;
34973                 }
34974             });
34975             return newS;
34976          }
34977         return null;
34978     },
34979
34980     onKeyDown : function(e){
34981         var s = this.selNode || this.lastSelNode;
34982         // undesirable, but required
34983         var sm = this;
34984         if(!s){
34985             return;
34986         }
34987         var k = e.getKey();
34988         switch(k){
34989              case e.DOWN:
34990                  e.stopEvent();
34991                  this.selectNext();
34992              break;
34993              case e.UP:
34994                  e.stopEvent();
34995                  this.selectPrevious();
34996              break;
34997              case e.RIGHT:
34998                  e.preventDefault();
34999                  if(s.hasChildNodes()){
35000                      if(!s.isExpanded()){
35001                          s.expand();
35002                      }else if(s.firstChild){
35003                          this.select(s.firstChild, e);
35004                      }
35005                  }
35006              break;
35007              case e.LEFT:
35008                  e.preventDefault();
35009                  if(s.hasChildNodes() && s.isExpanded()){
35010                      s.collapse();
35011                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35012                      this.select(s.parentNode, e);
35013                  }
35014              break;
35015         };
35016     }
35017 });
35018
35019 /**
35020  * @class Roo.tree.MultiSelectionModel
35021  * @extends Roo.util.Observable
35022  * Multi selection for a TreePanel.
35023  * @param {Object} cfg Configuration
35024  */
35025 Roo.tree.MultiSelectionModel = function(){
35026    this.selNodes = [];
35027    this.selMap = {};
35028    this.addEvents({
35029        /**
35030         * @event selectionchange
35031         * Fires when the selected nodes change
35032         * @param {MultiSelectionModel} this
35033         * @param {Array} nodes Array of the selected nodes
35034         */
35035        "selectionchange" : true
35036    });
35037    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35038    
35039 };
35040
35041 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35042     init : function(tree){
35043         this.tree = tree;
35044         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35045         tree.on("click", this.onNodeClick, this);
35046     },
35047     
35048     onNodeClick : function(node, e){
35049         this.select(node, e, e.ctrlKey);
35050     },
35051     
35052     /**
35053      * Select a node.
35054      * @param {TreeNode} node The node to select
35055      * @param {EventObject} e (optional) An event associated with the selection
35056      * @param {Boolean} keepExisting True to retain existing selections
35057      * @return {TreeNode} The selected node
35058      */
35059     select : function(node, e, keepExisting){
35060         if(keepExisting !== true){
35061             this.clearSelections(true);
35062         }
35063         if(this.isSelected(node)){
35064             this.lastSelNode = node;
35065             return node;
35066         }
35067         this.selNodes.push(node);
35068         this.selMap[node.id] = node;
35069         this.lastSelNode = node;
35070         node.ui.onSelectedChange(true);
35071         this.fireEvent("selectionchange", this, this.selNodes);
35072         return node;
35073     },
35074     
35075     /**
35076      * Deselect a node.
35077      * @param {TreeNode} node The node to unselect
35078      */
35079     unselect : function(node){
35080         if(this.selMap[node.id]){
35081             node.ui.onSelectedChange(false);
35082             var sn = this.selNodes;
35083             var index = -1;
35084             if(sn.indexOf){
35085                 index = sn.indexOf(node);
35086             }else{
35087                 for(var i = 0, len = sn.length; i < len; i++){
35088                     if(sn[i] == node){
35089                         index = i;
35090                         break;
35091                     }
35092                 }
35093             }
35094             if(index != -1){
35095                 this.selNodes.splice(index, 1);
35096             }
35097             delete this.selMap[node.id];
35098             this.fireEvent("selectionchange", this, this.selNodes);
35099         }
35100     },
35101     
35102     /**
35103      * Clear all selections
35104      */
35105     clearSelections : function(suppressEvent){
35106         var sn = this.selNodes;
35107         if(sn.length > 0){
35108             for(var i = 0, len = sn.length; i < len; i++){
35109                 sn[i].ui.onSelectedChange(false);
35110             }
35111             this.selNodes = [];
35112             this.selMap = {};
35113             if(suppressEvent !== true){
35114                 this.fireEvent("selectionchange", this, this.selNodes);
35115             }
35116         }
35117     },
35118     
35119     /**
35120      * Returns true if the node is selected
35121      * @param {TreeNode} node The node to check
35122      * @return {Boolean}
35123      */
35124     isSelected : function(node){
35125         return this.selMap[node.id] ? true : false;  
35126     },
35127     
35128     /**
35129      * Returns an array of the selected nodes
35130      * @return {Array}
35131      */
35132     getSelectedNodes : function(){
35133         return this.selNodes;    
35134     },
35135
35136     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35137
35138     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35139
35140     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35141 });/*
35142  * Based on:
35143  * Ext JS Library 1.1.1
35144  * Copyright(c) 2006-2007, Ext JS, LLC.
35145  *
35146  * Originally Released Under LGPL - original licence link has changed is not relivant.
35147  *
35148  * Fork - LGPL
35149  * <script type="text/javascript">
35150  */
35151  
35152 /**
35153  * @class Roo.tree.TreeNode
35154  * @extends Roo.data.Node
35155  * @cfg {String} text The text for this node
35156  * @cfg {Boolean} expanded true to start the node expanded
35157  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35158  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35159  * @cfg {Boolean} disabled true to start the node disabled
35160  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35161  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35162  * @cfg {String} cls A css class to be added to the node
35163  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35164  * @cfg {String} href URL of the link used for the node (defaults to #)
35165  * @cfg {String} hrefTarget target frame for the link
35166  * @cfg {String} qtip An Ext QuickTip for the node
35167  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35168  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35169  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35170  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35171  * (defaults to undefined with no checkbox rendered)
35172  * @constructor
35173  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35174  */
35175 Roo.tree.TreeNode = function(attributes){
35176     attributes = attributes || {};
35177     if(typeof attributes == "string"){
35178         attributes = {text: attributes};
35179     }
35180     this.childrenRendered = false;
35181     this.rendered = false;
35182     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35183     this.expanded = attributes.expanded === true;
35184     this.isTarget = attributes.isTarget !== false;
35185     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35186     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35187
35188     /**
35189      * Read-only. The text for this node. To change it use setText().
35190      * @type String
35191      */
35192     this.text = attributes.text;
35193     /**
35194      * True if this node is disabled.
35195      * @type Boolean
35196      */
35197     this.disabled = attributes.disabled === true;
35198
35199     this.addEvents({
35200         /**
35201         * @event textchange
35202         * Fires when the text for this node is changed
35203         * @param {Node} this This node
35204         * @param {String} text The new text
35205         * @param {String} oldText The old text
35206         */
35207         "textchange" : true,
35208         /**
35209         * @event beforeexpand
35210         * Fires before this node is expanded, return false to cancel.
35211         * @param {Node} this This node
35212         * @param {Boolean} deep
35213         * @param {Boolean} anim
35214         */
35215         "beforeexpand" : true,
35216         /**
35217         * @event beforecollapse
35218         * Fires before this node is collapsed, return false to cancel.
35219         * @param {Node} this This node
35220         * @param {Boolean} deep
35221         * @param {Boolean} anim
35222         */
35223         "beforecollapse" : true,
35224         /**
35225         * @event expand
35226         * Fires when this node is expanded
35227         * @param {Node} this This node
35228         */
35229         "expand" : true,
35230         /**
35231         * @event disabledchange
35232         * Fires when the disabled status of this node changes
35233         * @param {Node} this This node
35234         * @param {Boolean} disabled
35235         */
35236         "disabledchange" : true,
35237         /**
35238         * @event collapse
35239         * Fires when this node is collapsed
35240         * @param {Node} this This node
35241         */
35242         "collapse" : true,
35243         /**
35244         * @event beforeclick
35245         * Fires before click processing. Return false to cancel the default action.
35246         * @param {Node} this This node
35247         * @param {Roo.EventObject} e The event object
35248         */
35249         "beforeclick":true,
35250         /**
35251         * @event checkchange
35252         * Fires when a node with a checkbox's checked property changes
35253         * @param {Node} this This node
35254         * @param {Boolean} checked
35255         */
35256         "checkchange":true,
35257         /**
35258         * @event click
35259         * Fires when this node is clicked
35260         * @param {Node} this This node
35261         * @param {Roo.EventObject} e The event object
35262         */
35263         "click":true,
35264         /**
35265         * @event dblclick
35266         * Fires when this node is double clicked
35267         * @param {Node} this This node
35268         * @param {Roo.EventObject} e The event object
35269         */
35270         "dblclick":true,
35271         /**
35272         * @event contextmenu
35273         * Fires when this node is right clicked
35274         * @param {Node} this This node
35275         * @param {Roo.EventObject} e The event object
35276         */
35277         "contextmenu":true,
35278         /**
35279         * @event beforechildrenrendered
35280         * Fires right before the child nodes for this node are rendered
35281         * @param {Node} this This node
35282         */
35283         "beforechildrenrendered":true
35284     });
35285
35286     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
35287
35288     /**
35289      * Read-only. The UI for this node
35290      * @type TreeNodeUI
35291      */
35292     this.ui = new uiClass(this);
35293     
35294     // finally support items[]
35295     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
35296         return;
35297     }
35298     
35299     
35300     Roo.each(this.attributes.items, function(c) {
35301         this.appendChild(Roo.factory(c,Roo.Tree));
35302     }, this);
35303     delete this.attributes.items;
35304     
35305     
35306     
35307 };
35308 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
35309     preventHScroll: true,
35310     /**
35311      * Returns true if this node is expanded
35312      * @return {Boolean}
35313      */
35314     isExpanded : function(){
35315         return this.expanded;
35316     },
35317
35318     /**
35319      * Returns the UI object for this node
35320      * @return {TreeNodeUI}
35321      */
35322     getUI : function(){
35323         return this.ui;
35324     },
35325
35326     // private override
35327     setFirstChild : function(node){
35328         var of = this.firstChild;
35329         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
35330         if(this.childrenRendered && of && node != of){
35331             of.renderIndent(true, true);
35332         }
35333         if(this.rendered){
35334             this.renderIndent(true, true);
35335         }
35336     },
35337
35338     // private override
35339     setLastChild : function(node){
35340         var ol = this.lastChild;
35341         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
35342         if(this.childrenRendered && ol && node != ol){
35343             ol.renderIndent(true, true);
35344         }
35345         if(this.rendered){
35346             this.renderIndent(true, true);
35347         }
35348     },
35349
35350     // these methods are overridden to provide lazy rendering support
35351     // private override
35352     appendChild : function()
35353     {
35354         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
35355         if(node && this.childrenRendered){
35356             node.render();
35357         }
35358         this.ui.updateExpandIcon();
35359         return node;
35360     },
35361
35362     // private override
35363     removeChild : function(node){
35364         this.ownerTree.getSelectionModel().unselect(node);
35365         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
35366         // if it's been rendered remove dom node
35367         if(this.childrenRendered){
35368             node.ui.remove();
35369         }
35370         if(this.childNodes.length < 1){
35371             this.collapse(false, false);
35372         }else{
35373             this.ui.updateExpandIcon();
35374         }
35375         if(!this.firstChild) {
35376             this.childrenRendered = false;
35377         }
35378         return node;
35379     },
35380
35381     // private override
35382     insertBefore : function(node, refNode){
35383         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
35384         if(newNode && refNode && this.childrenRendered){
35385             node.render();
35386         }
35387         this.ui.updateExpandIcon();
35388         return newNode;
35389     },
35390
35391     /**
35392      * Sets the text for this node
35393      * @param {String} text
35394      */
35395     setText : function(text){
35396         var oldText = this.text;
35397         this.text = text;
35398         this.attributes.text = text;
35399         if(this.rendered){ // event without subscribing
35400             this.ui.onTextChange(this, text, oldText);
35401         }
35402         this.fireEvent("textchange", this, text, oldText);
35403     },
35404
35405     /**
35406      * Triggers selection of this node
35407      */
35408     select : function(){
35409         this.getOwnerTree().getSelectionModel().select(this);
35410     },
35411
35412     /**
35413      * Triggers deselection of this node
35414      */
35415     unselect : function(){
35416         this.getOwnerTree().getSelectionModel().unselect(this);
35417     },
35418
35419     /**
35420      * Returns true if this node is selected
35421      * @return {Boolean}
35422      */
35423     isSelected : function(){
35424         return this.getOwnerTree().getSelectionModel().isSelected(this);
35425     },
35426
35427     /**
35428      * Expand this node.
35429      * @param {Boolean} deep (optional) True to expand all children as well
35430      * @param {Boolean} anim (optional) false to cancel the default animation
35431      * @param {Function} callback (optional) A callback to be called when
35432      * expanding this node completes (does not wait for deep expand to complete).
35433      * Called with 1 parameter, this node.
35434      */
35435     expand : function(deep, anim, callback){
35436         if(!this.expanded){
35437             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
35438                 return;
35439             }
35440             if(!this.childrenRendered){
35441                 this.renderChildren();
35442             }
35443             this.expanded = true;
35444             
35445             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
35446                 this.ui.animExpand(function(){
35447                     this.fireEvent("expand", this);
35448                     if(typeof callback == "function"){
35449                         callback(this);
35450                     }
35451                     if(deep === true){
35452                         this.expandChildNodes(true);
35453                     }
35454                 }.createDelegate(this));
35455                 return;
35456             }else{
35457                 this.ui.expand();
35458                 this.fireEvent("expand", this);
35459                 if(typeof callback == "function"){
35460                     callback(this);
35461                 }
35462             }
35463         }else{
35464            if(typeof callback == "function"){
35465                callback(this);
35466            }
35467         }
35468         if(deep === true){
35469             this.expandChildNodes(true);
35470         }
35471     },
35472
35473     isHiddenRoot : function(){
35474         return this.isRoot && !this.getOwnerTree().rootVisible;
35475     },
35476
35477     /**
35478      * Collapse this node.
35479      * @param {Boolean} deep (optional) True to collapse all children as well
35480      * @param {Boolean} anim (optional) false to cancel the default animation
35481      */
35482     collapse : function(deep, anim){
35483         if(this.expanded && !this.isHiddenRoot()){
35484             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
35485                 return;
35486             }
35487             this.expanded = false;
35488             if((this.getOwnerTree().animate && anim !== false) || anim){
35489                 this.ui.animCollapse(function(){
35490                     this.fireEvent("collapse", this);
35491                     if(deep === true){
35492                         this.collapseChildNodes(true);
35493                     }
35494                 }.createDelegate(this));
35495                 return;
35496             }else{
35497                 this.ui.collapse();
35498                 this.fireEvent("collapse", this);
35499             }
35500         }
35501         if(deep === true){
35502             var cs = this.childNodes;
35503             for(var i = 0, len = cs.length; i < len; i++) {
35504                 cs[i].collapse(true, false);
35505             }
35506         }
35507     },
35508
35509     // private
35510     delayedExpand : function(delay){
35511         if(!this.expandProcId){
35512             this.expandProcId = this.expand.defer(delay, this);
35513         }
35514     },
35515
35516     // private
35517     cancelExpand : function(){
35518         if(this.expandProcId){
35519             clearTimeout(this.expandProcId);
35520         }
35521         this.expandProcId = false;
35522     },
35523
35524     /**
35525      * Toggles expanded/collapsed state of the node
35526      */
35527     toggle : function(){
35528         if(this.expanded){
35529             this.collapse();
35530         }else{
35531             this.expand();
35532         }
35533     },
35534
35535     /**
35536      * Ensures all parent nodes are expanded
35537      */
35538     ensureVisible : function(callback){
35539         var tree = this.getOwnerTree();
35540         tree.expandPath(this.parentNode.getPath(), false, function(){
35541             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
35542             Roo.callback(callback);
35543         }.createDelegate(this));
35544     },
35545
35546     /**
35547      * Expand all child nodes
35548      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
35549      */
35550     expandChildNodes : function(deep){
35551         var cs = this.childNodes;
35552         for(var i = 0, len = cs.length; i < len; i++) {
35553                 cs[i].expand(deep);
35554         }
35555     },
35556
35557     /**
35558      * Collapse all child nodes
35559      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
35560      */
35561     collapseChildNodes : function(deep){
35562         var cs = this.childNodes;
35563         for(var i = 0, len = cs.length; i < len; i++) {
35564                 cs[i].collapse(deep);
35565         }
35566     },
35567
35568     /**
35569      * Disables this node
35570      */
35571     disable : function(){
35572         this.disabled = true;
35573         this.unselect();
35574         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35575             this.ui.onDisableChange(this, true);
35576         }
35577         this.fireEvent("disabledchange", this, true);
35578     },
35579
35580     /**
35581      * Enables this node
35582      */
35583     enable : function(){
35584         this.disabled = false;
35585         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
35586             this.ui.onDisableChange(this, false);
35587         }
35588         this.fireEvent("disabledchange", this, false);
35589     },
35590
35591     // private
35592     renderChildren : function(suppressEvent){
35593         if(suppressEvent !== false){
35594             this.fireEvent("beforechildrenrendered", this);
35595         }
35596         var cs = this.childNodes;
35597         for(var i = 0, len = cs.length; i < len; i++){
35598             cs[i].render(true);
35599         }
35600         this.childrenRendered = true;
35601     },
35602
35603     // private
35604     sort : function(fn, scope){
35605         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
35606         if(this.childrenRendered){
35607             var cs = this.childNodes;
35608             for(var i = 0, len = cs.length; i < len; i++){
35609                 cs[i].render(true);
35610             }
35611         }
35612     },
35613
35614     // private
35615     render : function(bulkRender){
35616         this.ui.render(bulkRender);
35617         if(!this.rendered){
35618             this.rendered = true;
35619             if(this.expanded){
35620                 this.expanded = false;
35621                 this.expand(false, false);
35622             }
35623         }
35624     },
35625
35626     // private
35627     renderIndent : function(deep, refresh){
35628         if(refresh){
35629             this.ui.childIndent = null;
35630         }
35631         this.ui.renderIndent();
35632         if(deep === true && this.childrenRendered){
35633             var cs = this.childNodes;
35634             for(var i = 0, len = cs.length; i < len; i++){
35635                 cs[i].renderIndent(true, refresh);
35636             }
35637         }
35638     }
35639 });/*
35640  * Based on:
35641  * Ext JS Library 1.1.1
35642  * Copyright(c) 2006-2007, Ext JS, LLC.
35643  *
35644  * Originally Released Under LGPL - original licence link has changed is not relivant.
35645  *
35646  * Fork - LGPL
35647  * <script type="text/javascript">
35648  */
35649  
35650 /**
35651  * @class Roo.tree.AsyncTreeNode
35652  * @extends Roo.tree.TreeNode
35653  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
35654  * @constructor
35655  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
35656  */
35657  Roo.tree.AsyncTreeNode = function(config){
35658     this.loaded = false;
35659     this.loading = false;
35660     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
35661     /**
35662     * @event beforeload
35663     * Fires before this node is loaded, return false to cancel
35664     * @param {Node} this This node
35665     */
35666     this.addEvents({'beforeload':true, 'load': true});
35667     /**
35668     * @event load
35669     * Fires when this node is loaded
35670     * @param {Node} this This node
35671     */
35672     /**
35673      * The loader used by this node (defaults to using the tree's defined loader)
35674      * @type TreeLoader
35675      * @property loader
35676      */
35677 };
35678 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
35679     expand : function(deep, anim, callback){
35680         if(this.loading){ // if an async load is already running, waiting til it's done
35681             var timer;
35682             var f = function(){
35683                 if(!this.loading){ // done loading
35684                     clearInterval(timer);
35685                     this.expand(deep, anim, callback);
35686                 }
35687             }.createDelegate(this);
35688             timer = setInterval(f, 200);
35689             return;
35690         }
35691         if(!this.loaded){
35692             if(this.fireEvent("beforeload", this) === false){
35693                 return;
35694             }
35695             this.loading = true;
35696             this.ui.beforeLoad(this);
35697             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
35698             if(loader){
35699                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
35700                 return;
35701             }
35702         }
35703         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
35704     },
35705     
35706     /**
35707      * Returns true if this node is currently loading
35708      * @return {Boolean}
35709      */
35710     isLoading : function(){
35711         return this.loading;  
35712     },
35713     
35714     loadComplete : function(deep, anim, callback){
35715         this.loading = false;
35716         this.loaded = true;
35717         this.ui.afterLoad(this);
35718         this.fireEvent("load", this);
35719         this.expand(deep, anim, callback);
35720     },
35721     
35722     /**
35723      * Returns true if this node has been loaded
35724      * @return {Boolean}
35725      */
35726     isLoaded : function(){
35727         return this.loaded;
35728     },
35729     
35730     hasChildNodes : function(){
35731         if(!this.isLeaf() && !this.loaded){
35732             return true;
35733         }else{
35734             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
35735         }
35736     },
35737
35738     /**
35739      * Trigger a reload for this node
35740      * @param {Function} callback
35741      */
35742     reload : function(callback){
35743         this.collapse(false, false);
35744         while(this.firstChild){
35745             this.removeChild(this.firstChild);
35746         }
35747         this.childrenRendered = false;
35748         this.loaded = false;
35749         if(this.isHiddenRoot()){
35750             this.expanded = false;
35751         }
35752         this.expand(false, false, callback);
35753     }
35754 });/*
35755  * Based on:
35756  * Ext JS Library 1.1.1
35757  * Copyright(c) 2006-2007, Ext JS, LLC.
35758  *
35759  * Originally Released Under LGPL - original licence link has changed is not relivant.
35760  *
35761  * Fork - LGPL
35762  * <script type="text/javascript">
35763  */
35764  
35765 /**
35766  * @class Roo.tree.TreeNodeUI
35767  * @constructor
35768  * @param {Object} node The node to render
35769  * The TreeNode UI implementation is separate from the
35770  * tree implementation. Unless you are customizing the tree UI,
35771  * you should never have to use this directly.
35772  */
35773 Roo.tree.TreeNodeUI = function(node){
35774     this.node = node;
35775     this.rendered = false;
35776     this.animating = false;
35777     this.emptyIcon = Roo.BLANK_IMAGE_URL;
35778 };
35779
35780 Roo.tree.TreeNodeUI.prototype = {
35781     removeChild : function(node){
35782         if(this.rendered){
35783             this.ctNode.removeChild(node.ui.getEl());
35784         }
35785     },
35786
35787     beforeLoad : function(){
35788          this.addClass("x-tree-node-loading");
35789     },
35790
35791     afterLoad : function(){
35792          this.removeClass("x-tree-node-loading");
35793     },
35794
35795     onTextChange : function(node, text, oldText){
35796         if(this.rendered){
35797             this.textNode.innerHTML = text;
35798         }
35799     },
35800
35801     onDisableChange : function(node, state){
35802         this.disabled = state;
35803         if(state){
35804             this.addClass("x-tree-node-disabled");
35805         }else{
35806             this.removeClass("x-tree-node-disabled");
35807         }
35808     },
35809
35810     onSelectedChange : function(state){
35811         if(state){
35812             this.focus();
35813             this.addClass("x-tree-selected");
35814         }else{
35815             //this.blur();
35816             this.removeClass("x-tree-selected");
35817         }
35818     },
35819
35820     onMove : function(tree, node, oldParent, newParent, index, refNode){
35821         this.childIndent = null;
35822         if(this.rendered){
35823             var targetNode = newParent.ui.getContainer();
35824             if(!targetNode){//target not rendered
35825                 this.holder = document.createElement("div");
35826                 this.holder.appendChild(this.wrap);
35827                 return;
35828             }
35829             var insertBefore = refNode ? refNode.ui.getEl() : null;
35830             if(insertBefore){
35831                 targetNode.insertBefore(this.wrap, insertBefore);
35832             }else{
35833                 targetNode.appendChild(this.wrap);
35834             }
35835             this.node.renderIndent(true);
35836         }
35837     },
35838
35839     addClass : function(cls){
35840         if(this.elNode){
35841             Roo.fly(this.elNode).addClass(cls);
35842         }
35843     },
35844
35845     removeClass : function(cls){
35846         if(this.elNode){
35847             Roo.fly(this.elNode).removeClass(cls);
35848         }
35849     },
35850
35851     remove : function(){
35852         if(this.rendered){
35853             this.holder = document.createElement("div");
35854             this.holder.appendChild(this.wrap);
35855         }
35856     },
35857
35858     fireEvent : function(){
35859         return this.node.fireEvent.apply(this.node, arguments);
35860     },
35861
35862     initEvents : function(){
35863         this.node.on("move", this.onMove, this);
35864         var E = Roo.EventManager;
35865         var a = this.anchor;
35866
35867         var el = Roo.fly(a, '_treeui');
35868
35869         if(Roo.isOpera){ // opera render bug ignores the CSS
35870             el.setStyle("text-decoration", "none");
35871         }
35872
35873         el.on("click", this.onClick, this);
35874         el.on("dblclick", this.onDblClick, this);
35875
35876         if(this.checkbox){
35877             Roo.EventManager.on(this.checkbox,
35878                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
35879         }
35880
35881         el.on("contextmenu", this.onContextMenu, this);
35882
35883         var icon = Roo.fly(this.iconNode);
35884         icon.on("click", this.onClick, this);
35885         icon.on("dblclick", this.onDblClick, this);
35886         icon.on("contextmenu", this.onContextMenu, this);
35887         E.on(this.ecNode, "click", this.ecClick, this, true);
35888
35889         if(this.node.disabled){
35890             this.addClass("x-tree-node-disabled");
35891         }
35892         if(this.node.hidden){
35893             this.addClass("x-tree-node-disabled");
35894         }
35895         var ot = this.node.getOwnerTree();
35896         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
35897         if(dd && (!this.node.isRoot || ot.rootVisible)){
35898             Roo.dd.Registry.register(this.elNode, {
35899                 node: this.node,
35900                 handles: this.getDDHandles(),
35901                 isHandle: false
35902             });
35903         }
35904     },
35905
35906     getDDHandles : function(){
35907         return [this.iconNode, this.textNode];
35908     },
35909
35910     hide : function(){
35911         if(this.rendered){
35912             this.wrap.style.display = "none";
35913         }
35914     },
35915
35916     show : function(){
35917         if(this.rendered){
35918             this.wrap.style.display = "";
35919         }
35920     },
35921
35922     onContextMenu : function(e){
35923         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
35924             e.preventDefault();
35925             this.focus();
35926             this.fireEvent("contextmenu", this.node, e);
35927         }
35928     },
35929
35930     onClick : function(e){
35931         if(this.dropping){
35932             e.stopEvent();
35933             return;
35934         }
35935         if(this.fireEvent("beforeclick", this.node, e) !== false){
35936             if(!this.disabled && this.node.attributes.href){
35937                 this.fireEvent("click", this.node, e);
35938                 return;
35939             }
35940             e.preventDefault();
35941             if(this.disabled){
35942                 return;
35943             }
35944
35945             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
35946                 this.node.toggle();
35947             }
35948
35949             this.fireEvent("click", this.node, e);
35950         }else{
35951             e.stopEvent();
35952         }
35953     },
35954
35955     onDblClick : function(e){
35956         e.preventDefault();
35957         if(this.disabled){
35958             return;
35959         }
35960         if(this.checkbox){
35961             this.toggleCheck();
35962         }
35963         if(!this.animating && this.node.hasChildNodes()){
35964             this.node.toggle();
35965         }
35966         this.fireEvent("dblclick", this.node, e);
35967     },
35968
35969     onCheckChange : function(){
35970         var checked = this.checkbox.checked;
35971         this.node.attributes.checked = checked;
35972         this.fireEvent('checkchange', this.node, checked);
35973     },
35974
35975     ecClick : function(e){
35976         if(!this.animating && this.node.hasChildNodes()){
35977             this.node.toggle();
35978         }
35979     },
35980
35981     startDrop : function(){
35982         this.dropping = true;
35983     },
35984
35985     // delayed drop so the click event doesn't get fired on a drop
35986     endDrop : function(){
35987        setTimeout(function(){
35988            this.dropping = false;
35989        }.createDelegate(this), 50);
35990     },
35991
35992     expand : function(){
35993         this.updateExpandIcon();
35994         this.ctNode.style.display = "";
35995     },
35996
35997     focus : function(){
35998         if(!this.node.preventHScroll){
35999             try{this.anchor.focus();
36000             }catch(e){}
36001         }else if(!Roo.isIE){
36002             try{
36003                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36004                 var l = noscroll.scrollLeft;
36005                 this.anchor.focus();
36006                 noscroll.scrollLeft = l;
36007             }catch(e){}
36008         }
36009     },
36010
36011     toggleCheck : function(value){
36012         var cb = this.checkbox;
36013         if(cb){
36014             cb.checked = (value === undefined ? !cb.checked : value);
36015         }
36016     },
36017
36018     blur : function(){
36019         try{
36020             this.anchor.blur();
36021         }catch(e){}
36022     },
36023
36024     animExpand : function(callback){
36025         var ct = Roo.get(this.ctNode);
36026         ct.stopFx();
36027         if(!this.node.hasChildNodes()){
36028             this.updateExpandIcon();
36029             this.ctNode.style.display = "";
36030             Roo.callback(callback);
36031             return;
36032         }
36033         this.animating = true;
36034         this.updateExpandIcon();
36035
36036         ct.slideIn('t', {
36037            callback : function(){
36038                this.animating = false;
36039                Roo.callback(callback);
36040             },
36041             scope: this,
36042             duration: this.node.ownerTree.duration || .25
36043         });
36044     },
36045
36046     highlight : function(){
36047         var tree = this.node.getOwnerTree();
36048         Roo.fly(this.wrap).highlight(
36049             tree.hlColor || "C3DAF9",
36050             {endColor: tree.hlBaseColor}
36051         );
36052     },
36053
36054     collapse : function(){
36055         this.updateExpandIcon();
36056         this.ctNode.style.display = "none";
36057     },
36058
36059     animCollapse : function(callback){
36060         var ct = Roo.get(this.ctNode);
36061         ct.enableDisplayMode('block');
36062         ct.stopFx();
36063
36064         this.animating = true;
36065         this.updateExpandIcon();
36066
36067         ct.slideOut('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     getContainer : function(){
36078         return this.ctNode;
36079     },
36080
36081     getEl : function(){
36082         return this.wrap;
36083     },
36084
36085     appendDDGhost : function(ghostNode){
36086         ghostNode.appendChild(this.elNode.cloneNode(true));
36087     },
36088
36089     getDDRepairXY : function(){
36090         return Roo.lib.Dom.getXY(this.iconNode);
36091     },
36092
36093     onRender : function(){
36094         this.render();
36095     },
36096
36097     render : function(bulkRender){
36098         var n = this.node, a = n.attributes;
36099         var targetNode = n.parentNode ?
36100               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36101
36102         if(!this.rendered){
36103             this.rendered = true;
36104
36105             this.renderElements(n, a, targetNode, bulkRender);
36106
36107             if(a.qtip){
36108                if(this.textNode.setAttributeNS){
36109                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36110                    if(a.qtipTitle){
36111                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36112                    }
36113                }else{
36114                    this.textNode.setAttribute("ext:qtip", a.qtip);
36115                    if(a.qtipTitle){
36116                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36117                    }
36118                }
36119             }else if(a.qtipCfg){
36120                 a.qtipCfg.target = Roo.id(this.textNode);
36121                 Roo.QuickTips.register(a.qtipCfg);
36122             }
36123             this.initEvents();
36124             if(!this.node.expanded){
36125                 this.updateExpandIcon();
36126             }
36127         }else{
36128             if(bulkRender === true) {
36129                 targetNode.appendChild(this.wrap);
36130             }
36131         }
36132     },
36133
36134     renderElements : function(n, a, targetNode, bulkRender)
36135     {
36136         // add some indent caching, this helps performance when rendering a large tree
36137         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36138         var t = n.getOwnerTree();
36139         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36140         if (typeof(n.attributes.html) != 'undefined') {
36141             txt = n.attributes.html;
36142         }
36143         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36144         var cb = typeof a.checked == 'boolean';
36145         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36146         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36147             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36148             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36149             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36150             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36151             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36152              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36153                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36154             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36155             "</li>"];
36156
36157         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36158             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36159                                 n.nextSibling.ui.getEl(), buf.join(""));
36160         }else{
36161             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36162         }
36163
36164         this.elNode = this.wrap.childNodes[0];
36165         this.ctNode = this.wrap.childNodes[1];
36166         var cs = this.elNode.childNodes;
36167         this.indentNode = cs[0];
36168         this.ecNode = cs[1];
36169         this.iconNode = cs[2];
36170         var index = 3;
36171         if(cb){
36172             this.checkbox = cs[3];
36173             index++;
36174         }
36175         this.anchor = cs[index];
36176         this.textNode = cs[index].firstChild;
36177     },
36178
36179     getAnchor : function(){
36180         return this.anchor;
36181     },
36182
36183     getTextEl : function(){
36184         return this.textNode;
36185     },
36186
36187     getIconEl : function(){
36188         return this.iconNode;
36189     },
36190
36191     isChecked : function(){
36192         return this.checkbox ? this.checkbox.checked : false;
36193     },
36194
36195     updateExpandIcon : function(){
36196         if(this.rendered){
36197             var n = this.node, c1, c2;
36198             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36199             var hasChild = n.hasChildNodes();
36200             if(hasChild){
36201                 if(n.expanded){
36202                     cls += "-minus";
36203                     c1 = "x-tree-node-collapsed";
36204                     c2 = "x-tree-node-expanded";
36205                 }else{
36206                     cls += "-plus";
36207                     c1 = "x-tree-node-expanded";
36208                     c2 = "x-tree-node-collapsed";
36209                 }
36210                 if(this.wasLeaf){
36211                     this.removeClass("x-tree-node-leaf");
36212                     this.wasLeaf = false;
36213                 }
36214                 if(this.c1 != c1 || this.c2 != c2){
36215                     Roo.fly(this.elNode).replaceClass(c1, c2);
36216                     this.c1 = c1; this.c2 = c2;
36217                 }
36218             }else{
36219                 // this changes non-leafs into leafs if they have no children.
36220                 // it's not very rational behaviour..
36221                 
36222                 if(!this.wasLeaf && this.node.leaf){
36223                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36224                     delete this.c1;
36225                     delete this.c2;
36226                     this.wasLeaf = true;
36227                 }
36228             }
36229             var ecc = "x-tree-ec-icon "+cls;
36230             if(this.ecc != ecc){
36231                 this.ecNode.className = ecc;
36232                 this.ecc = ecc;
36233             }
36234         }
36235     },
36236
36237     getChildIndent : function(){
36238         if(!this.childIndent){
36239             var buf = [];
36240             var p = this.node;
36241             while(p){
36242                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
36243                     if(!p.isLast()) {
36244                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
36245                     } else {
36246                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
36247                     }
36248                 }
36249                 p = p.parentNode;
36250             }
36251             this.childIndent = buf.join("");
36252         }
36253         return this.childIndent;
36254     },
36255
36256     renderIndent : function(){
36257         if(this.rendered){
36258             var indent = "";
36259             var p = this.node.parentNode;
36260             if(p){
36261                 indent = p.ui.getChildIndent();
36262             }
36263             if(this.indentMarkup != indent){ // don't rerender if not required
36264                 this.indentNode.innerHTML = indent;
36265                 this.indentMarkup = indent;
36266             }
36267             this.updateExpandIcon();
36268         }
36269     }
36270 };
36271
36272 Roo.tree.RootTreeNodeUI = function(){
36273     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
36274 };
36275 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
36276     render : function(){
36277         if(!this.rendered){
36278             var targetNode = this.node.ownerTree.innerCt.dom;
36279             this.node.expanded = true;
36280             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
36281             this.wrap = this.ctNode = targetNode.firstChild;
36282         }
36283     },
36284     collapse : function(){
36285     },
36286     expand : function(){
36287     }
36288 });/*
36289  * Based on:
36290  * Ext JS Library 1.1.1
36291  * Copyright(c) 2006-2007, Ext JS, LLC.
36292  *
36293  * Originally Released Under LGPL - original licence link has changed is not relivant.
36294  *
36295  * Fork - LGPL
36296  * <script type="text/javascript">
36297  */
36298 /**
36299  * @class Roo.tree.TreeLoader
36300  * @extends Roo.util.Observable
36301  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
36302  * nodes from a specified URL. The response must be a javascript Array definition
36303  * who's elements are node definition objects. eg:
36304  * <pre><code>
36305 {  success : true,
36306    data :      [
36307    
36308     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
36309     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
36310     ]
36311 }
36312
36313
36314 </code></pre>
36315  * <br><br>
36316  * The old style respose with just an array is still supported, but not recommended.
36317  * <br><br>
36318  *
36319  * A server request is sent, and child nodes are loaded only when a node is expanded.
36320  * The loading node's id is passed to the server under the parameter name "node" to
36321  * enable the server to produce the correct child nodes.
36322  * <br><br>
36323  * To pass extra parameters, an event handler may be attached to the "beforeload"
36324  * event, and the parameters specified in the TreeLoader's baseParams property:
36325  * <pre><code>
36326     myTreeLoader.on("beforeload", function(treeLoader, node) {
36327         this.baseParams.category = node.attributes.category;
36328     }, this);
36329     
36330 </code></pre>
36331  *
36332  * This would pass an HTTP parameter called "category" to the server containing
36333  * the value of the Node's "category" attribute.
36334  * @constructor
36335  * Creates a new Treeloader.
36336  * @param {Object} config A config object containing config properties.
36337  */
36338 Roo.tree.TreeLoader = function(config){
36339     this.baseParams = {};
36340     this.requestMethod = "POST";
36341     Roo.apply(this, config);
36342
36343     this.addEvents({
36344     
36345         /**
36346          * @event beforeload
36347          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
36348          * @param {Object} This TreeLoader object.
36349          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36350          * @param {Object} callback The callback function specified in the {@link #load} call.
36351          */
36352         beforeload : true,
36353         /**
36354          * @event load
36355          * Fires when the node has been successfuly loaded.
36356          * @param {Object} This TreeLoader object.
36357          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36358          * @param {Object} response The response object containing the data from the server.
36359          */
36360         load : true,
36361         /**
36362          * @event loadexception
36363          * Fires if the network request failed.
36364          * @param {Object} This TreeLoader object.
36365          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
36366          * @param {Object} response The response object containing the data from the server.
36367          */
36368         loadexception : true,
36369         /**
36370          * @event create
36371          * Fires before a node is created, enabling you to return custom Node types 
36372          * @param {Object} This TreeLoader object.
36373          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
36374          */
36375         create : true
36376     });
36377
36378     Roo.tree.TreeLoader.superclass.constructor.call(this);
36379 };
36380
36381 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
36382     /**
36383     * @cfg {String} dataUrl The URL from which to request a Json string which
36384     * specifies an array of node definition object representing the child nodes
36385     * to be loaded.
36386     */
36387     /**
36388     * @cfg {String} requestMethod either GET or POST
36389     * defaults to POST (due to BC)
36390     * to be loaded.
36391     */
36392     /**
36393     * @cfg {Object} baseParams (optional) An object containing properties which
36394     * specify HTTP parameters to be passed to each request for child nodes.
36395     */
36396     /**
36397     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
36398     * created by this loader. If the attributes sent by the server have an attribute in this object,
36399     * they take priority.
36400     */
36401     /**
36402     * @cfg {Object} uiProviders (optional) An object containing properties which
36403     * 
36404     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
36405     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
36406     * <i>uiProvider</i> attribute of a returned child node is a string rather
36407     * than a reference to a TreeNodeUI implementation, this that string value
36408     * is used as a property name in the uiProviders object. You can define the provider named
36409     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
36410     */
36411     uiProviders : {},
36412
36413     /**
36414     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
36415     * child nodes before loading.
36416     */
36417     clearOnLoad : true,
36418
36419     /**
36420     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
36421     * property on loading, rather than expecting an array. (eg. more compatible to a standard
36422     * Grid query { data : [ .....] }
36423     */
36424     
36425     root : false,
36426      /**
36427     * @cfg {String} queryParam (optional) 
36428     * Name of the query as it will be passed on the querystring (defaults to 'node')
36429     * eg. the request will be ?node=[id]
36430     */
36431     
36432     
36433     queryParam: false,
36434     
36435     /**
36436      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
36437      * This is called automatically when a node is expanded, but may be used to reload
36438      * a node (or append new children if the {@link #clearOnLoad} option is false.)
36439      * @param {Roo.tree.TreeNode} node
36440      * @param {Function} callback
36441      */
36442     load : function(node, callback){
36443         if(this.clearOnLoad){
36444             while(node.firstChild){
36445                 node.removeChild(node.firstChild);
36446             }
36447         }
36448         if(node.attributes.children){ // preloaded json children
36449             var cs = node.attributes.children;
36450             for(var i = 0, len = cs.length; i < len; i++){
36451                 node.appendChild(this.createNode(cs[i]));
36452             }
36453             if(typeof callback == "function"){
36454                 callback();
36455             }
36456         }else if(this.dataUrl){
36457             this.requestData(node, callback);
36458         }
36459     },
36460
36461     getParams: function(node){
36462         var buf = [], bp = this.baseParams;
36463         for(var key in bp){
36464             if(typeof bp[key] != "function"){
36465                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
36466             }
36467         }
36468         var n = this.queryParam === false ? 'node' : this.queryParam;
36469         buf.push(n + "=", encodeURIComponent(node.id));
36470         return buf.join("");
36471     },
36472
36473     requestData : function(node, callback){
36474         if(this.fireEvent("beforeload", this, node, callback) !== false){
36475             this.transId = Roo.Ajax.request({
36476                 method:this.requestMethod,
36477                 url: this.dataUrl||this.url,
36478                 success: this.handleResponse,
36479                 failure: this.handleFailure,
36480                 scope: this,
36481                 argument: {callback: callback, node: node},
36482                 params: this.getParams(node)
36483             });
36484         }else{
36485             // if the load is cancelled, make sure we notify
36486             // the node that we are done
36487             if(typeof callback == "function"){
36488                 callback();
36489             }
36490         }
36491     },
36492
36493     isLoading : function(){
36494         return this.transId ? true : false;
36495     },
36496
36497     abort : function(){
36498         if(this.isLoading()){
36499             Roo.Ajax.abort(this.transId);
36500         }
36501     },
36502
36503     // private
36504     createNode : function(attr)
36505     {
36506         // apply baseAttrs, nice idea Corey!
36507         if(this.baseAttrs){
36508             Roo.applyIf(attr, this.baseAttrs);
36509         }
36510         if(this.applyLoader !== false){
36511             attr.loader = this;
36512         }
36513         // uiProvider = depreciated..
36514         
36515         if(typeof(attr.uiProvider) == 'string'){
36516            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
36517                 /**  eval:var:attr */ eval(attr.uiProvider);
36518         }
36519         if(typeof(this.uiProviders['default']) != 'undefined') {
36520             attr.uiProvider = this.uiProviders['default'];
36521         }
36522         
36523         this.fireEvent('create', this, attr);
36524         
36525         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
36526         return(attr.leaf ?
36527                         new Roo.tree.TreeNode(attr) :
36528                         new Roo.tree.AsyncTreeNode(attr));
36529     },
36530
36531     processResponse : function(response, node, callback)
36532     {
36533         var json = response.responseText;
36534         try {
36535             
36536             var o = Roo.decode(json);
36537             
36538             if (this.root === false && typeof(o.success) != undefined) {
36539                 this.root = 'data'; // the default behaviour for list like data..
36540                 }
36541                 
36542             if (this.root !== false &&  !o.success) {
36543                 // it's a failure condition.
36544                 var a = response.argument;
36545                 this.fireEvent("loadexception", this, a.node, response);
36546                 Roo.log("Load failed - should have a handler really");
36547                 return;
36548             }
36549             
36550             
36551             
36552             if (this.root !== false) {
36553                  o = o[this.root];
36554             }
36555             
36556             for(var i = 0, len = o.length; i < len; i++){
36557                 var n = this.createNode(o[i]);
36558                 if(n){
36559                     node.appendChild(n);
36560                 }
36561             }
36562             if(typeof callback == "function"){
36563                 callback(this, node);
36564             }
36565         }catch(e){
36566             this.handleFailure(response);
36567         }
36568     },
36569
36570     handleResponse : function(response){
36571         this.transId = false;
36572         var a = response.argument;
36573         this.processResponse(response, a.node, a.callback);
36574         this.fireEvent("load", this, a.node, response);
36575     },
36576
36577     handleFailure : function(response)
36578     {
36579         // should handle failure better..
36580         this.transId = false;
36581         var a = response.argument;
36582         this.fireEvent("loadexception", this, a.node, response);
36583         if(typeof a.callback == "function"){
36584             a.callback(this, a.node);
36585         }
36586     }
36587 });/*
36588  * Based on:
36589  * Ext JS Library 1.1.1
36590  * Copyright(c) 2006-2007, Ext JS, LLC.
36591  *
36592  * Originally Released Under LGPL - original licence link has changed is not relivant.
36593  *
36594  * Fork - LGPL
36595  * <script type="text/javascript">
36596  */
36597
36598 /**
36599 * @class Roo.tree.TreeFilter
36600 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
36601 * @param {TreePanel} tree
36602 * @param {Object} config (optional)
36603  */
36604 Roo.tree.TreeFilter = function(tree, config){
36605     this.tree = tree;
36606     this.filtered = {};
36607     Roo.apply(this, config);
36608 };
36609
36610 Roo.tree.TreeFilter.prototype = {
36611     clearBlank:false,
36612     reverse:false,
36613     autoClear:false,
36614     remove:false,
36615
36616      /**
36617      * Filter the data by a specific attribute.
36618      * @param {String/RegExp} value Either string that the attribute value
36619      * should start with or a RegExp to test against the attribute
36620      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
36621      * @param {TreeNode} startNode (optional) The node to start the filter at.
36622      */
36623     filter : function(value, attr, startNode){
36624         attr = attr || "text";
36625         var f;
36626         if(typeof value == "string"){
36627             var vlen = value.length;
36628             // auto clear empty filter
36629             if(vlen == 0 && this.clearBlank){
36630                 this.clear();
36631                 return;
36632             }
36633             value = value.toLowerCase();
36634             f = function(n){
36635                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
36636             };
36637         }else if(value.exec){ // regex?
36638             f = function(n){
36639                 return value.test(n.attributes[attr]);
36640             };
36641         }else{
36642             throw 'Illegal filter type, must be string or regex';
36643         }
36644         this.filterBy(f, null, startNode);
36645         },
36646
36647     /**
36648      * Filter by a function. The passed function will be called with each
36649      * node in the tree (or from the startNode). If the function returns true, the node is kept
36650      * otherwise it is filtered. If a node is filtered, its children are also filtered.
36651      * @param {Function} fn The filter function
36652      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
36653      */
36654     filterBy : function(fn, scope, startNode){
36655         startNode = startNode || this.tree.root;
36656         if(this.autoClear){
36657             this.clear();
36658         }
36659         var af = this.filtered, rv = this.reverse;
36660         var f = function(n){
36661             if(n == startNode){
36662                 return true;
36663             }
36664             if(af[n.id]){
36665                 return false;
36666             }
36667             var m = fn.call(scope || n, n);
36668             if(!m || rv){
36669                 af[n.id] = n;
36670                 n.ui.hide();
36671                 return false;
36672             }
36673             return true;
36674         };
36675         startNode.cascade(f);
36676         if(this.remove){
36677            for(var id in af){
36678                if(typeof id != "function"){
36679                    var n = af[id];
36680                    if(n && n.parentNode){
36681                        n.parentNode.removeChild(n);
36682                    }
36683                }
36684            }
36685         }
36686     },
36687
36688     /**
36689      * Clears the current filter. Note: with the "remove" option
36690      * set a filter cannot be cleared.
36691      */
36692     clear : function(){
36693         var t = this.tree;
36694         var af = this.filtered;
36695         for(var id in af){
36696             if(typeof id != "function"){
36697                 var n = af[id];
36698                 if(n){
36699                     n.ui.show();
36700                 }
36701             }
36702         }
36703         this.filtered = {};
36704     }
36705 };
36706 /*
36707  * Based on:
36708  * Ext JS Library 1.1.1
36709  * Copyright(c) 2006-2007, Ext JS, LLC.
36710  *
36711  * Originally Released Under LGPL - original licence link has changed is not relivant.
36712  *
36713  * Fork - LGPL
36714  * <script type="text/javascript">
36715  */
36716  
36717
36718 /**
36719  * @class Roo.tree.TreeSorter
36720  * Provides sorting of nodes in a TreePanel
36721  * 
36722  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
36723  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
36724  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
36725  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
36726  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
36727  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
36728  * @constructor
36729  * @param {TreePanel} tree
36730  * @param {Object} config
36731  */
36732 Roo.tree.TreeSorter = function(tree, config){
36733     Roo.apply(this, config);
36734     tree.on("beforechildrenrendered", this.doSort, this);
36735     tree.on("append", this.updateSort, this);
36736     tree.on("insert", this.updateSort, this);
36737     
36738     var dsc = this.dir && this.dir.toLowerCase() == "desc";
36739     var p = this.property || "text";
36740     var sortType = this.sortType;
36741     var fs = this.folderSort;
36742     var cs = this.caseSensitive === true;
36743     var leafAttr = this.leafAttr || 'leaf';
36744
36745     this.sortFn = function(n1, n2){
36746         if(fs){
36747             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
36748                 return 1;
36749             }
36750             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
36751                 return -1;
36752             }
36753         }
36754         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
36755         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
36756         if(v1 < v2){
36757                         return dsc ? +1 : -1;
36758                 }else if(v1 > v2){
36759                         return dsc ? -1 : +1;
36760         }else{
36761                 return 0;
36762         }
36763     };
36764 };
36765
36766 Roo.tree.TreeSorter.prototype = {
36767     doSort : function(node){
36768         node.sort(this.sortFn);
36769     },
36770     
36771     compareNodes : function(n1, n2){
36772         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
36773     },
36774     
36775     updateSort : function(tree, node){
36776         if(node.childrenRendered){
36777             this.doSort.defer(1, this, [node]);
36778         }
36779     }
36780 };/*
36781  * Based on:
36782  * Ext JS Library 1.1.1
36783  * Copyright(c) 2006-2007, Ext JS, LLC.
36784  *
36785  * Originally Released Under LGPL - original licence link has changed is not relivant.
36786  *
36787  * Fork - LGPL
36788  * <script type="text/javascript">
36789  */
36790
36791 if(Roo.dd.DropZone){
36792     
36793 Roo.tree.TreeDropZone = function(tree, config){
36794     this.allowParentInsert = false;
36795     this.allowContainerDrop = false;
36796     this.appendOnly = false;
36797     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
36798     this.tree = tree;
36799     this.lastInsertClass = "x-tree-no-status";
36800     this.dragOverData = {};
36801 };
36802
36803 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
36804     ddGroup : "TreeDD",
36805     scroll:  true,
36806     
36807     expandDelay : 1000,
36808     
36809     expandNode : function(node){
36810         if(node.hasChildNodes() && !node.isExpanded()){
36811             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
36812         }
36813     },
36814     
36815     queueExpand : function(node){
36816         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
36817     },
36818     
36819     cancelExpand : function(){
36820         if(this.expandProcId){
36821             clearTimeout(this.expandProcId);
36822             this.expandProcId = false;
36823         }
36824     },
36825     
36826     isValidDropPoint : function(n, pt, dd, e, data){
36827         if(!n || !data){ return false; }
36828         var targetNode = n.node;
36829         var dropNode = data.node;
36830         // default drop rules
36831         if(!(targetNode && targetNode.isTarget && pt)){
36832             return false;
36833         }
36834         if(pt == "append" && targetNode.allowChildren === false){
36835             return false;
36836         }
36837         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
36838             return false;
36839         }
36840         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
36841             return false;
36842         }
36843         // reuse the object
36844         var overEvent = this.dragOverData;
36845         overEvent.tree = this.tree;
36846         overEvent.target = targetNode;
36847         overEvent.data = data;
36848         overEvent.point = pt;
36849         overEvent.source = dd;
36850         overEvent.rawEvent = e;
36851         overEvent.dropNode = dropNode;
36852         overEvent.cancel = false;  
36853         var result = this.tree.fireEvent("nodedragover", overEvent);
36854         return overEvent.cancel === false && result !== false;
36855     },
36856     
36857     getDropPoint : function(e, n, dd)
36858     {
36859         var tn = n.node;
36860         if(tn.isRoot){
36861             return tn.allowChildren !== false ? "append" : false; // always append for root
36862         }
36863         var dragEl = n.ddel;
36864         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
36865         var y = Roo.lib.Event.getPageY(e);
36866         //var noAppend = tn.allowChildren === false || tn.isLeaf();
36867         
36868         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
36869         var noAppend = tn.allowChildren === false;
36870         if(this.appendOnly || tn.parentNode.allowChildren === false){
36871             return noAppend ? false : "append";
36872         }
36873         var noBelow = false;
36874         if(!this.allowParentInsert){
36875             noBelow = tn.hasChildNodes() && tn.isExpanded();
36876         }
36877         var q = (b - t) / (noAppend ? 2 : 3);
36878         if(y >= t && y < (t + q)){
36879             return "above";
36880         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
36881             return "below";
36882         }else{
36883             return "append";
36884         }
36885     },
36886     
36887     onNodeEnter : function(n, dd, e, data)
36888     {
36889         this.cancelExpand();
36890     },
36891     
36892     onNodeOver : function(n, dd, e, data)
36893     {
36894        
36895         var pt = this.getDropPoint(e, n, dd);
36896         var node = n.node;
36897         
36898         // auto node expand check
36899         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
36900             this.queueExpand(node);
36901         }else if(pt != "append"){
36902             this.cancelExpand();
36903         }
36904         
36905         // set the insert point style on the target node
36906         var returnCls = this.dropNotAllowed;
36907         if(this.isValidDropPoint(n, pt, dd, e, data)){
36908            if(pt){
36909                var el = n.ddel;
36910                var cls;
36911                if(pt == "above"){
36912                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
36913                    cls = "x-tree-drag-insert-above";
36914                }else if(pt == "below"){
36915                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
36916                    cls = "x-tree-drag-insert-below";
36917                }else{
36918                    returnCls = "x-tree-drop-ok-append";
36919                    cls = "x-tree-drag-append";
36920                }
36921                if(this.lastInsertClass != cls){
36922                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
36923                    this.lastInsertClass = cls;
36924                }
36925            }
36926        }
36927        return returnCls;
36928     },
36929     
36930     onNodeOut : function(n, dd, e, data){
36931         
36932         this.cancelExpand();
36933         this.removeDropIndicators(n);
36934     },
36935     
36936     onNodeDrop : function(n, dd, e, data){
36937         var point = this.getDropPoint(e, n, dd);
36938         var targetNode = n.node;
36939         targetNode.ui.startDrop();
36940         if(!this.isValidDropPoint(n, point, dd, e, data)){
36941             targetNode.ui.endDrop();
36942             return false;
36943         }
36944         // first try to find the drop node
36945         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
36946         var dropEvent = {
36947             tree : this.tree,
36948             target: targetNode,
36949             data: data,
36950             point: point,
36951             source: dd,
36952             rawEvent: e,
36953             dropNode: dropNode,
36954             cancel: !dropNode   
36955         };
36956         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
36957         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
36958             targetNode.ui.endDrop();
36959             return false;
36960         }
36961         // allow target changing
36962         targetNode = dropEvent.target;
36963         if(point == "append" && !targetNode.isExpanded()){
36964             targetNode.expand(false, null, function(){
36965                 this.completeDrop(dropEvent);
36966             }.createDelegate(this));
36967         }else{
36968             this.completeDrop(dropEvent);
36969         }
36970         return true;
36971     },
36972     
36973     completeDrop : function(de){
36974         var ns = de.dropNode, p = de.point, t = de.target;
36975         if(!(ns instanceof Array)){
36976             ns = [ns];
36977         }
36978         var n;
36979         for(var i = 0, len = ns.length; i < len; i++){
36980             n = ns[i];
36981             if(p == "above"){
36982                 t.parentNode.insertBefore(n, t);
36983             }else if(p == "below"){
36984                 t.parentNode.insertBefore(n, t.nextSibling);
36985             }else{
36986                 t.appendChild(n);
36987             }
36988         }
36989         n.ui.focus();
36990         if(this.tree.hlDrop){
36991             n.ui.highlight();
36992         }
36993         t.ui.endDrop();
36994         this.tree.fireEvent("nodedrop", de);
36995     },
36996     
36997     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
36998         if(this.tree.hlDrop){
36999             dropNode.ui.focus();
37000             dropNode.ui.highlight();
37001         }
37002         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37003     },
37004     
37005     getTree : function(){
37006         return this.tree;
37007     },
37008     
37009     removeDropIndicators : function(n){
37010         if(n && n.ddel){
37011             var el = n.ddel;
37012             Roo.fly(el).removeClass([
37013                     "x-tree-drag-insert-above",
37014                     "x-tree-drag-insert-below",
37015                     "x-tree-drag-append"]);
37016             this.lastInsertClass = "_noclass";
37017         }
37018     },
37019     
37020     beforeDragDrop : function(target, e, id){
37021         this.cancelExpand();
37022         return true;
37023     },
37024     
37025     afterRepair : function(data){
37026         if(data && Roo.enableFx){
37027             data.node.ui.highlight();
37028         }
37029         this.hideProxy();
37030     } 
37031     
37032 });
37033
37034 }
37035 /*
37036  * Based on:
37037  * Ext JS Library 1.1.1
37038  * Copyright(c) 2006-2007, Ext JS, LLC.
37039  *
37040  * Originally Released Under LGPL - original licence link has changed is not relivant.
37041  *
37042  * Fork - LGPL
37043  * <script type="text/javascript">
37044  */
37045  
37046
37047 if(Roo.dd.DragZone){
37048 Roo.tree.TreeDragZone = function(tree, config){
37049     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37050     this.tree = tree;
37051 };
37052
37053 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37054     ddGroup : "TreeDD",
37055    
37056     onBeforeDrag : function(data, e){
37057         var n = data.node;
37058         return n && n.draggable && !n.disabled;
37059     },
37060      
37061     
37062     onInitDrag : function(e){
37063         var data = this.dragData;
37064         this.tree.getSelectionModel().select(data.node);
37065         this.proxy.update("");
37066         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37067         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37068     },
37069     
37070     getRepairXY : function(e, data){
37071         return data.node.ui.getDDRepairXY();
37072     },
37073     
37074     onEndDrag : function(data, e){
37075         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37076         
37077         
37078     },
37079     
37080     onValidDrop : function(dd, e, id){
37081         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37082         this.hideProxy();
37083     },
37084     
37085     beforeInvalidDrop : function(e, id){
37086         // this scrolls the original position back into view
37087         var sm = this.tree.getSelectionModel();
37088         sm.clearSelections();
37089         sm.select(this.dragData.node);
37090     }
37091 });
37092 }/*
37093  * Based on:
37094  * Ext JS Library 1.1.1
37095  * Copyright(c) 2006-2007, Ext JS, LLC.
37096  *
37097  * Originally Released Under LGPL - original licence link has changed is not relivant.
37098  *
37099  * Fork - LGPL
37100  * <script type="text/javascript">
37101  */
37102 /**
37103  * @class Roo.tree.TreeEditor
37104  * @extends Roo.Editor
37105  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37106  * as the editor field.
37107  * @constructor
37108  * @param {Object} config (used to be the tree panel.)
37109  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37110  * 
37111  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37112  * @cfg {Roo.form.TextField|Object} field The field configuration
37113  *
37114  * 
37115  */
37116 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37117     var tree = config;
37118     var field;
37119     if (oldconfig) { // old style..
37120         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37121     } else {
37122         // new style..
37123         tree = config.tree;
37124         config.field = config.field  || {};
37125         config.field.xtype = 'TextField';
37126         field = Roo.factory(config.field, Roo.form);
37127     }
37128     config = config || {};
37129     
37130     
37131     this.addEvents({
37132         /**
37133          * @event beforenodeedit
37134          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37135          * false from the handler of this event.
37136          * @param {Editor} this
37137          * @param {Roo.tree.Node} node 
37138          */
37139         "beforenodeedit" : true
37140     });
37141     
37142     //Roo.log(config);
37143     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37144
37145     this.tree = tree;
37146
37147     tree.on('beforeclick', this.beforeNodeClick, this);
37148     tree.getTreeEl().on('mousedown', this.hide, this);
37149     this.on('complete', this.updateNode, this);
37150     this.on('beforestartedit', this.fitToTree, this);
37151     this.on('startedit', this.bindScroll, this, {delay:10});
37152     this.on('specialkey', this.onSpecialKey, this);
37153 };
37154
37155 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37156     /**
37157      * @cfg {String} alignment
37158      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37159      */
37160     alignment: "l-l",
37161     // inherit
37162     autoSize: false,
37163     /**
37164      * @cfg {Boolean} hideEl
37165      * True to hide the bound element while the editor is displayed (defaults to false)
37166      */
37167     hideEl : false,
37168     /**
37169      * @cfg {String} cls
37170      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37171      */
37172     cls: "x-small-editor x-tree-editor",
37173     /**
37174      * @cfg {Boolean} shim
37175      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37176      */
37177     shim:false,
37178     // inherit
37179     shadow:"frame",
37180     /**
37181      * @cfg {Number} maxWidth
37182      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37183      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37184      * scroll and client offsets into account prior to each edit.
37185      */
37186     maxWidth: 250,
37187
37188     editDelay : 350,
37189
37190     // private
37191     fitToTree : function(ed, el){
37192         var td = this.tree.getTreeEl().dom, nd = el.dom;
37193         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37194             td.scrollLeft = nd.offsetLeft;
37195         }
37196         var w = Math.min(
37197                 this.maxWidth,
37198                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37199         this.setSize(w, '');
37200         
37201         return this.fireEvent('beforenodeedit', this, this.editNode);
37202         
37203     },
37204
37205     // private
37206     triggerEdit : function(node){
37207         this.completeEdit();
37208         this.editNode = node;
37209         this.startEdit(node.ui.textNode, node.text);
37210     },
37211
37212     // private
37213     bindScroll : function(){
37214         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37215     },
37216
37217     // private
37218     beforeNodeClick : function(node, e){
37219         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37220         this.lastClick = new Date();
37221         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37222             e.stopEvent();
37223             this.triggerEdit(node);
37224             return false;
37225         }
37226         return true;
37227     },
37228
37229     // private
37230     updateNode : function(ed, value){
37231         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
37232         this.editNode.setText(value);
37233     },
37234
37235     // private
37236     onHide : function(){
37237         Roo.tree.TreeEditor.superclass.onHide.call(this);
37238         if(this.editNode){
37239             this.editNode.ui.focus();
37240         }
37241     },
37242
37243     // private
37244     onSpecialKey : function(field, e){
37245         var k = e.getKey();
37246         if(k == e.ESC){
37247             e.stopEvent();
37248             this.cancelEdit();
37249         }else if(k == e.ENTER && !e.hasModifier()){
37250             e.stopEvent();
37251             this.completeEdit();
37252         }
37253     }
37254 });//<Script type="text/javascript">
37255 /*
37256  * Based on:
37257  * Ext JS Library 1.1.1
37258  * Copyright(c) 2006-2007, Ext JS, LLC.
37259  *
37260  * Originally Released Under LGPL - original licence link has changed is not relivant.
37261  *
37262  * Fork - LGPL
37263  * <script type="text/javascript">
37264  */
37265  
37266 /**
37267  * Not documented??? - probably should be...
37268  */
37269
37270 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
37271     //focus: Roo.emptyFn, // prevent odd scrolling behavior
37272     
37273     renderElements : function(n, a, targetNode, bulkRender){
37274         //consel.log("renderElements?");
37275         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
37276
37277         var t = n.getOwnerTree();
37278         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
37279         
37280         var cols = t.columns;
37281         var bw = t.borderWidth;
37282         var c = cols[0];
37283         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
37284          var cb = typeof a.checked == "boolean";
37285         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37286         var colcls = 'x-t-' + tid + '-c0';
37287         var buf = [
37288             '<li class="x-tree-node">',
37289             
37290                 
37291                 '<div class="x-tree-node-el ', a.cls,'">',
37292                     // extran...
37293                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
37294                 
37295                 
37296                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
37297                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
37298                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
37299                            (a.icon ? ' x-tree-node-inline-icon' : ''),
37300                            (a.iconCls ? ' '+a.iconCls : ''),
37301                            '" unselectable="on" />',
37302                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
37303                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
37304                              
37305                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37306                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
37307                             '<span unselectable="on" qtip="' + tx + '">',
37308                              tx,
37309                              '</span></a>' ,
37310                     '</div>',
37311                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
37312                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
37313                  ];
37314         for(var i = 1, len = cols.length; i < len; i++){
37315             c = cols[i];
37316             colcls = 'x-t-' + tid + '-c' +i;
37317             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
37318             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
37319                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
37320                       "</div>");
37321          }
37322          
37323          buf.push(
37324             '</a>',
37325             '<div class="x-clear"></div></div>',
37326             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37327             "</li>");
37328         
37329         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37330             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37331                                 n.nextSibling.ui.getEl(), buf.join(""));
37332         }else{
37333             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37334         }
37335         var el = this.wrap.firstChild;
37336         this.elRow = el;
37337         this.elNode = el.firstChild;
37338         this.ranchor = el.childNodes[1];
37339         this.ctNode = this.wrap.childNodes[1];
37340         var cs = el.firstChild.childNodes;
37341         this.indentNode = cs[0];
37342         this.ecNode = cs[1];
37343         this.iconNode = cs[2];
37344         var index = 3;
37345         if(cb){
37346             this.checkbox = cs[3];
37347             index++;
37348         }
37349         this.anchor = cs[index];
37350         
37351         this.textNode = cs[index].firstChild;
37352         
37353         //el.on("click", this.onClick, this);
37354         //el.on("dblclick", this.onDblClick, this);
37355         
37356         
37357        // console.log(this);
37358     },
37359     initEvents : function(){
37360         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
37361         
37362             
37363         var a = this.ranchor;
37364
37365         var el = Roo.get(a);
37366
37367         if(Roo.isOpera){ // opera render bug ignores the CSS
37368             el.setStyle("text-decoration", "none");
37369         }
37370
37371         el.on("click", this.onClick, this);
37372         el.on("dblclick", this.onDblClick, this);
37373         el.on("contextmenu", this.onContextMenu, this);
37374         
37375     },
37376     
37377     /*onSelectedChange : function(state){
37378         if(state){
37379             this.focus();
37380             this.addClass("x-tree-selected");
37381         }else{
37382             //this.blur();
37383             this.removeClass("x-tree-selected");
37384         }
37385     },*/
37386     addClass : function(cls){
37387         if(this.elRow){
37388             Roo.fly(this.elRow).addClass(cls);
37389         }
37390         
37391     },
37392     
37393     
37394     removeClass : function(cls){
37395         if(this.elRow){
37396             Roo.fly(this.elRow).removeClass(cls);
37397         }
37398     }
37399
37400     
37401     
37402 });//<Script type="text/javascript">
37403
37404 /*
37405  * Based on:
37406  * Ext JS Library 1.1.1
37407  * Copyright(c) 2006-2007, Ext JS, LLC.
37408  *
37409  * Originally Released Under LGPL - original licence link has changed is not relivant.
37410  *
37411  * Fork - LGPL
37412  * <script type="text/javascript">
37413  */
37414  
37415
37416 /**
37417  * @class Roo.tree.ColumnTree
37418  * @extends Roo.data.TreePanel
37419  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
37420  * @cfg {int} borderWidth  compined right/left border allowance
37421  * @constructor
37422  * @param {String/HTMLElement/Element} el The container element
37423  * @param {Object} config
37424  */
37425 Roo.tree.ColumnTree =  function(el, config)
37426 {
37427    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
37428    this.addEvents({
37429         /**
37430         * @event resize
37431         * Fire this event on a container when it resizes
37432         * @param {int} w Width
37433         * @param {int} h Height
37434         */
37435        "resize" : true
37436     });
37437     this.on('resize', this.onResize, this);
37438 };
37439
37440 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
37441     //lines:false,
37442     
37443     
37444     borderWidth: Roo.isBorderBox ? 0 : 2, 
37445     headEls : false,
37446     
37447     render : function(){
37448         // add the header.....
37449        
37450         Roo.tree.ColumnTree.superclass.render.apply(this);
37451         
37452         this.el.addClass('x-column-tree');
37453         
37454         this.headers = this.el.createChild(
37455             {cls:'x-tree-headers'},this.innerCt.dom);
37456    
37457         var cols = this.columns, c;
37458         var totalWidth = 0;
37459         this.headEls = [];
37460         var  len = cols.length;
37461         for(var i = 0; i < len; i++){
37462              c = cols[i];
37463              totalWidth += c.width;
37464             this.headEls.push(this.headers.createChild({
37465                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
37466                  cn: {
37467                      cls:'x-tree-hd-text',
37468                      html: c.header
37469                  },
37470                  style:'width:'+(c.width-this.borderWidth)+'px;'
37471              }));
37472         }
37473         this.headers.createChild({cls:'x-clear'});
37474         // prevent floats from wrapping when clipped
37475         this.headers.setWidth(totalWidth);
37476         //this.innerCt.setWidth(totalWidth);
37477         this.innerCt.setStyle({ overflow: 'auto' });
37478         this.onResize(this.width, this.height);
37479              
37480         
37481     },
37482     onResize : function(w,h)
37483     {
37484         this.height = h;
37485         this.width = w;
37486         // resize cols..
37487         this.innerCt.setWidth(this.width);
37488         this.innerCt.setHeight(this.height-20);
37489         
37490         // headers...
37491         var cols = this.columns, c;
37492         var totalWidth = 0;
37493         var expEl = false;
37494         var len = cols.length;
37495         for(var i = 0; i < len; i++){
37496             c = cols[i];
37497             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
37498                 // it's the expander..
37499                 expEl  = this.headEls[i];
37500                 continue;
37501             }
37502             totalWidth += c.width;
37503             
37504         }
37505         if (expEl) {
37506             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
37507         }
37508         this.headers.setWidth(w-20);
37509
37510         
37511         
37512         
37513     }
37514 });
37515 /*
37516  * Based on:
37517  * Ext JS Library 1.1.1
37518  * Copyright(c) 2006-2007, Ext JS, LLC.
37519  *
37520  * Originally Released Under LGPL - original licence link has changed is not relivant.
37521  *
37522  * Fork - LGPL
37523  * <script type="text/javascript">
37524  */
37525  
37526 /**
37527  * @class Roo.menu.Menu
37528  * @extends Roo.util.Observable
37529  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
37530  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
37531  * @constructor
37532  * Creates a new Menu
37533  * @param {Object} config Configuration options
37534  */
37535 Roo.menu.Menu = function(config){
37536     
37537     Roo.menu.Menu.superclass.constructor.call(this, config);
37538     
37539     this.id = this.id || Roo.id();
37540     this.addEvents({
37541         /**
37542          * @event beforeshow
37543          * Fires before this menu is displayed
37544          * @param {Roo.menu.Menu} this
37545          */
37546         beforeshow : true,
37547         /**
37548          * @event beforehide
37549          * Fires before this menu is hidden
37550          * @param {Roo.menu.Menu} this
37551          */
37552         beforehide : true,
37553         /**
37554          * @event show
37555          * Fires after this menu is displayed
37556          * @param {Roo.menu.Menu} this
37557          */
37558         show : true,
37559         /**
37560          * @event hide
37561          * Fires after this menu is hidden
37562          * @param {Roo.menu.Menu} this
37563          */
37564         hide : true,
37565         /**
37566          * @event click
37567          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
37568          * @param {Roo.menu.Menu} this
37569          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37570          * @param {Roo.EventObject} e
37571          */
37572         click : true,
37573         /**
37574          * @event mouseover
37575          * Fires when the mouse is hovering over this menu
37576          * @param {Roo.menu.Menu} this
37577          * @param {Roo.EventObject} e
37578          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37579          */
37580         mouseover : true,
37581         /**
37582          * @event mouseout
37583          * Fires when the mouse exits this menu
37584          * @param {Roo.menu.Menu} this
37585          * @param {Roo.EventObject} e
37586          * @param {Roo.menu.Item} menuItem The menu item that was clicked
37587          */
37588         mouseout : true,
37589         /**
37590          * @event itemclick
37591          * Fires when a menu item contained in this menu is clicked
37592          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
37593          * @param {Roo.EventObject} e
37594          */
37595         itemclick: true
37596     });
37597     if (this.registerMenu) {
37598         Roo.menu.MenuMgr.register(this);
37599     }
37600     
37601     var mis = this.items;
37602     this.items = new Roo.util.MixedCollection();
37603     if(mis){
37604         this.add.apply(this, mis);
37605     }
37606 };
37607
37608 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
37609     /**
37610      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
37611      */
37612     minWidth : 120,
37613     /**
37614      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
37615      * for bottom-right shadow (defaults to "sides")
37616      */
37617     shadow : "sides",
37618     /**
37619      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
37620      * this menu (defaults to "tl-tr?")
37621      */
37622     subMenuAlign : "tl-tr?",
37623     /**
37624      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
37625      * relative to its element of origin (defaults to "tl-bl?")
37626      */
37627     defaultAlign : "tl-bl?",
37628     /**
37629      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
37630      */
37631     allowOtherMenus : false,
37632     /**
37633      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
37634      */
37635     registerMenu : true,
37636
37637     hidden:true,
37638
37639     // private
37640     render : function(){
37641         if(this.el){
37642             return;
37643         }
37644         var el = this.el = new Roo.Layer({
37645             cls: "x-menu",
37646             shadow:this.shadow,
37647             constrain: false,
37648             parentEl: this.parentEl || document.body,
37649             zindex:15000
37650         });
37651
37652         this.keyNav = new Roo.menu.MenuNav(this);
37653
37654         if(this.plain){
37655             el.addClass("x-menu-plain");
37656         }
37657         if(this.cls){
37658             el.addClass(this.cls);
37659         }
37660         // generic focus element
37661         this.focusEl = el.createChild({
37662             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
37663         });
37664         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
37665         //disabling touch- as it's causing issues ..
37666         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
37667         ul.on('click'   , this.onClick, this);
37668         
37669         
37670         ul.on("mouseover", this.onMouseOver, this);
37671         ul.on("mouseout", this.onMouseOut, this);
37672         this.items.each(function(item){
37673             if (item.hidden) {
37674                 return;
37675             }
37676             
37677             var li = document.createElement("li");
37678             li.className = "x-menu-list-item";
37679             ul.dom.appendChild(li);
37680             item.render(li, this);
37681         }, this);
37682         this.ul = ul;
37683         this.autoWidth();
37684     },
37685
37686     // private
37687     autoWidth : function(){
37688         var el = this.el, ul = this.ul;
37689         if(!el){
37690             return;
37691         }
37692         var w = this.width;
37693         if(w){
37694             el.setWidth(w);
37695         }else if(Roo.isIE){
37696             el.setWidth(this.minWidth);
37697             var t = el.dom.offsetWidth; // force recalc
37698             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
37699         }
37700     },
37701
37702     // private
37703     delayAutoWidth : function(){
37704         if(this.rendered){
37705             if(!this.awTask){
37706                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
37707             }
37708             this.awTask.delay(20);
37709         }
37710     },
37711
37712     // private
37713     findTargetItem : function(e){
37714         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
37715         if(t && t.menuItemId){
37716             return this.items.get(t.menuItemId);
37717         }
37718     },
37719
37720     // private
37721     onClick : function(e){
37722         Roo.log("menu.onClick");
37723         var t = this.findTargetItem(e);
37724         if(!t){
37725             return;
37726         }
37727         Roo.log(e);
37728         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
37729             if(t == this.activeItem && t.shouldDeactivate(e)){
37730                 this.activeItem.deactivate();
37731                 delete this.activeItem;
37732                 return;
37733             }
37734             if(t.canActivate){
37735                 this.setActiveItem(t, true);
37736             }
37737             return;
37738             
37739             
37740         }
37741         
37742         t.onClick(e);
37743         this.fireEvent("click", this, t, e);
37744     },
37745
37746     // private
37747     setActiveItem : function(item, autoExpand){
37748         if(item != this.activeItem){
37749             if(this.activeItem){
37750                 this.activeItem.deactivate();
37751             }
37752             this.activeItem = item;
37753             item.activate(autoExpand);
37754         }else if(autoExpand){
37755             item.expandMenu();
37756         }
37757     },
37758
37759     // private
37760     tryActivate : function(start, step){
37761         var items = this.items;
37762         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
37763             var item = items.get(i);
37764             if(!item.disabled && item.canActivate){
37765                 this.setActiveItem(item, false);
37766                 return item;
37767             }
37768         }
37769         return false;
37770     },
37771
37772     // private
37773     onMouseOver : function(e){
37774         var t;
37775         if(t = this.findTargetItem(e)){
37776             if(t.canActivate && !t.disabled){
37777                 this.setActiveItem(t, true);
37778             }
37779         }
37780         this.fireEvent("mouseover", this, e, t);
37781     },
37782
37783     // private
37784     onMouseOut : function(e){
37785         var t;
37786         if(t = this.findTargetItem(e)){
37787             if(t == this.activeItem && t.shouldDeactivate(e)){
37788                 this.activeItem.deactivate();
37789                 delete this.activeItem;
37790             }
37791         }
37792         this.fireEvent("mouseout", this, e, t);
37793     },
37794
37795     /**
37796      * Read-only.  Returns true if the menu is currently displayed, else false.
37797      * @type Boolean
37798      */
37799     isVisible : function(){
37800         return this.el && !this.hidden;
37801     },
37802
37803     /**
37804      * Displays this menu relative to another element
37805      * @param {String/HTMLElement/Roo.Element} element The element to align to
37806      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
37807      * the element (defaults to this.defaultAlign)
37808      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37809      */
37810     show : function(el, pos, parentMenu){
37811         this.parentMenu = parentMenu;
37812         if(!this.el){
37813             this.render();
37814         }
37815         this.fireEvent("beforeshow", this);
37816         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
37817     },
37818
37819     /**
37820      * Displays this menu at a specific xy position
37821      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
37822      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
37823      */
37824     showAt : function(xy, parentMenu, /* private: */_e){
37825         this.parentMenu = parentMenu;
37826         if(!this.el){
37827             this.render();
37828         }
37829         if(_e !== false){
37830             this.fireEvent("beforeshow", this);
37831             xy = this.el.adjustForConstraints(xy);
37832         }
37833         this.el.setXY(xy);
37834         this.el.show();
37835         this.hidden = false;
37836         this.focus();
37837         this.fireEvent("show", this);
37838     },
37839
37840     focus : function(){
37841         if(!this.hidden){
37842             this.doFocus.defer(50, this);
37843         }
37844     },
37845
37846     doFocus : function(){
37847         if(!this.hidden){
37848             this.focusEl.focus();
37849         }
37850     },
37851
37852     /**
37853      * Hides this menu and optionally all parent menus
37854      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
37855      */
37856     hide : function(deep){
37857         if(this.el && this.isVisible()){
37858             this.fireEvent("beforehide", this);
37859             if(this.activeItem){
37860                 this.activeItem.deactivate();
37861                 this.activeItem = null;
37862             }
37863             this.el.hide();
37864             this.hidden = true;
37865             this.fireEvent("hide", this);
37866         }
37867         if(deep === true && this.parentMenu){
37868             this.parentMenu.hide(true);
37869         }
37870     },
37871
37872     /**
37873      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
37874      * Any of the following are valid:
37875      * <ul>
37876      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
37877      * <li>An HTMLElement object which will be converted to a menu item</li>
37878      * <li>A menu item config object that will be created as a new menu item</li>
37879      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
37880      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
37881      * </ul>
37882      * Usage:
37883      * <pre><code>
37884 // Create the menu
37885 var menu = new Roo.menu.Menu();
37886
37887 // Create a menu item to add by reference
37888 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
37889
37890 // Add a bunch of items at once using different methods.
37891 // Only the last item added will be returned.
37892 var item = menu.add(
37893     menuItem,                // add existing item by ref
37894     'Dynamic Item',          // new TextItem
37895     '-',                     // new separator
37896     { text: 'Config Item' }  // new item by config
37897 );
37898 </code></pre>
37899      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
37900      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
37901      */
37902     add : function(){
37903         var a = arguments, l = a.length, item;
37904         for(var i = 0; i < l; i++){
37905             var el = a[i];
37906             if ((typeof(el) == "object") && el.xtype && el.xns) {
37907                 el = Roo.factory(el, Roo.menu);
37908             }
37909             
37910             if(el.render){ // some kind of Item
37911                 item = this.addItem(el);
37912             }else if(typeof el == "string"){ // string
37913                 if(el == "separator" || el == "-"){
37914                     item = this.addSeparator();
37915                 }else{
37916                     item = this.addText(el);
37917                 }
37918             }else if(el.tagName || el.el){ // element
37919                 item = this.addElement(el);
37920             }else if(typeof el == "object"){ // must be menu item config?
37921                 item = this.addMenuItem(el);
37922             }
37923         }
37924         return item;
37925     },
37926
37927     /**
37928      * Returns this menu's underlying {@link Roo.Element} object
37929      * @return {Roo.Element} The element
37930      */
37931     getEl : function(){
37932         if(!this.el){
37933             this.render();
37934         }
37935         return this.el;
37936     },
37937
37938     /**
37939      * Adds a separator bar to the menu
37940      * @return {Roo.menu.Item} The menu item that was added
37941      */
37942     addSeparator : function(){
37943         return this.addItem(new Roo.menu.Separator());
37944     },
37945
37946     /**
37947      * Adds an {@link Roo.Element} object to the menu
37948      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
37949      * @return {Roo.menu.Item} The menu item that was added
37950      */
37951     addElement : function(el){
37952         return this.addItem(new Roo.menu.BaseItem(el));
37953     },
37954
37955     /**
37956      * Adds an existing object based on {@link Roo.menu.Item} to the menu
37957      * @param {Roo.menu.Item} item The menu item to add
37958      * @return {Roo.menu.Item} The menu item that was added
37959      */
37960     addItem : function(item){
37961         this.items.add(item);
37962         if(this.ul){
37963             var li = document.createElement("li");
37964             li.className = "x-menu-list-item";
37965             this.ul.dom.appendChild(li);
37966             item.render(li, this);
37967             this.delayAutoWidth();
37968         }
37969         return item;
37970     },
37971
37972     /**
37973      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
37974      * @param {Object} config A MenuItem config object
37975      * @return {Roo.menu.Item} The menu item that was added
37976      */
37977     addMenuItem : function(config){
37978         if(!(config instanceof Roo.menu.Item)){
37979             if(typeof config.checked == "boolean"){ // must be check menu item config?
37980                 config = new Roo.menu.CheckItem(config);
37981             }else{
37982                 config = new Roo.menu.Item(config);
37983             }
37984         }
37985         return this.addItem(config);
37986     },
37987
37988     /**
37989      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
37990      * @param {String} text The text to display in the menu item
37991      * @return {Roo.menu.Item} The menu item that was added
37992      */
37993     addText : function(text){
37994         return this.addItem(new Roo.menu.TextItem({ text : text }));
37995     },
37996
37997     /**
37998      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
37999      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38000      * @param {Roo.menu.Item} item The menu item to add
38001      * @return {Roo.menu.Item} The menu item that was added
38002      */
38003     insert : function(index, item){
38004         this.items.insert(index, item);
38005         if(this.ul){
38006             var li = document.createElement("li");
38007             li.className = "x-menu-list-item";
38008             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38009             item.render(li, this);
38010             this.delayAutoWidth();
38011         }
38012         return item;
38013     },
38014
38015     /**
38016      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38017      * @param {Roo.menu.Item} item The menu item to remove
38018      */
38019     remove : function(item){
38020         this.items.removeKey(item.id);
38021         item.destroy();
38022     },
38023
38024     /**
38025      * Removes and destroys all items in the menu
38026      */
38027     removeAll : function(){
38028         var f;
38029         while(f = this.items.first()){
38030             this.remove(f);
38031         }
38032     }
38033 });
38034
38035 // MenuNav is a private utility class used internally by the Menu
38036 Roo.menu.MenuNav = function(menu){
38037     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38038     this.scope = this.menu = menu;
38039 };
38040
38041 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38042     doRelay : function(e, h){
38043         var k = e.getKey();
38044         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38045             this.menu.tryActivate(0, 1);
38046             return false;
38047         }
38048         return h.call(this.scope || this, e, this.menu);
38049     },
38050
38051     up : function(e, m){
38052         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38053             m.tryActivate(m.items.length-1, -1);
38054         }
38055     },
38056
38057     down : function(e, m){
38058         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38059             m.tryActivate(0, 1);
38060         }
38061     },
38062
38063     right : function(e, m){
38064         if(m.activeItem){
38065             m.activeItem.expandMenu(true);
38066         }
38067     },
38068
38069     left : function(e, m){
38070         m.hide();
38071         if(m.parentMenu && m.parentMenu.activeItem){
38072             m.parentMenu.activeItem.activate();
38073         }
38074     },
38075
38076     enter : function(e, m){
38077         if(m.activeItem){
38078             e.stopPropagation();
38079             m.activeItem.onClick(e);
38080             m.fireEvent("click", this, m.activeItem);
38081             return true;
38082         }
38083     }
38084 });/*
38085  * Based on:
38086  * Ext JS Library 1.1.1
38087  * Copyright(c) 2006-2007, Ext JS, LLC.
38088  *
38089  * Originally Released Under LGPL - original licence link has changed is not relivant.
38090  *
38091  * Fork - LGPL
38092  * <script type="text/javascript">
38093  */
38094  
38095 /**
38096  * @class Roo.menu.MenuMgr
38097  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38098  * @singleton
38099  */
38100 Roo.menu.MenuMgr = function(){
38101    var menus, active, groups = {}, attached = false, lastShow = new Date();
38102
38103    // private - called when first menu is created
38104    function init(){
38105        menus = {};
38106        active = new Roo.util.MixedCollection();
38107        Roo.get(document).addKeyListener(27, function(){
38108            if(active.length > 0){
38109                hideAll();
38110            }
38111        });
38112    }
38113
38114    // private
38115    function hideAll(){
38116        if(active && active.length > 0){
38117            var c = active.clone();
38118            c.each(function(m){
38119                m.hide();
38120            });
38121        }
38122    }
38123
38124    // private
38125    function onHide(m){
38126        active.remove(m);
38127        if(active.length < 1){
38128            Roo.get(document).un("mousedown", onMouseDown);
38129            attached = false;
38130        }
38131    }
38132
38133    // private
38134    function onShow(m){
38135        var last = active.last();
38136        lastShow = new Date();
38137        active.add(m);
38138        if(!attached){
38139            Roo.get(document).on("mousedown", onMouseDown);
38140            attached = true;
38141        }
38142        if(m.parentMenu){
38143           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38144           m.parentMenu.activeChild = m;
38145        }else if(last && last.isVisible()){
38146           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38147        }
38148    }
38149
38150    // private
38151    function onBeforeHide(m){
38152        if(m.activeChild){
38153            m.activeChild.hide();
38154        }
38155        if(m.autoHideTimer){
38156            clearTimeout(m.autoHideTimer);
38157            delete m.autoHideTimer;
38158        }
38159    }
38160
38161    // private
38162    function onBeforeShow(m){
38163        var pm = m.parentMenu;
38164        if(!pm && !m.allowOtherMenus){
38165            hideAll();
38166        }else if(pm && pm.activeChild && active != m){
38167            pm.activeChild.hide();
38168        }
38169    }
38170
38171    // private
38172    function onMouseDown(e){
38173        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38174            hideAll();
38175        }
38176    }
38177
38178    // private
38179    function onBeforeCheck(mi, state){
38180        if(state){
38181            var g = groups[mi.group];
38182            for(var i = 0, l = g.length; i < l; i++){
38183                if(g[i] != mi){
38184                    g[i].setChecked(false);
38185                }
38186            }
38187        }
38188    }
38189
38190    return {
38191
38192        /**
38193         * Hides all menus that are currently visible
38194         */
38195        hideAll : function(){
38196             hideAll();  
38197        },
38198
38199        // private
38200        register : function(menu){
38201            if(!menus){
38202                init();
38203            }
38204            menus[menu.id] = menu;
38205            menu.on("beforehide", onBeforeHide);
38206            menu.on("hide", onHide);
38207            menu.on("beforeshow", onBeforeShow);
38208            menu.on("show", onShow);
38209            var g = menu.group;
38210            if(g && menu.events["checkchange"]){
38211                if(!groups[g]){
38212                    groups[g] = [];
38213                }
38214                groups[g].push(menu);
38215                menu.on("checkchange", onCheck);
38216            }
38217        },
38218
38219         /**
38220          * Returns a {@link Roo.menu.Menu} object
38221          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38222          * be used to generate and return a new Menu instance.
38223          */
38224        get : function(menu){
38225            if(typeof menu == "string"){ // menu id
38226                return menus[menu];
38227            }else if(menu.events){  // menu instance
38228                return menu;
38229            }else if(typeof menu.length == 'number'){ // array of menu items?
38230                return new Roo.menu.Menu({items:menu});
38231            }else{ // otherwise, must be a config
38232                return new Roo.menu.Menu(menu);
38233            }
38234        },
38235
38236        // private
38237        unregister : function(menu){
38238            delete menus[menu.id];
38239            menu.un("beforehide", onBeforeHide);
38240            menu.un("hide", onHide);
38241            menu.un("beforeshow", onBeforeShow);
38242            menu.un("show", onShow);
38243            var g = menu.group;
38244            if(g && menu.events["checkchange"]){
38245                groups[g].remove(menu);
38246                menu.un("checkchange", onCheck);
38247            }
38248        },
38249
38250        // private
38251        registerCheckable : function(menuItem){
38252            var g = menuItem.group;
38253            if(g){
38254                if(!groups[g]){
38255                    groups[g] = [];
38256                }
38257                groups[g].push(menuItem);
38258                menuItem.on("beforecheckchange", onBeforeCheck);
38259            }
38260        },
38261
38262        // private
38263        unregisterCheckable : function(menuItem){
38264            var g = menuItem.group;
38265            if(g){
38266                groups[g].remove(menuItem);
38267                menuItem.un("beforecheckchange", onBeforeCheck);
38268            }
38269        }
38270    };
38271 }();/*
38272  * Based on:
38273  * Ext JS Library 1.1.1
38274  * Copyright(c) 2006-2007, Ext JS, LLC.
38275  *
38276  * Originally Released Under LGPL - original licence link has changed is not relivant.
38277  *
38278  * Fork - LGPL
38279  * <script type="text/javascript">
38280  */
38281  
38282
38283 /**
38284  * @class Roo.menu.BaseItem
38285  * @extends Roo.Component
38286  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
38287  * management and base configuration options shared by all menu components.
38288  * @constructor
38289  * Creates a new BaseItem
38290  * @param {Object} config Configuration options
38291  */
38292 Roo.menu.BaseItem = function(config){
38293     Roo.menu.BaseItem.superclass.constructor.call(this, config);
38294
38295     this.addEvents({
38296         /**
38297          * @event click
38298          * Fires when this item is clicked
38299          * @param {Roo.menu.BaseItem} this
38300          * @param {Roo.EventObject} e
38301          */
38302         click: true,
38303         /**
38304          * @event activate
38305          * Fires when this item is activated
38306          * @param {Roo.menu.BaseItem} this
38307          */
38308         activate : true,
38309         /**
38310          * @event deactivate
38311          * Fires when this item is deactivated
38312          * @param {Roo.menu.BaseItem} this
38313          */
38314         deactivate : true
38315     });
38316
38317     if(this.handler){
38318         this.on("click", this.handler, this.scope, true);
38319     }
38320 };
38321
38322 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
38323     /**
38324      * @cfg {Function} handler
38325      * A function that will handle the click event of this menu item (defaults to undefined)
38326      */
38327     /**
38328      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
38329      */
38330     canActivate : false,
38331     
38332      /**
38333      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
38334      */
38335     hidden: false,
38336     
38337     /**
38338      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
38339      */
38340     activeClass : "x-menu-item-active",
38341     /**
38342      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
38343      */
38344     hideOnClick : true,
38345     /**
38346      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
38347      */
38348     hideDelay : 100,
38349
38350     // private
38351     ctype: "Roo.menu.BaseItem",
38352
38353     // private
38354     actionMode : "container",
38355
38356     // private
38357     render : function(container, parentMenu){
38358         this.parentMenu = parentMenu;
38359         Roo.menu.BaseItem.superclass.render.call(this, container);
38360         this.container.menuItemId = this.id;
38361     },
38362
38363     // private
38364     onRender : function(container, position){
38365         this.el = Roo.get(this.el);
38366         container.dom.appendChild(this.el.dom);
38367     },
38368
38369     // private
38370     onClick : function(e){
38371         if(!this.disabled && this.fireEvent("click", this, e) !== false
38372                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
38373             this.handleClick(e);
38374         }else{
38375             e.stopEvent();
38376         }
38377     },
38378
38379     // private
38380     activate : function(){
38381         if(this.disabled){
38382             return false;
38383         }
38384         var li = this.container;
38385         li.addClass(this.activeClass);
38386         this.region = li.getRegion().adjust(2, 2, -2, -2);
38387         this.fireEvent("activate", this);
38388         return true;
38389     },
38390
38391     // private
38392     deactivate : function(){
38393         this.container.removeClass(this.activeClass);
38394         this.fireEvent("deactivate", this);
38395     },
38396
38397     // private
38398     shouldDeactivate : function(e){
38399         return !this.region || !this.region.contains(e.getPoint());
38400     },
38401
38402     // private
38403     handleClick : function(e){
38404         if(this.hideOnClick){
38405             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
38406         }
38407     },
38408
38409     // private
38410     expandMenu : function(autoActivate){
38411         // do nothing
38412     },
38413
38414     // private
38415     hideMenu : function(){
38416         // do nothing
38417     }
38418 });/*
38419  * Based on:
38420  * Ext JS Library 1.1.1
38421  * Copyright(c) 2006-2007, Ext JS, LLC.
38422  *
38423  * Originally Released Under LGPL - original licence link has changed is not relivant.
38424  *
38425  * Fork - LGPL
38426  * <script type="text/javascript">
38427  */
38428  
38429 /**
38430  * @class Roo.menu.Adapter
38431  * @extends Roo.menu.BaseItem
38432  * 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.
38433  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
38434  * @constructor
38435  * Creates a new Adapter
38436  * @param {Object} config Configuration options
38437  */
38438 Roo.menu.Adapter = function(component, config){
38439     Roo.menu.Adapter.superclass.constructor.call(this, config);
38440     this.component = component;
38441 };
38442 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
38443     // private
38444     canActivate : true,
38445
38446     // private
38447     onRender : function(container, position){
38448         this.component.render(container);
38449         this.el = this.component.getEl();
38450     },
38451
38452     // private
38453     activate : function(){
38454         if(this.disabled){
38455             return false;
38456         }
38457         this.component.focus();
38458         this.fireEvent("activate", this);
38459         return true;
38460     },
38461
38462     // private
38463     deactivate : function(){
38464         this.fireEvent("deactivate", this);
38465     },
38466
38467     // private
38468     disable : function(){
38469         this.component.disable();
38470         Roo.menu.Adapter.superclass.disable.call(this);
38471     },
38472
38473     // private
38474     enable : function(){
38475         this.component.enable();
38476         Roo.menu.Adapter.superclass.enable.call(this);
38477     }
38478 });/*
38479  * Based on:
38480  * Ext JS Library 1.1.1
38481  * Copyright(c) 2006-2007, Ext JS, LLC.
38482  *
38483  * Originally Released Under LGPL - original licence link has changed is not relivant.
38484  *
38485  * Fork - LGPL
38486  * <script type="text/javascript">
38487  */
38488
38489 /**
38490  * @class Roo.menu.TextItem
38491  * @extends Roo.menu.BaseItem
38492  * Adds a static text string to a menu, usually used as either a heading or group separator.
38493  * Note: old style constructor with text is still supported.
38494  * 
38495  * @constructor
38496  * Creates a new TextItem
38497  * @param {Object} cfg Configuration
38498  */
38499 Roo.menu.TextItem = function(cfg){
38500     if (typeof(cfg) == 'string') {
38501         this.text = cfg;
38502     } else {
38503         Roo.apply(this,cfg);
38504     }
38505     
38506     Roo.menu.TextItem.superclass.constructor.call(this);
38507 };
38508
38509 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
38510     /**
38511      * @cfg {String} text Text to show on item.
38512      */
38513     text : '',
38514     
38515     /**
38516      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38517      */
38518     hideOnClick : false,
38519     /**
38520      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
38521      */
38522     itemCls : "x-menu-text",
38523
38524     // private
38525     onRender : function(){
38526         var s = document.createElement("span");
38527         s.className = this.itemCls;
38528         s.innerHTML = this.text;
38529         this.el = s;
38530         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
38531     }
38532 });/*
38533  * Based on:
38534  * Ext JS Library 1.1.1
38535  * Copyright(c) 2006-2007, Ext JS, LLC.
38536  *
38537  * Originally Released Under LGPL - original licence link has changed is not relivant.
38538  *
38539  * Fork - LGPL
38540  * <script type="text/javascript">
38541  */
38542
38543 /**
38544  * @class Roo.menu.Separator
38545  * @extends Roo.menu.BaseItem
38546  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
38547  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
38548  * @constructor
38549  * @param {Object} config Configuration options
38550  */
38551 Roo.menu.Separator = function(config){
38552     Roo.menu.Separator.superclass.constructor.call(this, config);
38553 };
38554
38555 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
38556     /**
38557      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
38558      */
38559     itemCls : "x-menu-sep",
38560     /**
38561      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
38562      */
38563     hideOnClick : false,
38564
38565     // private
38566     onRender : function(li){
38567         var s = document.createElement("span");
38568         s.className = this.itemCls;
38569         s.innerHTML = "&#160;";
38570         this.el = s;
38571         li.addClass("x-menu-sep-li");
38572         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
38573     }
38574 });/*
38575  * Based on:
38576  * Ext JS Library 1.1.1
38577  * Copyright(c) 2006-2007, Ext JS, LLC.
38578  *
38579  * Originally Released Under LGPL - original licence link has changed is not relivant.
38580  *
38581  * Fork - LGPL
38582  * <script type="text/javascript">
38583  */
38584 /**
38585  * @class Roo.menu.Item
38586  * @extends Roo.menu.BaseItem
38587  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
38588  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
38589  * activation and click handling.
38590  * @constructor
38591  * Creates a new Item
38592  * @param {Object} config Configuration options
38593  */
38594 Roo.menu.Item = function(config){
38595     Roo.menu.Item.superclass.constructor.call(this, config);
38596     if(this.menu){
38597         this.menu = Roo.menu.MenuMgr.get(this.menu);
38598     }
38599 };
38600 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
38601     
38602     /**
38603      * @cfg {String} text
38604      * The text to show on the menu item.
38605      */
38606     text: '',
38607      /**
38608      * @cfg {String} HTML to render in menu
38609      * The text to show on the menu item (HTML version).
38610      */
38611     html: '',
38612     /**
38613      * @cfg {String} icon
38614      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
38615      */
38616     icon: undefined,
38617     /**
38618      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
38619      */
38620     itemCls : "x-menu-item",
38621     /**
38622      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
38623      */
38624     canActivate : true,
38625     /**
38626      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
38627      */
38628     showDelay: 200,
38629     // doc'd in BaseItem
38630     hideDelay: 200,
38631
38632     // private
38633     ctype: "Roo.menu.Item",
38634     
38635     // private
38636     onRender : function(container, position){
38637         var el = document.createElement("a");
38638         el.hideFocus = true;
38639         el.unselectable = "on";
38640         el.href = this.href || "#";
38641         if(this.hrefTarget){
38642             el.target = this.hrefTarget;
38643         }
38644         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
38645         
38646         var html = this.html.length ? this.html  : String.format('{0}',this.text);
38647         
38648         el.innerHTML = String.format(
38649                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
38650                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
38651         this.el = el;
38652         Roo.menu.Item.superclass.onRender.call(this, container, position);
38653     },
38654
38655     /**
38656      * Sets the text to display in this menu item
38657      * @param {String} text The text to display
38658      * @param {Boolean} isHTML true to indicate text is pure html.
38659      */
38660     setText : function(text, isHTML){
38661         if (isHTML) {
38662             this.html = text;
38663         } else {
38664             this.text = text;
38665             this.html = '';
38666         }
38667         if(this.rendered){
38668             var html = this.html.length ? this.html  : String.format('{0}',this.text);
38669      
38670             this.el.update(String.format(
38671                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
38672                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
38673             this.parentMenu.autoWidth();
38674         }
38675     },
38676
38677     // private
38678     handleClick : function(e){
38679         if(!this.href){ // if no link defined, stop the event automatically
38680             e.stopEvent();
38681         }
38682         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
38683     },
38684
38685     // private
38686     activate : function(autoExpand){
38687         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
38688             this.focus();
38689             if(autoExpand){
38690                 this.expandMenu();
38691             }
38692         }
38693         return true;
38694     },
38695
38696     // private
38697     shouldDeactivate : function(e){
38698         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
38699             if(this.menu && this.menu.isVisible()){
38700                 return !this.menu.getEl().getRegion().contains(e.getPoint());
38701             }
38702             return true;
38703         }
38704         return false;
38705     },
38706
38707     // private
38708     deactivate : function(){
38709         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
38710         this.hideMenu();
38711     },
38712
38713     // private
38714     expandMenu : function(autoActivate){
38715         if(!this.disabled && this.menu){
38716             clearTimeout(this.hideTimer);
38717             delete this.hideTimer;
38718             if(!this.menu.isVisible() && !this.showTimer){
38719                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
38720             }else if (this.menu.isVisible() && autoActivate){
38721                 this.menu.tryActivate(0, 1);
38722             }
38723         }
38724     },
38725
38726     // private
38727     deferExpand : function(autoActivate){
38728         delete this.showTimer;
38729         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
38730         if(autoActivate){
38731             this.menu.tryActivate(0, 1);
38732         }
38733     },
38734
38735     // private
38736     hideMenu : function(){
38737         clearTimeout(this.showTimer);
38738         delete this.showTimer;
38739         if(!this.hideTimer && this.menu && this.menu.isVisible()){
38740             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
38741         }
38742     },
38743
38744     // private
38745     deferHide : function(){
38746         delete this.hideTimer;
38747         this.menu.hide();
38748     }
38749 });/*
38750  * Based on:
38751  * Ext JS Library 1.1.1
38752  * Copyright(c) 2006-2007, Ext JS, LLC.
38753  *
38754  * Originally Released Under LGPL - original licence link has changed is not relivant.
38755  *
38756  * Fork - LGPL
38757  * <script type="text/javascript">
38758  */
38759  
38760 /**
38761  * @class Roo.menu.CheckItem
38762  * @extends Roo.menu.Item
38763  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
38764  * @constructor
38765  * Creates a new CheckItem
38766  * @param {Object} config Configuration options
38767  */
38768 Roo.menu.CheckItem = function(config){
38769     Roo.menu.CheckItem.superclass.constructor.call(this, config);
38770     this.addEvents({
38771         /**
38772          * @event beforecheckchange
38773          * Fires before the checked value is set, providing an opportunity to cancel if needed
38774          * @param {Roo.menu.CheckItem} this
38775          * @param {Boolean} checked The new checked value that will be set
38776          */
38777         "beforecheckchange" : true,
38778         /**
38779          * @event checkchange
38780          * Fires after the checked value has been set
38781          * @param {Roo.menu.CheckItem} this
38782          * @param {Boolean} checked The checked value that was set
38783          */
38784         "checkchange" : true
38785     });
38786     if(this.checkHandler){
38787         this.on('checkchange', this.checkHandler, this.scope);
38788     }
38789 };
38790 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
38791     /**
38792      * @cfg {String} group
38793      * All check items with the same group name will automatically be grouped into a single-select
38794      * radio button group (defaults to '')
38795      */
38796     /**
38797      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
38798      */
38799     itemCls : "x-menu-item x-menu-check-item",
38800     /**
38801      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
38802      */
38803     groupClass : "x-menu-group-item",
38804
38805     /**
38806      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
38807      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
38808      * initialized with checked = true will be rendered as checked.
38809      */
38810     checked: false,
38811
38812     // private
38813     ctype: "Roo.menu.CheckItem",
38814
38815     // private
38816     onRender : function(c){
38817         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
38818         if(this.group){
38819             this.el.addClass(this.groupClass);
38820         }
38821         Roo.menu.MenuMgr.registerCheckable(this);
38822         if(this.checked){
38823             this.checked = false;
38824             this.setChecked(true, true);
38825         }
38826     },
38827
38828     // private
38829     destroy : function(){
38830         if(this.rendered){
38831             Roo.menu.MenuMgr.unregisterCheckable(this);
38832         }
38833         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
38834     },
38835
38836     /**
38837      * Set the checked state of this item
38838      * @param {Boolean} checked The new checked value
38839      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
38840      */
38841     setChecked : function(state, suppressEvent){
38842         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
38843             if(this.container){
38844                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
38845             }
38846             this.checked = state;
38847             if(suppressEvent !== true){
38848                 this.fireEvent("checkchange", this, state);
38849             }
38850         }
38851     },
38852
38853     // private
38854     handleClick : function(e){
38855        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
38856            this.setChecked(!this.checked);
38857        }
38858        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
38859     }
38860 });/*
38861  * Based on:
38862  * Ext JS Library 1.1.1
38863  * Copyright(c) 2006-2007, Ext JS, LLC.
38864  *
38865  * Originally Released Under LGPL - original licence link has changed is not relivant.
38866  *
38867  * Fork - LGPL
38868  * <script type="text/javascript">
38869  */
38870  
38871 /**
38872  * @class Roo.menu.DateItem
38873  * @extends Roo.menu.Adapter
38874  * A menu item that wraps the {@link Roo.DatPicker} component.
38875  * @constructor
38876  * Creates a new DateItem
38877  * @param {Object} config Configuration options
38878  */
38879 Roo.menu.DateItem = function(config){
38880     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
38881     /** The Roo.DatePicker object @type Roo.DatePicker */
38882     this.picker = this.component;
38883     this.addEvents({select: true});
38884     
38885     this.picker.on("render", function(picker){
38886         picker.getEl().swallowEvent("click");
38887         picker.container.addClass("x-menu-date-item");
38888     });
38889
38890     this.picker.on("select", this.onSelect, this);
38891 };
38892
38893 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
38894     // private
38895     onSelect : function(picker, date){
38896         this.fireEvent("select", this, date, picker);
38897         Roo.menu.DateItem.superclass.handleClick.call(this);
38898     }
38899 });/*
38900  * Based on:
38901  * Ext JS Library 1.1.1
38902  * Copyright(c) 2006-2007, Ext JS, LLC.
38903  *
38904  * Originally Released Under LGPL - original licence link has changed is not relivant.
38905  *
38906  * Fork - LGPL
38907  * <script type="text/javascript">
38908  */
38909  
38910 /**
38911  * @class Roo.menu.ColorItem
38912  * @extends Roo.menu.Adapter
38913  * A menu item that wraps the {@link Roo.ColorPalette} component.
38914  * @constructor
38915  * Creates a new ColorItem
38916  * @param {Object} config Configuration options
38917  */
38918 Roo.menu.ColorItem = function(config){
38919     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
38920     /** The Roo.ColorPalette object @type Roo.ColorPalette */
38921     this.palette = this.component;
38922     this.relayEvents(this.palette, ["select"]);
38923     if(this.selectHandler){
38924         this.on('select', this.selectHandler, this.scope);
38925     }
38926 };
38927 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
38928  * Based on:
38929  * Ext JS Library 1.1.1
38930  * Copyright(c) 2006-2007, Ext JS, LLC.
38931  *
38932  * Originally Released Under LGPL - original licence link has changed is not relivant.
38933  *
38934  * Fork - LGPL
38935  * <script type="text/javascript">
38936  */
38937  
38938
38939 /**
38940  * @class Roo.menu.DateMenu
38941  * @extends Roo.menu.Menu
38942  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
38943  * @constructor
38944  * Creates a new DateMenu
38945  * @param {Object} config Configuration options
38946  */
38947 Roo.menu.DateMenu = function(config){
38948     Roo.menu.DateMenu.superclass.constructor.call(this, config);
38949     this.plain = true;
38950     var di = new Roo.menu.DateItem(config);
38951     this.add(di);
38952     /**
38953      * The {@link Roo.DatePicker} instance for this DateMenu
38954      * @type DatePicker
38955      */
38956     this.picker = di.picker;
38957     /**
38958      * @event select
38959      * @param {DatePicker} picker
38960      * @param {Date} date
38961      */
38962     this.relayEvents(di, ["select"]);
38963     this.on('beforeshow', function(){
38964         if(this.picker){
38965             this.picker.hideMonthPicker(false);
38966         }
38967     }, this);
38968 };
38969 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
38970     cls:'x-date-menu'
38971 });/*
38972  * Based on:
38973  * Ext JS Library 1.1.1
38974  * Copyright(c) 2006-2007, Ext JS, LLC.
38975  *
38976  * Originally Released Under LGPL - original licence link has changed is not relivant.
38977  *
38978  * Fork - LGPL
38979  * <script type="text/javascript">
38980  */
38981  
38982
38983 /**
38984  * @class Roo.menu.ColorMenu
38985  * @extends Roo.menu.Menu
38986  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
38987  * @constructor
38988  * Creates a new ColorMenu
38989  * @param {Object} config Configuration options
38990  */
38991 Roo.menu.ColorMenu = function(config){
38992     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
38993     this.plain = true;
38994     var ci = new Roo.menu.ColorItem(config);
38995     this.add(ci);
38996     /**
38997      * The {@link Roo.ColorPalette} instance for this ColorMenu
38998      * @type ColorPalette
38999      */
39000     this.palette = ci.palette;
39001     /**
39002      * @event select
39003      * @param {ColorPalette} palette
39004      * @param {String} color
39005      */
39006     this.relayEvents(ci, ["select"]);
39007 };
39008 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
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  * @class Roo.form.TextItem
39021  * @extends Roo.BoxComponent
39022  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39023  * @constructor
39024  * Creates a new TextItem
39025  * @param {Object} config Configuration options
39026  */
39027 Roo.form.TextItem = function(config){
39028     Roo.form.TextItem.superclass.constructor.call(this, config);
39029 };
39030
39031 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39032     
39033     /**
39034      * @cfg {String} tag the tag for this item (default div)
39035      */
39036     tag : 'div',
39037     /**
39038      * @cfg {String} html the content for this item
39039      */
39040     html : '',
39041     
39042     getAutoCreate : function()
39043     {
39044         var cfg = {
39045             id: this.id,
39046             tag: this.tag,
39047             html: this.html,
39048             cls: 'x-form-item'
39049         };
39050         
39051         return cfg;
39052         
39053     },
39054     
39055     onRender : function(ct, position)
39056     {
39057         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39058         
39059         if(!this.el){
39060             var cfg = this.getAutoCreate();
39061             if(!cfg.name){
39062                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39063             }
39064             if (!cfg.name.length) {
39065                 delete cfg.name;
39066             }
39067             this.el = ct.createChild(cfg, position);
39068         }
39069     },
39070     /*
39071      * setHTML
39072      * @param {String} html update the Contents of the element.
39073      */
39074     setHTML : function(html)
39075     {
39076         this.fieldEl.dom.innerHTML = html;
39077     }
39078     
39079 });/*
39080  * Based on:
39081  * Ext JS Library 1.1.1
39082  * Copyright(c) 2006-2007, Ext JS, LLC.
39083  *
39084  * Originally Released Under LGPL - original licence link has changed is not relivant.
39085  *
39086  * Fork - LGPL
39087  * <script type="text/javascript">
39088  */
39089  
39090 /**
39091  * @class Roo.form.Field
39092  * @extends Roo.BoxComponent
39093  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39094  * @constructor
39095  * Creates a new Field
39096  * @param {Object} config Configuration options
39097  */
39098 Roo.form.Field = function(config){
39099     Roo.form.Field.superclass.constructor.call(this, config);
39100 };
39101
39102 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39103     /**
39104      * @cfg {String} fieldLabel Label to use when rendering a form.
39105      */
39106        /**
39107      * @cfg {String} qtip Mouse over tip
39108      */
39109      
39110     /**
39111      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39112      */
39113     invalidClass : "x-form-invalid",
39114     /**
39115      * @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")
39116      */
39117     invalidText : "The value in this field is invalid",
39118     /**
39119      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39120      */
39121     focusClass : "x-form-focus",
39122     /**
39123      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39124       automatic validation (defaults to "keyup").
39125      */
39126     validationEvent : "keyup",
39127     /**
39128      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39129      */
39130     validateOnBlur : true,
39131     /**
39132      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39133      */
39134     validationDelay : 250,
39135     /**
39136      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39137      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39138      */
39139     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39140     /**
39141      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39142      */
39143     fieldClass : "x-form-field",
39144     /**
39145      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39146      *<pre>
39147 Value         Description
39148 -----------   ----------------------------------------------------------------------
39149 qtip          Display a quick tip when the user hovers over the field
39150 title         Display a default browser title attribute popup
39151 under         Add a block div beneath the field containing the error text
39152 side          Add an error icon to the right of the field with a popup on hover
39153 [element id]  Add the error text directly to the innerHTML of the specified element
39154 </pre>
39155      */
39156     msgTarget : 'qtip',
39157     /**
39158      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39159      */
39160     msgFx : 'normal',
39161
39162     /**
39163      * @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.
39164      */
39165     readOnly : false,
39166
39167     /**
39168      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39169      */
39170     disabled : false,
39171
39172     /**
39173      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39174      */
39175     inputType : undefined,
39176     
39177     /**
39178      * @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).
39179          */
39180         tabIndex : undefined,
39181         
39182     // private
39183     isFormField : true,
39184
39185     // private
39186     hasFocus : false,
39187     /**
39188      * @property {Roo.Element} fieldEl
39189      * Element Containing the rendered Field (with label etc.)
39190      */
39191     /**
39192      * @cfg {Mixed} value A value to initialize this field with.
39193      */
39194     value : undefined,
39195
39196     /**
39197      * @cfg {String} name The field's HTML name attribute.
39198      */
39199     /**
39200      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39201      */
39202     // private
39203     loadedValue : false,
39204      
39205      
39206         // private ??
39207         initComponent : function(){
39208         Roo.form.Field.superclass.initComponent.call(this);
39209         this.addEvents({
39210             /**
39211              * @event focus
39212              * Fires when this field receives input focus.
39213              * @param {Roo.form.Field} this
39214              */
39215             focus : true,
39216             /**
39217              * @event blur
39218              * Fires when this field loses input focus.
39219              * @param {Roo.form.Field} this
39220              */
39221             blur : true,
39222             /**
39223              * @event specialkey
39224              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
39225              * {@link Roo.EventObject#getKey} to determine which key was pressed.
39226              * @param {Roo.form.Field} this
39227              * @param {Roo.EventObject} e The event object
39228              */
39229             specialkey : true,
39230             /**
39231              * @event change
39232              * Fires just before the field blurs if the field value has changed.
39233              * @param {Roo.form.Field} this
39234              * @param {Mixed} newValue The new value
39235              * @param {Mixed} oldValue The original value
39236              */
39237             change : true,
39238             /**
39239              * @event invalid
39240              * Fires after the field has been marked as invalid.
39241              * @param {Roo.form.Field} this
39242              * @param {String} msg The validation message
39243              */
39244             invalid : true,
39245             /**
39246              * @event valid
39247              * Fires after the field has been validated with no errors.
39248              * @param {Roo.form.Field} this
39249              */
39250             valid : true,
39251              /**
39252              * @event keyup
39253              * Fires after the key up
39254              * @param {Roo.form.Field} this
39255              * @param {Roo.EventObject}  e The event Object
39256              */
39257             keyup : true
39258         });
39259     },
39260
39261     /**
39262      * Returns the name attribute of the field if available
39263      * @return {String} name The field name
39264      */
39265     getName: function(){
39266          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
39267     },
39268
39269     // private
39270     onRender : function(ct, position){
39271         Roo.form.Field.superclass.onRender.call(this, ct, position);
39272         if(!this.el){
39273             var cfg = this.getAutoCreate();
39274             if(!cfg.name){
39275                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39276             }
39277             if (!cfg.name.length) {
39278                 delete cfg.name;
39279             }
39280             if(this.inputType){
39281                 cfg.type = this.inputType;
39282             }
39283             this.el = ct.createChild(cfg, position);
39284         }
39285         var type = this.el.dom.type;
39286         if(type){
39287             if(type == 'password'){
39288                 type = 'text';
39289             }
39290             this.el.addClass('x-form-'+type);
39291         }
39292         if(this.readOnly){
39293             this.el.dom.readOnly = true;
39294         }
39295         if(this.tabIndex !== undefined){
39296             this.el.dom.setAttribute('tabIndex', this.tabIndex);
39297         }
39298
39299         this.el.addClass([this.fieldClass, this.cls]);
39300         this.initValue();
39301     },
39302
39303     /**
39304      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
39305      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
39306      * @return {Roo.form.Field} this
39307      */
39308     applyTo : function(target){
39309         this.allowDomMove = false;
39310         this.el = Roo.get(target);
39311         this.render(this.el.dom.parentNode);
39312         return this;
39313     },
39314
39315     // private
39316     initValue : function(){
39317         if(this.value !== undefined){
39318             this.setValue(this.value);
39319         }else if(this.el.dom.value.length > 0){
39320             this.setValue(this.el.dom.value);
39321         }
39322     },
39323
39324     /**
39325      * Returns true if this field has been changed since it was originally loaded and is not disabled.
39326      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
39327      */
39328     isDirty : function() {
39329         if(this.disabled) {
39330             return false;
39331         }
39332         return String(this.getValue()) !== String(this.originalValue);
39333     },
39334
39335     /**
39336      * stores the current value in loadedValue
39337      */
39338     resetHasChanged : function()
39339     {
39340         this.loadedValue = String(this.getValue());
39341     },
39342     /**
39343      * checks the current value against the 'loaded' value.
39344      * Note - will return false if 'resetHasChanged' has not been called first.
39345      */
39346     hasChanged : function()
39347     {
39348         if(this.disabled || this.readOnly) {
39349             return false;
39350         }
39351         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
39352     },
39353     
39354     
39355     
39356     // private
39357     afterRender : function(){
39358         Roo.form.Field.superclass.afterRender.call(this);
39359         this.initEvents();
39360     },
39361
39362     // private
39363     fireKey : function(e){
39364         //Roo.log('field ' + e.getKey());
39365         if(e.isNavKeyPress()){
39366             this.fireEvent("specialkey", this, e);
39367         }
39368     },
39369
39370     /**
39371      * Resets the current field value to the originally loaded value and clears any validation messages
39372      */
39373     reset : function(){
39374         this.setValue(this.resetValue);
39375         this.originalValue = this.getValue();
39376         this.clearInvalid();
39377     },
39378
39379     // private
39380     initEvents : function(){
39381         // safari killled keypress - so keydown is now used..
39382         this.el.on("keydown" , this.fireKey,  this);
39383         this.el.on("focus", this.onFocus,  this);
39384         this.el.on("blur", this.onBlur,  this);
39385         this.el.relayEvent('keyup', this);
39386
39387         // reference to original value for reset
39388         this.originalValue = this.getValue();
39389         this.resetValue =  this.getValue();
39390     },
39391
39392     // private
39393     onFocus : function(){
39394         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39395             this.el.addClass(this.focusClass);
39396         }
39397         if(!this.hasFocus){
39398             this.hasFocus = true;
39399             this.startValue = this.getValue();
39400             this.fireEvent("focus", this);
39401         }
39402     },
39403
39404     beforeBlur : Roo.emptyFn,
39405
39406     // private
39407     onBlur : function(){
39408         this.beforeBlur();
39409         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
39410             this.el.removeClass(this.focusClass);
39411         }
39412         this.hasFocus = false;
39413         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
39414             this.validate();
39415         }
39416         var v = this.getValue();
39417         if(String(v) !== String(this.startValue)){
39418             this.fireEvent('change', this, v, this.startValue);
39419         }
39420         this.fireEvent("blur", this);
39421     },
39422
39423     /**
39424      * Returns whether or not the field value is currently valid
39425      * @param {Boolean} preventMark True to disable marking the field invalid
39426      * @return {Boolean} True if the value is valid, else false
39427      */
39428     isValid : function(preventMark){
39429         if(this.disabled){
39430             return true;
39431         }
39432         var restore = this.preventMark;
39433         this.preventMark = preventMark === true;
39434         var v = this.validateValue(this.processValue(this.getRawValue()));
39435         this.preventMark = restore;
39436         return v;
39437     },
39438
39439     /**
39440      * Validates the field value
39441      * @return {Boolean} True if the value is valid, else false
39442      */
39443     validate : function(){
39444         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
39445             this.clearInvalid();
39446             return true;
39447         }
39448         return false;
39449     },
39450
39451     processValue : function(value){
39452         return value;
39453     },
39454
39455     // private
39456     // Subclasses should provide the validation implementation by overriding this
39457     validateValue : function(value){
39458         return true;
39459     },
39460
39461     /**
39462      * Mark this field as invalid
39463      * @param {String} msg The validation message
39464      */
39465     markInvalid : function(msg){
39466         if(!this.rendered || this.preventMark){ // not rendered
39467             return;
39468         }
39469         
39470         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39471         
39472         obj.el.addClass(this.invalidClass);
39473         msg = msg || this.invalidText;
39474         switch(this.msgTarget){
39475             case 'qtip':
39476                 obj.el.dom.qtip = msg;
39477                 obj.el.dom.qclass = 'x-form-invalid-tip';
39478                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
39479                     Roo.QuickTips.enable();
39480                 }
39481                 break;
39482             case 'title':
39483                 this.el.dom.title = msg;
39484                 break;
39485             case 'under':
39486                 if(!this.errorEl){
39487                     var elp = this.el.findParent('.x-form-element', 5, true);
39488                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
39489                     this.errorEl.setWidth(elp.getWidth(true)-20);
39490                 }
39491                 this.errorEl.update(msg);
39492                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
39493                 break;
39494             case 'side':
39495                 if(!this.errorIcon){
39496                     var elp = this.el.findParent('.x-form-element', 5, true);
39497                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
39498                 }
39499                 this.alignErrorIcon();
39500                 this.errorIcon.dom.qtip = msg;
39501                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
39502                 this.errorIcon.show();
39503                 this.on('resize', this.alignErrorIcon, this);
39504                 break;
39505             default:
39506                 var t = Roo.getDom(this.msgTarget);
39507                 t.innerHTML = msg;
39508                 t.style.display = this.msgDisplay;
39509                 break;
39510         }
39511         this.fireEvent('invalid', this, msg);
39512     },
39513
39514     // private
39515     alignErrorIcon : function(){
39516         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
39517     },
39518
39519     /**
39520      * Clear any invalid styles/messages for this field
39521      */
39522     clearInvalid : function(){
39523         if(!this.rendered || this.preventMark){ // not rendered
39524             return;
39525         }
39526         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
39527         
39528         obj.el.removeClass(this.invalidClass);
39529         switch(this.msgTarget){
39530             case 'qtip':
39531                 obj.el.dom.qtip = '';
39532                 break;
39533             case 'title':
39534                 this.el.dom.title = '';
39535                 break;
39536             case 'under':
39537                 if(this.errorEl){
39538                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
39539                 }
39540                 break;
39541             case 'side':
39542                 if(this.errorIcon){
39543                     this.errorIcon.dom.qtip = '';
39544                     this.errorIcon.hide();
39545                     this.un('resize', this.alignErrorIcon, this);
39546                 }
39547                 break;
39548             default:
39549                 var t = Roo.getDom(this.msgTarget);
39550                 t.innerHTML = '';
39551                 t.style.display = 'none';
39552                 break;
39553         }
39554         this.fireEvent('valid', this);
39555     },
39556
39557     /**
39558      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
39559      * @return {Mixed} value The field value
39560      */
39561     getRawValue : function(){
39562         var v = this.el.getValue();
39563         
39564         return v;
39565     },
39566
39567     /**
39568      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
39569      * @return {Mixed} value The field value
39570      */
39571     getValue : function(){
39572         var v = this.el.getValue();
39573          
39574         return v;
39575     },
39576
39577     /**
39578      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
39579      * @param {Mixed} value The value to set
39580      */
39581     setRawValue : function(v){
39582         return this.el.dom.value = (v === null || v === undefined ? '' : v);
39583     },
39584
39585     /**
39586      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
39587      * @param {Mixed} value The value to set
39588      */
39589     setValue : function(v){
39590         this.value = v;
39591         if(this.rendered){
39592             this.el.dom.value = (v === null || v === undefined ? '' : v);
39593              this.validate();
39594         }
39595     },
39596
39597     adjustSize : function(w, h){
39598         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
39599         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
39600         return s;
39601     },
39602
39603     adjustWidth : function(tag, w){
39604         tag = tag.toLowerCase();
39605         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
39606             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
39607                 if(tag == 'input'){
39608                     return w + 2;
39609                 }
39610                 if(tag == 'textarea'){
39611                     return w-2;
39612                 }
39613             }else if(Roo.isOpera){
39614                 if(tag == 'input'){
39615                     return w + 2;
39616                 }
39617                 if(tag == 'textarea'){
39618                     return w-2;
39619                 }
39620             }
39621         }
39622         return w;
39623     }
39624 });
39625
39626
39627 // anything other than normal should be considered experimental
39628 Roo.form.Field.msgFx = {
39629     normal : {
39630         show: function(msgEl, f){
39631             msgEl.setDisplayed('block');
39632         },
39633
39634         hide : function(msgEl, f){
39635             msgEl.setDisplayed(false).update('');
39636         }
39637     },
39638
39639     slide : {
39640         show: function(msgEl, f){
39641             msgEl.slideIn('t', {stopFx:true});
39642         },
39643
39644         hide : function(msgEl, f){
39645             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
39646         }
39647     },
39648
39649     slideRight : {
39650         show: function(msgEl, f){
39651             msgEl.fixDisplay();
39652             msgEl.alignTo(f.el, 'tl-tr');
39653             msgEl.slideIn('l', {stopFx:true});
39654         },
39655
39656         hide : function(msgEl, f){
39657             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
39658         }
39659     }
39660 };/*
39661  * Based on:
39662  * Ext JS Library 1.1.1
39663  * Copyright(c) 2006-2007, Ext JS, LLC.
39664  *
39665  * Originally Released Under LGPL - original licence link has changed is not relivant.
39666  *
39667  * Fork - LGPL
39668  * <script type="text/javascript">
39669  */
39670  
39671
39672 /**
39673  * @class Roo.form.TextField
39674  * @extends Roo.form.Field
39675  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
39676  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
39677  * @constructor
39678  * Creates a new TextField
39679  * @param {Object} config Configuration options
39680  */
39681 Roo.form.TextField = function(config){
39682     Roo.form.TextField.superclass.constructor.call(this, config);
39683     this.addEvents({
39684         /**
39685          * @event autosize
39686          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
39687          * according to the default logic, but this event provides a hook for the developer to apply additional
39688          * logic at runtime to resize the field if needed.
39689              * @param {Roo.form.Field} this This text field
39690              * @param {Number} width The new field width
39691              */
39692         autosize : true
39693     });
39694 };
39695
39696 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
39697     /**
39698      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
39699      */
39700     grow : false,
39701     /**
39702      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
39703      */
39704     growMin : 30,
39705     /**
39706      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
39707      */
39708     growMax : 800,
39709     /**
39710      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
39711      */
39712     vtype : null,
39713     /**
39714      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
39715      */
39716     maskRe : null,
39717     /**
39718      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
39719      */
39720     disableKeyFilter : false,
39721     /**
39722      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
39723      */
39724     allowBlank : true,
39725     /**
39726      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
39727      */
39728     minLength : 0,
39729     /**
39730      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
39731      */
39732     maxLength : Number.MAX_VALUE,
39733     /**
39734      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
39735      */
39736     minLengthText : "The minimum length for this field is {0}",
39737     /**
39738      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
39739      */
39740     maxLengthText : "The maximum length for this field is {0}",
39741     /**
39742      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
39743      */
39744     selectOnFocus : false,
39745     /**
39746      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
39747      */    
39748     allowLeadingSpace : false,
39749     /**
39750      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
39751      */
39752     blankText : "This field is required",
39753     /**
39754      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
39755      * If available, this function will be called only after the basic validators all return true, and will be passed the
39756      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
39757      */
39758     validator : null,
39759     /**
39760      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
39761      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
39762      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
39763      */
39764     regex : null,
39765     /**
39766      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
39767      */
39768     regexText : "",
39769     /**
39770      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
39771      */
39772     emptyText : null,
39773    
39774
39775     // private
39776     initEvents : function()
39777     {
39778         if (this.emptyText) {
39779             this.el.attr('placeholder', this.emptyText);
39780         }
39781         
39782         Roo.form.TextField.superclass.initEvents.call(this);
39783         if(this.validationEvent == 'keyup'){
39784             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
39785             this.el.on('keyup', this.filterValidation, this);
39786         }
39787         else if(this.validationEvent !== false){
39788             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
39789         }
39790         
39791         if(this.selectOnFocus){
39792             this.on("focus", this.preFocus, this);
39793         }
39794         if (!this.allowLeadingSpace) {
39795             this.on('blur', this.cleanLeadingSpace, this);
39796         }
39797         
39798         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
39799             this.el.on("keypress", this.filterKeys, this);
39800         }
39801         if(this.grow){
39802             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
39803             this.el.on("click", this.autoSize,  this);
39804         }
39805         if(this.el.is('input[type=password]') && Roo.isSafari){
39806             this.el.on('keydown', this.SafariOnKeyDown, this);
39807         }
39808     },
39809
39810     processValue : function(value){
39811         if(this.stripCharsRe){
39812             var newValue = value.replace(this.stripCharsRe, '');
39813             if(newValue !== value){
39814                 this.setRawValue(newValue);
39815                 return newValue;
39816             }
39817         }
39818         return value;
39819     },
39820
39821     filterValidation : function(e){
39822         if(!e.isNavKeyPress()){
39823             this.validationTask.delay(this.validationDelay);
39824         }
39825     },
39826
39827     // private
39828     onKeyUp : function(e){
39829         if(!e.isNavKeyPress()){
39830             this.autoSize();
39831         }
39832     },
39833     // private - clean the leading white space
39834     cleanLeadingSpace : function(e)
39835     {
39836         if ( this.inputType == 'file') {
39837             return;
39838         }
39839         
39840         this.setValue((this.getValue() + '').replace(/^\s+/,''));
39841     },
39842     /**
39843      * Resets the current field value to the originally-loaded value and clears any validation messages.
39844      *  
39845      */
39846     reset : function(){
39847         Roo.form.TextField.superclass.reset.call(this);
39848        
39849     }, 
39850     // private
39851     preFocus : function(){
39852         
39853         if(this.selectOnFocus){
39854             this.el.dom.select();
39855         }
39856     },
39857
39858     
39859     // private
39860     filterKeys : function(e){
39861         var k = e.getKey();
39862         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
39863             return;
39864         }
39865         var c = e.getCharCode(), cc = String.fromCharCode(c);
39866         if(Roo.isIE && (e.isSpecialKey() || !cc)){
39867             return;
39868         }
39869         if(!this.maskRe.test(cc)){
39870             e.stopEvent();
39871         }
39872     },
39873
39874     setValue : function(v){
39875         
39876         Roo.form.TextField.superclass.setValue.apply(this, arguments);
39877         
39878         this.autoSize();
39879     },
39880
39881     /**
39882      * Validates a value according to the field's validation rules and marks the field as invalid
39883      * if the validation fails
39884      * @param {Mixed} value The value to validate
39885      * @return {Boolean} True if the value is valid, else false
39886      */
39887     validateValue : function(value){
39888         if(value.length < 1)  { // if it's blank
39889              if(this.allowBlank){
39890                 this.clearInvalid();
39891                 return true;
39892              }else{
39893                 this.markInvalid(this.blankText);
39894                 return false;
39895              }
39896         }
39897         if(value.length < this.minLength){
39898             this.markInvalid(String.format(this.minLengthText, this.minLength));
39899             return false;
39900         }
39901         if(value.length > this.maxLength){
39902             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
39903             return false;
39904         }
39905         if(this.vtype){
39906             var vt = Roo.form.VTypes;
39907             if(!vt[this.vtype](value, this)){
39908                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
39909                 return false;
39910             }
39911         }
39912         if(typeof this.validator == "function"){
39913             var msg = this.validator(value);
39914             if(msg !== true){
39915                 this.markInvalid(msg);
39916                 return false;
39917             }
39918         }
39919         if(this.regex && !this.regex.test(value)){
39920             this.markInvalid(this.regexText);
39921             return false;
39922         }
39923         return true;
39924     },
39925
39926     /**
39927      * Selects text in this field
39928      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
39929      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
39930      */
39931     selectText : function(start, end){
39932         var v = this.getRawValue();
39933         if(v.length > 0){
39934             start = start === undefined ? 0 : start;
39935             end = end === undefined ? v.length : end;
39936             var d = this.el.dom;
39937             if(d.setSelectionRange){
39938                 d.setSelectionRange(start, end);
39939             }else if(d.createTextRange){
39940                 var range = d.createTextRange();
39941                 range.moveStart("character", start);
39942                 range.moveEnd("character", v.length-end);
39943                 range.select();
39944             }
39945         }
39946     },
39947
39948     /**
39949      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
39950      * This only takes effect if grow = true, and fires the autosize event.
39951      */
39952     autoSize : function(){
39953         if(!this.grow || !this.rendered){
39954             return;
39955         }
39956         if(!this.metrics){
39957             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
39958         }
39959         var el = this.el;
39960         var v = el.dom.value;
39961         var d = document.createElement('div');
39962         d.appendChild(document.createTextNode(v));
39963         v = d.innerHTML;
39964         d = null;
39965         v += "&#160;";
39966         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
39967         this.el.setWidth(w);
39968         this.fireEvent("autosize", this, w);
39969     },
39970     
39971     // private
39972     SafariOnKeyDown : function(event)
39973     {
39974         // this is a workaround for a password hang bug on chrome/ webkit.
39975         
39976         var isSelectAll = false;
39977         
39978         if(this.el.dom.selectionEnd > 0){
39979             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
39980         }
39981         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
39982             event.preventDefault();
39983             this.setValue('');
39984             return;
39985         }
39986         
39987         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
39988             
39989             event.preventDefault();
39990             // this is very hacky as keydown always get's upper case.
39991             
39992             var cc = String.fromCharCode(event.getCharCode());
39993             
39994             
39995             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
39996             
39997         }
39998         
39999         
40000     }
40001 });/*
40002  * Based on:
40003  * Ext JS Library 1.1.1
40004  * Copyright(c) 2006-2007, Ext JS, LLC.
40005  *
40006  * Originally Released Under LGPL - original licence link has changed is not relivant.
40007  *
40008  * Fork - LGPL
40009  * <script type="text/javascript">
40010  */
40011  
40012 /**
40013  * @class Roo.form.Hidden
40014  * @extends Roo.form.TextField
40015  * Simple Hidden element used on forms 
40016  * 
40017  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40018  * 
40019  * @constructor
40020  * Creates a new Hidden form element.
40021  * @param {Object} config Configuration options
40022  */
40023
40024
40025
40026 // easy hidden field...
40027 Roo.form.Hidden = function(config){
40028     Roo.form.Hidden.superclass.constructor.call(this, config);
40029 };
40030   
40031 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40032     fieldLabel:      '',
40033     inputType:      'hidden',
40034     width:          50,
40035     allowBlank:     true,
40036     labelSeparator: '',
40037     hidden:         true,
40038     itemCls :       'x-form-item-display-none'
40039
40040
40041 });
40042
40043
40044 /*
40045  * Based on:
40046  * Ext JS Library 1.1.1
40047  * Copyright(c) 2006-2007, Ext JS, LLC.
40048  *
40049  * Originally Released Under LGPL - original licence link has changed is not relivant.
40050  *
40051  * Fork - LGPL
40052  * <script type="text/javascript">
40053  */
40054  
40055 /**
40056  * @class Roo.form.TriggerField
40057  * @extends Roo.form.TextField
40058  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40059  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40060  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40061  * for which you can provide a custom implementation.  For example:
40062  * <pre><code>
40063 var trigger = new Roo.form.TriggerField();
40064 trigger.onTriggerClick = myTriggerFn;
40065 trigger.applyTo('my-field');
40066 </code></pre>
40067  *
40068  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40069  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40070  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40071  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40072  * @constructor
40073  * Create a new TriggerField.
40074  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40075  * to the base TextField)
40076  */
40077 Roo.form.TriggerField = function(config){
40078     this.mimicing = false;
40079     Roo.form.TriggerField.superclass.constructor.call(this, config);
40080 };
40081
40082 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40083     /**
40084      * @cfg {String} triggerClass A CSS class to apply to the trigger
40085      */
40086     /**
40087      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40088      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40089      */
40090     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40091     /**
40092      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40093      */
40094     hideTrigger:false,
40095
40096     /** @cfg {Boolean} grow @hide */
40097     /** @cfg {Number} growMin @hide */
40098     /** @cfg {Number} growMax @hide */
40099
40100     /**
40101      * @hide 
40102      * @method
40103      */
40104     autoSize: Roo.emptyFn,
40105     // private
40106     monitorTab : true,
40107     // private
40108     deferHeight : true,
40109
40110     
40111     actionMode : 'wrap',
40112     // private
40113     onResize : function(w, h){
40114         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40115         if(typeof w == 'number'){
40116             var x = w - this.trigger.getWidth();
40117             this.el.setWidth(this.adjustWidth('input', x));
40118             this.trigger.setStyle('left', x+'px');
40119         }
40120     },
40121
40122     // private
40123     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40124
40125     // private
40126     getResizeEl : function(){
40127         return this.wrap;
40128     },
40129
40130     // private
40131     getPositionEl : function(){
40132         return this.wrap;
40133     },
40134
40135     // private
40136     alignErrorIcon : function(){
40137         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40138     },
40139
40140     // private
40141     onRender : function(ct, position){
40142         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40143         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40144         this.trigger = this.wrap.createChild(this.triggerConfig ||
40145                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40146         if(this.hideTrigger){
40147             this.trigger.setDisplayed(false);
40148         }
40149         this.initTrigger();
40150         if(!this.width){
40151             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40152         }
40153     },
40154
40155     // private
40156     initTrigger : function(){
40157         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40158         this.trigger.addClassOnOver('x-form-trigger-over');
40159         this.trigger.addClassOnClick('x-form-trigger-click');
40160     },
40161
40162     // private
40163     onDestroy : function(){
40164         if(this.trigger){
40165             this.trigger.removeAllListeners();
40166             this.trigger.remove();
40167         }
40168         if(this.wrap){
40169             this.wrap.remove();
40170         }
40171         Roo.form.TriggerField.superclass.onDestroy.call(this);
40172     },
40173
40174     // private
40175     onFocus : function(){
40176         Roo.form.TriggerField.superclass.onFocus.call(this);
40177         if(!this.mimicing){
40178             this.wrap.addClass('x-trigger-wrap-focus');
40179             this.mimicing = true;
40180             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40181             if(this.monitorTab){
40182                 this.el.on("keydown", this.checkTab, this);
40183             }
40184         }
40185     },
40186
40187     // private
40188     checkTab : function(e){
40189         if(e.getKey() == e.TAB){
40190             this.triggerBlur();
40191         }
40192     },
40193
40194     // private
40195     onBlur : function(){
40196         // do nothing
40197     },
40198
40199     // private
40200     mimicBlur : function(e, t){
40201         if(!this.wrap.contains(t) && this.validateBlur()){
40202             this.triggerBlur();
40203         }
40204     },
40205
40206     // private
40207     triggerBlur : function(){
40208         this.mimicing = false;
40209         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40210         if(this.monitorTab){
40211             this.el.un("keydown", this.checkTab, this);
40212         }
40213         this.wrap.removeClass('x-trigger-wrap-focus');
40214         Roo.form.TriggerField.superclass.onBlur.call(this);
40215     },
40216
40217     // private
40218     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40219     validateBlur : function(e, t){
40220         return true;
40221     },
40222
40223     // private
40224     onDisable : function(){
40225         Roo.form.TriggerField.superclass.onDisable.call(this);
40226         if(this.wrap){
40227             this.wrap.addClass('x-item-disabled');
40228         }
40229     },
40230
40231     // private
40232     onEnable : function(){
40233         Roo.form.TriggerField.superclass.onEnable.call(this);
40234         if(this.wrap){
40235             this.wrap.removeClass('x-item-disabled');
40236         }
40237     },
40238
40239     // private
40240     onShow : function(){
40241         var ae = this.getActionEl();
40242         
40243         if(ae){
40244             ae.dom.style.display = '';
40245             ae.dom.style.visibility = 'visible';
40246         }
40247     },
40248
40249     // private
40250     
40251     onHide : function(){
40252         var ae = this.getActionEl();
40253         ae.dom.style.display = 'none';
40254     },
40255
40256     /**
40257      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
40258      * by an implementing function.
40259      * @method
40260      * @param {EventObject} e
40261      */
40262     onTriggerClick : Roo.emptyFn
40263 });
40264
40265 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
40266 // to be extended by an implementing class.  For an example of implementing this class, see the custom
40267 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
40268 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
40269     initComponent : function(){
40270         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
40271
40272         this.triggerConfig = {
40273             tag:'span', cls:'x-form-twin-triggers', cn:[
40274             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
40275             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
40276         ]};
40277     },
40278
40279     getTrigger : function(index){
40280         return this.triggers[index];
40281     },
40282
40283     initTrigger : function(){
40284         var ts = this.trigger.select('.x-form-trigger', true);
40285         this.wrap.setStyle('overflow', 'hidden');
40286         var triggerField = this;
40287         ts.each(function(t, all, index){
40288             t.hide = function(){
40289                 var w = triggerField.wrap.getWidth();
40290                 this.dom.style.display = 'none';
40291                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40292             };
40293             t.show = function(){
40294                 var w = triggerField.wrap.getWidth();
40295                 this.dom.style.display = '';
40296                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
40297             };
40298             var triggerIndex = 'Trigger'+(index+1);
40299
40300             if(this['hide'+triggerIndex]){
40301                 t.dom.style.display = 'none';
40302             }
40303             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
40304             t.addClassOnOver('x-form-trigger-over');
40305             t.addClassOnClick('x-form-trigger-click');
40306         }, this);
40307         this.triggers = ts.elements;
40308     },
40309
40310     onTrigger1Click : Roo.emptyFn,
40311     onTrigger2Click : Roo.emptyFn
40312 });/*
40313  * Based on:
40314  * Ext JS Library 1.1.1
40315  * Copyright(c) 2006-2007, Ext JS, LLC.
40316  *
40317  * Originally Released Under LGPL - original licence link has changed is not relivant.
40318  *
40319  * Fork - LGPL
40320  * <script type="text/javascript">
40321  */
40322  
40323 /**
40324  * @class Roo.form.TextArea
40325  * @extends Roo.form.TextField
40326  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
40327  * support for auto-sizing.
40328  * @constructor
40329  * Creates a new TextArea
40330  * @param {Object} config Configuration options
40331  */
40332 Roo.form.TextArea = function(config){
40333     Roo.form.TextArea.superclass.constructor.call(this, config);
40334     // these are provided exchanges for backwards compat
40335     // minHeight/maxHeight were replaced by growMin/growMax to be
40336     // compatible with TextField growing config values
40337     if(this.minHeight !== undefined){
40338         this.growMin = this.minHeight;
40339     }
40340     if(this.maxHeight !== undefined){
40341         this.growMax = this.maxHeight;
40342     }
40343 };
40344
40345 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
40346     /**
40347      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
40348      */
40349     growMin : 60,
40350     /**
40351      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
40352      */
40353     growMax: 1000,
40354     /**
40355      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
40356      * in the field (equivalent to setting overflow: hidden, defaults to false)
40357      */
40358     preventScrollbars: false,
40359     /**
40360      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40361      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
40362      */
40363
40364     // private
40365     onRender : function(ct, position){
40366         if(!this.el){
40367             this.defaultAutoCreate = {
40368                 tag: "textarea",
40369                 style:"width:300px;height:60px;",
40370                 autocomplete: "new-password"
40371             };
40372         }
40373         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
40374         if(this.grow){
40375             this.textSizeEl = Roo.DomHelper.append(document.body, {
40376                 tag: "pre", cls: "x-form-grow-sizer"
40377             });
40378             if(this.preventScrollbars){
40379                 this.el.setStyle("overflow", "hidden");
40380             }
40381             this.el.setHeight(this.growMin);
40382         }
40383     },
40384
40385     onDestroy : function(){
40386         if(this.textSizeEl){
40387             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
40388         }
40389         Roo.form.TextArea.superclass.onDestroy.call(this);
40390     },
40391
40392     // private
40393     onKeyUp : function(e){
40394         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
40395             this.autoSize();
40396         }
40397     },
40398
40399     /**
40400      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
40401      * This only takes effect if grow = true, and fires the autosize event if the height changes.
40402      */
40403     autoSize : function(){
40404         if(!this.grow || !this.textSizeEl){
40405             return;
40406         }
40407         var el = this.el;
40408         var v = el.dom.value;
40409         var ts = this.textSizeEl;
40410
40411         ts.innerHTML = '';
40412         ts.appendChild(document.createTextNode(v));
40413         v = ts.innerHTML;
40414
40415         Roo.fly(ts).setWidth(this.el.getWidth());
40416         if(v.length < 1){
40417             v = "&#160;&#160;";
40418         }else{
40419             if(Roo.isIE){
40420                 v = v.replace(/\n/g, '<p>&#160;</p>');
40421             }
40422             v += "&#160;\n&#160;";
40423         }
40424         ts.innerHTML = v;
40425         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
40426         if(h != this.lastHeight){
40427             this.lastHeight = h;
40428             this.el.setHeight(h);
40429             this.fireEvent("autosize", this, h);
40430         }
40431     }
40432 });/*
40433  * Based on:
40434  * Ext JS Library 1.1.1
40435  * Copyright(c) 2006-2007, Ext JS, LLC.
40436  *
40437  * Originally Released Under LGPL - original licence link has changed is not relivant.
40438  *
40439  * Fork - LGPL
40440  * <script type="text/javascript">
40441  */
40442  
40443
40444 /**
40445  * @class Roo.form.NumberField
40446  * @extends Roo.form.TextField
40447  * Numeric text field that provides automatic keystroke filtering and numeric validation.
40448  * @constructor
40449  * Creates a new NumberField
40450  * @param {Object} config Configuration options
40451  */
40452 Roo.form.NumberField = function(config){
40453     Roo.form.NumberField.superclass.constructor.call(this, config);
40454 };
40455
40456 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
40457     /**
40458      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
40459      */
40460     fieldClass: "x-form-field x-form-num-field",
40461     /**
40462      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
40463      */
40464     allowDecimals : true,
40465     /**
40466      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
40467      */
40468     decimalSeparator : ".",
40469     /**
40470      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
40471      */
40472     decimalPrecision : 2,
40473     /**
40474      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
40475      */
40476     allowNegative : true,
40477     /**
40478      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
40479      */
40480     minValue : Number.NEGATIVE_INFINITY,
40481     /**
40482      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
40483      */
40484     maxValue : Number.MAX_VALUE,
40485     /**
40486      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
40487      */
40488     minText : "The minimum value for this field is {0}",
40489     /**
40490      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
40491      */
40492     maxText : "The maximum value for this field is {0}",
40493     /**
40494      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
40495      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
40496      */
40497     nanText : "{0} is not a valid number",
40498
40499     // private
40500     initEvents : function(){
40501         Roo.form.NumberField.superclass.initEvents.call(this);
40502         var allowed = "0123456789";
40503         if(this.allowDecimals){
40504             allowed += this.decimalSeparator;
40505         }
40506         if(this.allowNegative){
40507             allowed += "-";
40508         }
40509         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
40510         var keyPress = function(e){
40511             var k = e.getKey();
40512             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
40513                 return;
40514             }
40515             var c = e.getCharCode();
40516             if(allowed.indexOf(String.fromCharCode(c)) === -1){
40517                 e.stopEvent();
40518             }
40519         };
40520         this.el.on("keypress", keyPress, this);
40521     },
40522
40523     // private
40524     validateValue : function(value){
40525         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
40526             return false;
40527         }
40528         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40529              return true;
40530         }
40531         var num = this.parseValue(value);
40532         if(isNaN(num)){
40533             this.markInvalid(String.format(this.nanText, value));
40534             return false;
40535         }
40536         if(num < this.minValue){
40537             this.markInvalid(String.format(this.minText, this.minValue));
40538             return false;
40539         }
40540         if(num > this.maxValue){
40541             this.markInvalid(String.format(this.maxText, this.maxValue));
40542             return false;
40543         }
40544         return true;
40545     },
40546
40547     getValue : function(){
40548         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
40549     },
40550
40551     // private
40552     parseValue : function(value){
40553         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
40554         return isNaN(value) ? '' : value;
40555     },
40556
40557     // private
40558     fixPrecision : function(value){
40559         var nan = isNaN(value);
40560         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
40561             return nan ? '' : value;
40562         }
40563         return parseFloat(value).toFixed(this.decimalPrecision);
40564     },
40565
40566     setValue : function(v){
40567         v = this.fixPrecision(v);
40568         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
40569     },
40570
40571     // private
40572     decimalPrecisionFcn : function(v){
40573         return Math.floor(v);
40574     },
40575
40576     beforeBlur : function(){
40577         var v = this.parseValue(this.getRawValue());
40578         if(v){
40579             this.setValue(v);
40580         }
40581     }
40582 });/*
40583  * Based on:
40584  * Ext JS Library 1.1.1
40585  * Copyright(c) 2006-2007, Ext JS, LLC.
40586  *
40587  * Originally Released Under LGPL - original licence link has changed is not relivant.
40588  *
40589  * Fork - LGPL
40590  * <script type="text/javascript">
40591  */
40592  
40593 /**
40594  * @class Roo.form.DateField
40595  * @extends Roo.form.TriggerField
40596  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40597 * @constructor
40598 * Create a new DateField
40599 * @param {Object} config
40600  */
40601 Roo.form.DateField = function(config)
40602 {
40603     Roo.form.DateField.superclass.constructor.call(this, config);
40604     
40605       this.addEvents({
40606          
40607         /**
40608          * @event select
40609          * Fires when a date is selected
40610              * @param {Roo.form.DateField} combo This combo box
40611              * @param {Date} date The date selected
40612              */
40613         'select' : true
40614          
40615     });
40616     
40617     
40618     if(typeof this.minValue == "string") {
40619         this.minValue = this.parseDate(this.minValue);
40620     }
40621     if(typeof this.maxValue == "string") {
40622         this.maxValue = this.parseDate(this.maxValue);
40623     }
40624     this.ddMatch = null;
40625     if(this.disabledDates){
40626         var dd = this.disabledDates;
40627         var re = "(?:";
40628         for(var i = 0; i < dd.length; i++){
40629             re += dd[i];
40630             if(i != dd.length-1) {
40631                 re += "|";
40632             }
40633         }
40634         this.ddMatch = new RegExp(re + ")");
40635     }
40636 };
40637
40638 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
40639     /**
40640      * @cfg {String} format
40641      * The default date format string which can be overriden for localization support.  The format must be
40642      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
40643      */
40644     format : "m/d/y",
40645     /**
40646      * @cfg {String} altFormats
40647      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
40648      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
40649      */
40650     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
40651     /**
40652      * @cfg {Array} disabledDays
40653      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
40654      */
40655     disabledDays : null,
40656     /**
40657      * @cfg {String} disabledDaysText
40658      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
40659      */
40660     disabledDaysText : "Disabled",
40661     /**
40662      * @cfg {Array} disabledDates
40663      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
40664      * expression so they are very powerful. Some examples:
40665      * <ul>
40666      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
40667      * <li>["03/08", "09/16"] would disable those days for every year</li>
40668      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
40669      * <li>["03/../2006"] would disable every day in March 2006</li>
40670      * <li>["^03"] would disable every day in every March</li>
40671      * </ul>
40672      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
40673      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
40674      */
40675     disabledDates : null,
40676     /**
40677      * @cfg {String} disabledDatesText
40678      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
40679      */
40680     disabledDatesText : "Disabled",
40681     /**
40682      * @cfg {Date/String} minValue
40683      * The minimum allowed date. Can be either a Javascript date object or a string date in a
40684      * valid format (defaults to null).
40685      */
40686     minValue : null,
40687     /**
40688      * @cfg {Date/String} maxValue
40689      * The maximum allowed date. Can be either a Javascript date object or a string date in a
40690      * valid format (defaults to null).
40691      */
40692     maxValue : null,
40693     /**
40694      * @cfg {String} minText
40695      * The error text to display when the date in the cell is before minValue (defaults to
40696      * 'The date in this field must be after {minValue}').
40697      */
40698     minText : "The date in this field must be equal to or after {0}",
40699     /**
40700      * @cfg {String} maxText
40701      * The error text to display when the date in the cell is after maxValue (defaults to
40702      * 'The date in this field must be before {maxValue}').
40703      */
40704     maxText : "The date in this field must be equal to or before {0}",
40705     /**
40706      * @cfg {String} invalidText
40707      * The error text to display when the date in the field is invalid (defaults to
40708      * '{value} is not a valid date - it must be in the format {format}').
40709      */
40710     invalidText : "{0} is not a valid date - it must be in the format {1}",
40711     /**
40712      * @cfg {String} triggerClass
40713      * An additional CSS class used to style the trigger button.  The trigger will always get the
40714      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
40715      * which displays a calendar icon).
40716      */
40717     triggerClass : 'x-form-date-trigger',
40718     
40719
40720     /**
40721      * @cfg {Boolean} useIso
40722      * if enabled, then the date field will use a hidden field to store the 
40723      * real value as iso formated date. default (false)
40724      */ 
40725     useIso : false,
40726     /**
40727      * @cfg {String/Object} autoCreate
40728      * A DomHelper element spec, or true for a default element spec (defaults to
40729      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
40730      */ 
40731     // private
40732     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
40733     
40734     // private
40735     hiddenField: false,
40736     
40737     onRender : function(ct, position)
40738     {
40739         Roo.form.DateField.superclass.onRender.call(this, ct, position);
40740         if (this.useIso) {
40741             //this.el.dom.removeAttribute('name'); 
40742             Roo.log("Changing name?");
40743             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
40744             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
40745                     'before', true);
40746             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
40747             // prevent input submission
40748             this.hiddenName = this.name;
40749         }
40750             
40751             
40752     },
40753     
40754     // private
40755     validateValue : function(value)
40756     {
40757         value = this.formatDate(value);
40758         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
40759             Roo.log('super failed');
40760             return false;
40761         }
40762         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
40763              return true;
40764         }
40765         var svalue = value;
40766         value = this.parseDate(value);
40767         if(!value){
40768             Roo.log('parse date failed' + svalue);
40769             this.markInvalid(String.format(this.invalidText, svalue, this.format));
40770             return false;
40771         }
40772         var time = value.getTime();
40773         if(this.minValue && time < this.minValue.getTime()){
40774             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
40775             return false;
40776         }
40777         if(this.maxValue && time > this.maxValue.getTime()){
40778             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
40779             return false;
40780         }
40781         if(this.disabledDays){
40782             var day = value.getDay();
40783             for(var i = 0; i < this.disabledDays.length; i++) {
40784                 if(day === this.disabledDays[i]){
40785                     this.markInvalid(this.disabledDaysText);
40786                     return false;
40787                 }
40788             }
40789         }
40790         var fvalue = this.formatDate(value);
40791         if(this.ddMatch && this.ddMatch.test(fvalue)){
40792             this.markInvalid(String.format(this.disabledDatesText, fvalue));
40793             return false;
40794         }
40795         return true;
40796     },
40797
40798     // private
40799     // Provides logic to override the default TriggerField.validateBlur which just returns true
40800     validateBlur : function(){
40801         return !this.menu || !this.menu.isVisible();
40802     },
40803     
40804     getName: function()
40805     {
40806         // returns hidden if it's set..
40807         if (!this.rendered) {return ''};
40808         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
40809         
40810     },
40811
40812     /**
40813      * Returns the current date value of the date field.
40814      * @return {Date} The date value
40815      */
40816     getValue : function(){
40817         
40818         return  this.hiddenField ?
40819                 this.hiddenField.value :
40820                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
40821     },
40822
40823     /**
40824      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
40825      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
40826      * (the default format used is "m/d/y").
40827      * <br />Usage:
40828      * <pre><code>
40829 //All of these calls set the same date value (May 4, 2006)
40830
40831 //Pass a date object:
40832 var dt = new Date('5/4/06');
40833 dateField.setValue(dt);
40834
40835 //Pass a date string (default format):
40836 dateField.setValue('5/4/06');
40837
40838 //Pass a date string (custom format):
40839 dateField.format = 'Y-m-d';
40840 dateField.setValue('2006-5-4');
40841 </code></pre>
40842      * @param {String/Date} date The date or valid date string
40843      */
40844     setValue : function(date){
40845         if (this.hiddenField) {
40846             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
40847         }
40848         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
40849         // make sure the value field is always stored as a date..
40850         this.value = this.parseDate(date);
40851         
40852         
40853     },
40854
40855     // private
40856     parseDate : function(value){
40857         if(!value || value instanceof Date){
40858             return value;
40859         }
40860         var v = Date.parseDate(value, this.format);
40861          if (!v && this.useIso) {
40862             v = Date.parseDate(value, 'Y-m-d');
40863         }
40864         if(!v && this.altFormats){
40865             if(!this.altFormatsArray){
40866                 this.altFormatsArray = this.altFormats.split("|");
40867             }
40868             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
40869                 v = Date.parseDate(value, this.altFormatsArray[i]);
40870             }
40871         }
40872         return v;
40873     },
40874
40875     // private
40876     formatDate : function(date, fmt){
40877         return (!date || !(date instanceof Date)) ?
40878                date : date.dateFormat(fmt || this.format);
40879     },
40880
40881     // private
40882     menuListeners : {
40883         select: function(m, d){
40884             
40885             this.setValue(d);
40886             this.fireEvent('select', this, d);
40887         },
40888         show : function(){ // retain focus styling
40889             this.onFocus();
40890         },
40891         hide : function(){
40892             this.focus.defer(10, this);
40893             var ml = this.menuListeners;
40894             this.menu.un("select", ml.select,  this);
40895             this.menu.un("show", ml.show,  this);
40896             this.menu.un("hide", ml.hide,  this);
40897         }
40898     },
40899
40900     // private
40901     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
40902     onTriggerClick : function(){
40903         if(this.disabled){
40904             return;
40905         }
40906         if(this.menu == null){
40907             this.menu = new Roo.menu.DateMenu();
40908         }
40909         Roo.apply(this.menu.picker,  {
40910             showClear: this.allowBlank,
40911             minDate : this.minValue,
40912             maxDate : this.maxValue,
40913             disabledDatesRE : this.ddMatch,
40914             disabledDatesText : this.disabledDatesText,
40915             disabledDays : this.disabledDays,
40916             disabledDaysText : this.disabledDaysText,
40917             format : this.useIso ? 'Y-m-d' : this.format,
40918             minText : String.format(this.minText, this.formatDate(this.minValue)),
40919             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
40920         });
40921         this.menu.on(Roo.apply({}, this.menuListeners, {
40922             scope:this
40923         }));
40924         this.menu.picker.setValue(this.getValue() || new Date());
40925         this.menu.show(this.el, "tl-bl?");
40926     },
40927
40928     beforeBlur : function(){
40929         var v = this.parseDate(this.getRawValue());
40930         if(v){
40931             this.setValue(v);
40932         }
40933     },
40934
40935     /*@
40936      * overide
40937      * 
40938      */
40939     isDirty : function() {
40940         if(this.disabled) {
40941             return false;
40942         }
40943         
40944         if(typeof(this.startValue) === 'undefined'){
40945             return false;
40946         }
40947         
40948         return String(this.getValue()) !== String(this.startValue);
40949         
40950     },
40951     // @overide
40952     cleanLeadingSpace : function(e)
40953     {
40954        return;
40955     }
40956     
40957 });/*
40958  * Based on:
40959  * Ext JS Library 1.1.1
40960  * Copyright(c) 2006-2007, Ext JS, LLC.
40961  *
40962  * Originally Released Under LGPL - original licence link has changed is not relivant.
40963  *
40964  * Fork - LGPL
40965  * <script type="text/javascript">
40966  */
40967  
40968 /**
40969  * @class Roo.form.MonthField
40970  * @extends Roo.form.TriggerField
40971  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
40972 * @constructor
40973 * Create a new MonthField
40974 * @param {Object} config
40975  */
40976 Roo.form.MonthField = function(config){
40977     
40978     Roo.form.MonthField.superclass.constructor.call(this, config);
40979     
40980       this.addEvents({
40981          
40982         /**
40983          * @event select
40984          * Fires when a date is selected
40985              * @param {Roo.form.MonthFieeld} combo This combo box
40986              * @param {Date} date The date selected
40987              */
40988         'select' : true
40989          
40990     });
40991     
40992     
40993     if(typeof this.minValue == "string") {
40994         this.minValue = this.parseDate(this.minValue);
40995     }
40996     if(typeof this.maxValue == "string") {
40997         this.maxValue = this.parseDate(this.maxValue);
40998     }
40999     this.ddMatch = null;
41000     if(this.disabledDates){
41001         var dd = this.disabledDates;
41002         var re = "(?:";
41003         for(var i = 0; i < dd.length; i++){
41004             re += dd[i];
41005             if(i != dd.length-1) {
41006                 re += "|";
41007             }
41008         }
41009         this.ddMatch = new RegExp(re + ")");
41010     }
41011 };
41012
41013 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41014     /**
41015      * @cfg {String} format
41016      * The default date format string which can be overriden for localization support.  The format must be
41017      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41018      */
41019     format : "M Y",
41020     /**
41021      * @cfg {String} altFormats
41022      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41023      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41024      */
41025     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41026     /**
41027      * @cfg {Array} disabledDays
41028      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41029      */
41030     disabledDays : [0,1,2,3,4,5,6],
41031     /**
41032      * @cfg {String} disabledDaysText
41033      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41034      */
41035     disabledDaysText : "Disabled",
41036     /**
41037      * @cfg {Array} disabledDates
41038      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41039      * expression so they are very powerful. Some examples:
41040      * <ul>
41041      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41042      * <li>["03/08", "09/16"] would disable those days for every year</li>
41043      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41044      * <li>["03/../2006"] would disable every day in March 2006</li>
41045      * <li>["^03"] would disable every day in every March</li>
41046      * </ul>
41047      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41048      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41049      */
41050     disabledDates : null,
41051     /**
41052      * @cfg {String} disabledDatesText
41053      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41054      */
41055     disabledDatesText : "Disabled",
41056     /**
41057      * @cfg {Date/String} minValue
41058      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41059      * valid format (defaults to null).
41060      */
41061     minValue : null,
41062     /**
41063      * @cfg {Date/String} maxValue
41064      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41065      * valid format (defaults to null).
41066      */
41067     maxValue : null,
41068     /**
41069      * @cfg {String} minText
41070      * The error text to display when the date in the cell is before minValue (defaults to
41071      * 'The date in this field must be after {minValue}').
41072      */
41073     minText : "The date in this field must be equal to or after {0}",
41074     /**
41075      * @cfg {String} maxTextf
41076      * The error text to display when the date in the cell is after maxValue (defaults to
41077      * 'The date in this field must be before {maxValue}').
41078      */
41079     maxText : "The date in this field must be equal to or before {0}",
41080     /**
41081      * @cfg {String} invalidText
41082      * The error text to display when the date in the field is invalid (defaults to
41083      * '{value} is not a valid date - it must be in the format {format}').
41084      */
41085     invalidText : "{0} is not a valid date - it must be in the format {1}",
41086     /**
41087      * @cfg {String} triggerClass
41088      * An additional CSS class used to style the trigger button.  The trigger will always get the
41089      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41090      * which displays a calendar icon).
41091      */
41092     triggerClass : 'x-form-date-trigger',
41093     
41094
41095     /**
41096      * @cfg {Boolean} useIso
41097      * if enabled, then the date field will use a hidden field to store the 
41098      * real value as iso formated date. default (true)
41099      */ 
41100     useIso : true,
41101     /**
41102      * @cfg {String/Object} autoCreate
41103      * A DomHelper element spec, or true for a default element spec (defaults to
41104      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41105      */ 
41106     // private
41107     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41108     
41109     // private
41110     hiddenField: false,
41111     
41112     hideMonthPicker : false,
41113     
41114     onRender : function(ct, position)
41115     {
41116         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41117         if (this.useIso) {
41118             this.el.dom.removeAttribute('name'); 
41119             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41120                     'before', true);
41121             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41122             // prevent input submission
41123             this.hiddenName = this.name;
41124         }
41125             
41126             
41127     },
41128     
41129     // private
41130     validateValue : function(value)
41131     {
41132         value = this.formatDate(value);
41133         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41134             return false;
41135         }
41136         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41137              return true;
41138         }
41139         var svalue = value;
41140         value = this.parseDate(value);
41141         if(!value){
41142             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41143             return false;
41144         }
41145         var time = value.getTime();
41146         if(this.minValue && time < this.minValue.getTime()){
41147             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41148             return false;
41149         }
41150         if(this.maxValue && time > this.maxValue.getTime()){
41151             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41152             return false;
41153         }
41154         /*if(this.disabledDays){
41155             var day = value.getDay();
41156             for(var i = 0; i < this.disabledDays.length; i++) {
41157                 if(day === this.disabledDays[i]){
41158                     this.markInvalid(this.disabledDaysText);
41159                     return false;
41160                 }
41161             }
41162         }
41163         */
41164         var fvalue = this.formatDate(value);
41165         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41166             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41167             return false;
41168         }
41169         */
41170         return true;
41171     },
41172
41173     // private
41174     // Provides logic to override the default TriggerField.validateBlur which just returns true
41175     validateBlur : function(){
41176         return !this.menu || !this.menu.isVisible();
41177     },
41178
41179     /**
41180      * Returns the current date value of the date field.
41181      * @return {Date} The date value
41182      */
41183     getValue : function(){
41184         
41185         
41186         
41187         return  this.hiddenField ?
41188                 this.hiddenField.value :
41189                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41190     },
41191
41192     /**
41193      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41194      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41195      * (the default format used is "m/d/y").
41196      * <br />Usage:
41197      * <pre><code>
41198 //All of these calls set the same date value (May 4, 2006)
41199
41200 //Pass a date object:
41201 var dt = new Date('5/4/06');
41202 monthField.setValue(dt);
41203
41204 //Pass a date string (default format):
41205 monthField.setValue('5/4/06');
41206
41207 //Pass a date string (custom format):
41208 monthField.format = 'Y-m-d';
41209 monthField.setValue('2006-5-4');
41210 </code></pre>
41211      * @param {String/Date} date The date or valid date string
41212      */
41213     setValue : function(date){
41214         Roo.log('month setValue' + date);
41215         // can only be first of month..
41216         
41217         var val = this.parseDate(date);
41218         
41219         if (this.hiddenField) {
41220             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41221         }
41222         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41223         this.value = this.parseDate(date);
41224     },
41225
41226     // private
41227     parseDate : function(value){
41228         if(!value || value instanceof Date){
41229             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
41230             return value;
41231         }
41232         var v = Date.parseDate(value, this.format);
41233         if (!v && this.useIso) {
41234             v = Date.parseDate(value, 'Y-m-d');
41235         }
41236         if (v) {
41237             // 
41238             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
41239         }
41240         
41241         
41242         if(!v && this.altFormats){
41243             if(!this.altFormatsArray){
41244                 this.altFormatsArray = this.altFormats.split("|");
41245             }
41246             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41247                 v = Date.parseDate(value, this.altFormatsArray[i]);
41248             }
41249         }
41250         return v;
41251     },
41252
41253     // private
41254     formatDate : function(date, fmt){
41255         return (!date || !(date instanceof Date)) ?
41256                date : date.dateFormat(fmt || this.format);
41257     },
41258
41259     // private
41260     menuListeners : {
41261         select: function(m, d){
41262             this.setValue(d);
41263             this.fireEvent('select', this, d);
41264         },
41265         show : function(){ // retain focus styling
41266             this.onFocus();
41267         },
41268         hide : function(){
41269             this.focus.defer(10, this);
41270             var ml = this.menuListeners;
41271             this.menu.un("select", ml.select,  this);
41272             this.menu.un("show", ml.show,  this);
41273             this.menu.un("hide", ml.hide,  this);
41274         }
41275     },
41276     // private
41277     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41278     onTriggerClick : function(){
41279         if(this.disabled){
41280             return;
41281         }
41282         if(this.menu == null){
41283             this.menu = new Roo.menu.DateMenu();
41284            
41285         }
41286         
41287         Roo.apply(this.menu.picker,  {
41288             
41289             showClear: this.allowBlank,
41290             minDate : this.minValue,
41291             maxDate : this.maxValue,
41292             disabledDatesRE : this.ddMatch,
41293             disabledDatesText : this.disabledDatesText,
41294             
41295             format : this.useIso ? 'Y-m-d' : this.format,
41296             minText : String.format(this.minText, this.formatDate(this.minValue)),
41297             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41298             
41299         });
41300          this.menu.on(Roo.apply({}, this.menuListeners, {
41301             scope:this
41302         }));
41303        
41304         
41305         var m = this.menu;
41306         var p = m.picker;
41307         
41308         // hide month picker get's called when we called by 'before hide';
41309         
41310         var ignorehide = true;
41311         p.hideMonthPicker  = function(disableAnim){
41312             if (ignorehide) {
41313                 return;
41314             }
41315              if(this.monthPicker){
41316                 Roo.log("hideMonthPicker called");
41317                 if(disableAnim === true){
41318                     this.monthPicker.hide();
41319                 }else{
41320                     this.monthPicker.slideOut('t', {duration:.2});
41321                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
41322                     p.fireEvent("select", this, this.value);
41323                     m.hide();
41324                 }
41325             }
41326         }
41327         
41328         Roo.log('picker set value');
41329         Roo.log(this.getValue());
41330         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
41331         m.show(this.el, 'tl-bl?');
41332         ignorehide  = false;
41333         // this will trigger hideMonthPicker..
41334         
41335         
41336         // hidden the day picker
41337         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
41338         
41339         
41340         
41341       
41342         
41343         p.showMonthPicker.defer(100, p);
41344     
41345         
41346        
41347     },
41348
41349     beforeBlur : function(){
41350         var v = this.parseDate(this.getRawValue());
41351         if(v){
41352             this.setValue(v);
41353         }
41354     }
41355
41356     /** @cfg {Boolean} grow @hide */
41357     /** @cfg {Number} growMin @hide */
41358     /** @cfg {Number} growMax @hide */
41359     /**
41360      * @hide
41361      * @method autoSize
41362      */
41363 });/*
41364  * Based on:
41365  * Ext JS Library 1.1.1
41366  * Copyright(c) 2006-2007, Ext JS, LLC.
41367  *
41368  * Originally Released Under LGPL - original licence link has changed is not relivant.
41369  *
41370  * Fork - LGPL
41371  * <script type="text/javascript">
41372  */
41373  
41374
41375 /**
41376  * @class Roo.form.ComboBox
41377  * @extends Roo.form.TriggerField
41378  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
41379  * @constructor
41380  * Create a new ComboBox.
41381  * @param {Object} config Configuration options
41382  */
41383 Roo.form.ComboBox = function(config){
41384     Roo.form.ComboBox.superclass.constructor.call(this, config);
41385     this.addEvents({
41386         /**
41387          * @event expand
41388          * Fires when the dropdown list is expanded
41389              * @param {Roo.form.ComboBox} combo This combo box
41390              */
41391         'expand' : true,
41392         /**
41393          * @event collapse
41394          * Fires when the dropdown list is collapsed
41395              * @param {Roo.form.ComboBox} combo This combo box
41396              */
41397         'collapse' : true,
41398         /**
41399          * @event beforeselect
41400          * Fires before a list item is selected. Return false to cancel the selection.
41401              * @param {Roo.form.ComboBox} combo This combo box
41402              * @param {Roo.data.Record} record The data record returned from the underlying store
41403              * @param {Number} index The index of the selected item in the dropdown list
41404              */
41405         'beforeselect' : true,
41406         /**
41407          * @event select
41408          * Fires when a list item is selected
41409              * @param {Roo.form.ComboBox} combo This combo box
41410              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
41411              * @param {Number} index The index of the selected item in the dropdown list
41412              */
41413         'select' : true,
41414         /**
41415          * @event beforequery
41416          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
41417          * The event object passed has these properties:
41418              * @param {Roo.form.ComboBox} combo This combo box
41419              * @param {String} query The query
41420              * @param {Boolean} forceAll true to force "all" query
41421              * @param {Boolean} cancel true to cancel the query
41422              * @param {Object} e The query event object
41423              */
41424         'beforequery': true,
41425          /**
41426          * @event add
41427          * Fires when the 'add' icon is pressed (add a listener to enable add button)
41428              * @param {Roo.form.ComboBox} combo This combo box
41429              */
41430         'add' : true,
41431         /**
41432          * @event edit
41433          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
41434              * @param {Roo.form.ComboBox} combo This combo box
41435              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
41436              */
41437         'edit' : true
41438         
41439         
41440     });
41441     if(this.transform){
41442         this.allowDomMove = false;
41443         var s = Roo.getDom(this.transform);
41444         if(!this.hiddenName){
41445             this.hiddenName = s.name;
41446         }
41447         if(!this.store){
41448             this.mode = 'local';
41449             var d = [], opts = s.options;
41450             for(var i = 0, len = opts.length;i < len; i++){
41451                 var o = opts[i];
41452                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
41453                 if(o.selected) {
41454                     this.value = value;
41455                 }
41456                 d.push([value, o.text]);
41457             }
41458             this.store = new Roo.data.SimpleStore({
41459                 'id': 0,
41460                 fields: ['value', 'text'],
41461                 data : d
41462             });
41463             this.valueField = 'value';
41464             this.displayField = 'text';
41465         }
41466         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
41467         if(!this.lazyRender){
41468             this.target = true;
41469             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
41470             s.parentNode.removeChild(s); // remove it
41471             this.render(this.el.parentNode);
41472         }else{
41473             s.parentNode.removeChild(s); // remove it
41474         }
41475
41476     }
41477     if (this.store) {
41478         this.store = Roo.factory(this.store, Roo.data);
41479     }
41480     
41481     this.selectedIndex = -1;
41482     if(this.mode == 'local'){
41483         if(config.queryDelay === undefined){
41484             this.queryDelay = 10;
41485         }
41486         if(config.minChars === undefined){
41487             this.minChars = 0;
41488         }
41489     }
41490 };
41491
41492 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
41493     /**
41494      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
41495      */
41496     /**
41497      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
41498      * rendering into an Roo.Editor, defaults to false)
41499      */
41500     /**
41501      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
41502      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
41503      */
41504     /**
41505      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
41506      */
41507     /**
41508      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
41509      * the dropdown list (defaults to undefined, with no header element)
41510      */
41511
41512      /**
41513      * @cfg {String/Roo.Template} tpl The template to use to render the output
41514      */
41515      
41516     // private
41517     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
41518     /**
41519      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
41520      */
41521     listWidth: undefined,
41522     /**
41523      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
41524      * mode = 'remote' or 'text' if mode = 'local')
41525      */
41526     displayField: undefined,
41527     /**
41528      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
41529      * mode = 'remote' or 'value' if mode = 'local'). 
41530      * Note: use of a valueField requires the user make a selection
41531      * in order for a value to be mapped.
41532      */
41533     valueField: undefined,
41534     
41535     
41536     /**
41537      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
41538      * field's data value (defaults to the underlying DOM element's name)
41539      */
41540     hiddenName: undefined,
41541     /**
41542      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
41543      */
41544     listClass: '',
41545     /**
41546      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
41547      */
41548     selectedClass: 'x-combo-selected',
41549     /**
41550      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
41551      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
41552      * which displays a downward arrow icon).
41553      */
41554     triggerClass : 'x-form-arrow-trigger',
41555     /**
41556      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
41557      */
41558     shadow:'sides',
41559     /**
41560      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
41561      * anchor positions (defaults to 'tl-bl')
41562      */
41563     listAlign: 'tl-bl?',
41564     /**
41565      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
41566      */
41567     maxHeight: 300,
41568     /**
41569      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
41570      * query specified by the allQuery config option (defaults to 'query')
41571      */
41572     triggerAction: 'query',
41573     /**
41574      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
41575      * (defaults to 4, does not apply if editable = false)
41576      */
41577     minChars : 4,
41578     /**
41579      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
41580      * delay (typeAheadDelay) if it matches a known value (defaults to false)
41581      */
41582     typeAhead: false,
41583     /**
41584      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
41585      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
41586      */
41587     queryDelay: 500,
41588     /**
41589      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
41590      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
41591      */
41592     pageSize: 0,
41593     /**
41594      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
41595      * when editable = true (defaults to false)
41596      */
41597     selectOnFocus:false,
41598     /**
41599      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
41600      */
41601     queryParam: 'query',
41602     /**
41603      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
41604      * when mode = 'remote' (defaults to 'Loading...')
41605      */
41606     loadingText: 'Loading...',
41607     /**
41608      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
41609      */
41610     resizable: false,
41611     /**
41612      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
41613      */
41614     handleHeight : 8,
41615     /**
41616      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
41617      * traditional select (defaults to true)
41618      */
41619     editable: true,
41620     /**
41621      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
41622      */
41623     allQuery: '',
41624     /**
41625      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
41626      */
41627     mode: 'remote',
41628     /**
41629      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
41630      * listWidth has a higher value)
41631      */
41632     minListWidth : 70,
41633     /**
41634      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
41635      * allow the user to set arbitrary text into the field (defaults to false)
41636      */
41637     forceSelection:false,
41638     /**
41639      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
41640      * if typeAhead = true (defaults to 250)
41641      */
41642     typeAheadDelay : 250,
41643     /**
41644      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
41645      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
41646      */
41647     valueNotFoundText : undefined,
41648     /**
41649      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
41650      */
41651     blockFocus : false,
41652     
41653     /**
41654      * @cfg {Boolean} disableClear Disable showing of clear button.
41655      */
41656     disableClear : false,
41657     /**
41658      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
41659      */
41660     alwaysQuery : false,
41661     
41662     //private
41663     addicon : false,
41664     editicon: false,
41665     
41666     // element that contains real text value.. (when hidden is used..)
41667      
41668     // private
41669     onRender : function(ct, position)
41670     {
41671         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
41672         
41673         if(this.hiddenName){
41674             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
41675                     'before', true);
41676             this.hiddenField.value =
41677                 this.hiddenValue !== undefined ? this.hiddenValue :
41678                 this.value !== undefined ? this.value : '';
41679
41680             // prevent input submission
41681             this.el.dom.removeAttribute('name');
41682              
41683              
41684         }
41685         
41686         if(Roo.isGecko){
41687             this.el.dom.setAttribute('autocomplete', 'off');
41688         }
41689
41690         var cls = 'x-combo-list';
41691
41692         this.list = new Roo.Layer({
41693             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
41694         });
41695
41696         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
41697         this.list.setWidth(lw);
41698         this.list.swallowEvent('mousewheel');
41699         this.assetHeight = 0;
41700
41701         if(this.title){
41702             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
41703             this.assetHeight += this.header.getHeight();
41704         }
41705
41706         this.innerList = this.list.createChild({cls:cls+'-inner'});
41707         this.innerList.on('mouseover', this.onViewOver, this);
41708         this.innerList.on('mousemove', this.onViewMove, this);
41709         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41710         
41711         if(this.allowBlank && !this.pageSize && !this.disableClear){
41712             this.footer = this.list.createChild({cls:cls+'-ft'});
41713             this.pageTb = new Roo.Toolbar(this.footer);
41714            
41715         }
41716         if(this.pageSize){
41717             this.footer = this.list.createChild({cls:cls+'-ft'});
41718             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
41719                     {pageSize: this.pageSize});
41720             
41721         }
41722         
41723         if (this.pageTb && this.allowBlank && !this.disableClear) {
41724             var _this = this;
41725             this.pageTb.add(new Roo.Toolbar.Fill(), {
41726                 cls: 'x-btn-icon x-btn-clear',
41727                 text: '&#160;',
41728                 handler: function()
41729                 {
41730                     _this.collapse();
41731                     _this.clearValue();
41732                     _this.onSelect(false, -1);
41733                 }
41734             });
41735         }
41736         if (this.footer) {
41737             this.assetHeight += this.footer.getHeight();
41738         }
41739         
41740
41741         if(!this.tpl){
41742             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
41743         }
41744
41745         this.view = new Roo.View(this.innerList, this.tpl, {
41746             singleSelect:true,
41747             store: this.store,
41748             selectedClass: this.selectedClass
41749         });
41750
41751         this.view.on('click', this.onViewClick, this);
41752
41753         this.store.on('beforeload', this.onBeforeLoad, this);
41754         this.store.on('load', this.onLoad, this);
41755         this.store.on('loadexception', this.onLoadException, this);
41756
41757         if(this.resizable){
41758             this.resizer = new Roo.Resizable(this.list,  {
41759                pinned:true, handles:'se'
41760             });
41761             this.resizer.on('resize', function(r, w, h){
41762                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
41763                 this.listWidth = w;
41764                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
41765                 this.restrictHeight();
41766             }, this);
41767             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
41768         }
41769         if(!this.editable){
41770             this.editable = true;
41771             this.setEditable(false);
41772         }  
41773         
41774         
41775         if (typeof(this.events.add.listeners) != 'undefined') {
41776             
41777             this.addicon = this.wrap.createChild(
41778                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
41779        
41780             this.addicon.on('click', function(e) {
41781                 this.fireEvent('add', this);
41782             }, this);
41783         }
41784         if (typeof(this.events.edit.listeners) != 'undefined') {
41785             
41786             this.editicon = this.wrap.createChild(
41787                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
41788             if (this.addicon) {
41789                 this.editicon.setStyle('margin-left', '40px');
41790             }
41791             this.editicon.on('click', function(e) {
41792                 
41793                 // we fire even  if inothing is selected..
41794                 this.fireEvent('edit', this, this.lastData );
41795                 
41796             }, this);
41797         }
41798         
41799         
41800         
41801     },
41802
41803     // private
41804     initEvents : function(){
41805         Roo.form.ComboBox.superclass.initEvents.call(this);
41806
41807         this.keyNav = new Roo.KeyNav(this.el, {
41808             "up" : function(e){
41809                 this.inKeyMode = true;
41810                 this.selectPrev();
41811             },
41812
41813             "down" : function(e){
41814                 if(!this.isExpanded()){
41815                     this.onTriggerClick();
41816                 }else{
41817                     this.inKeyMode = true;
41818                     this.selectNext();
41819                 }
41820             },
41821
41822             "enter" : function(e){
41823                 this.onViewClick();
41824                 //return true;
41825             },
41826
41827             "esc" : function(e){
41828                 this.collapse();
41829             },
41830
41831             "tab" : function(e){
41832                 this.onViewClick(false);
41833                 this.fireEvent("specialkey", this, e);
41834                 return true;
41835             },
41836
41837             scope : this,
41838
41839             doRelay : function(foo, bar, hname){
41840                 if(hname == 'down' || this.scope.isExpanded()){
41841                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
41842                 }
41843                 return true;
41844             },
41845
41846             forceKeyDown: true
41847         });
41848         this.queryDelay = Math.max(this.queryDelay || 10,
41849                 this.mode == 'local' ? 10 : 250);
41850         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
41851         if(this.typeAhead){
41852             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
41853         }
41854         if(this.editable !== false){
41855             this.el.on("keyup", this.onKeyUp, this);
41856         }
41857         if(this.forceSelection){
41858             this.on('blur', this.doForce, this);
41859         }
41860     },
41861
41862     onDestroy : function(){
41863         if(this.view){
41864             this.view.setStore(null);
41865             this.view.el.removeAllListeners();
41866             this.view.el.remove();
41867             this.view.purgeListeners();
41868         }
41869         if(this.list){
41870             this.list.destroy();
41871         }
41872         if(this.store){
41873             this.store.un('beforeload', this.onBeforeLoad, this);
41874             this.store.un('load', this.onLoad, this);
41875             this.store.un('loadexception', this.onLoadException, this);
41876         }
41877         Roo.form.ComboBox.superclass.onDestroy.call(this);
41878     },
41879
41880     // private
41881     fireKey : function(e){
41882         if(e.isNavKeyPress() && !this.list.isVisible()){
41883             this.fireEvent("specialkey", this, e);
41884         }
41885     },
41886
41887     // private
41888     onResize: function(w, h){
41889         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
41890         
41891         if(typeof w != 'number'){
41892             // we do not handle it!?!?
41893             return;
41894         }
41895         var tw = this.trigger.getWidth();
41896         tw += this.addicon ? this.addicon.getWidth() : 0;
41897         tw += this.editicon ? this.editicon.getWidth() : 0;
41898         var x = w - tw;
41899         this.el.setWidth( this.adjustWidth('input', x));
41900             
41901         this.trigger.setStyle('left', x+'px');
41902         
41903         if(this.list && this.listWidth === undefined){
41904             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
41905             this.list.setWidth(lw);
41906             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
41907         }
41908         
41909     
41910         
41911     },
41912
41913     /**
41914      * Allow or prevent the user from directly editing the field text.  If false is passed,
41915      * the user will only be able to select from the items defined in the dropdown list.  This method
41916      * is the runtime equivalent of setting the 'editable' config option at config time.
41917      * @param {Boolean} value True to allow the user to directly edit the field text
41918      */
41919     setEditable : function(value){
41920         if(value == this.editable){
41921             return;
41922         }
41923         this.editable = value;
41924         if(!value){
41925             this.el.dom.setAttribute('readOnly', true);
41926             this.el.on('mousedown', this.onTriggerClick,  this);
41927             this.el.addClass('x-combo-noedit');
41928         }else{
41929             this.el.dom.setAttribute('readOnly', false);
41930             this.el.un('mousedown', this.onTriggerClick,  this);
41931             this.el.removeClass('x-combo-noedit');
41932         }
41933     },
41934
41935     // private
41936     onBeforeLoad : function(){
41937         if(!this.hasFocus){
41938             return;
41939         }
41940         this.innerList.update(this.loadingText ?
41941                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
41942         this.restrictHeight();
41943         this.selectedIndex = -1;
41944     },
41945
41946     // private
41947     onLoad : function(){
41948         if(!this.hasFocus){
41949             return;
41950         }
41951         if(this.store.getCount() > 0){
41952             this.expand();
41953             this.restrictHeight();
41954             if(this.lastQuery == this.allQuery){
41955                 if(this.editable){
41956                     this.el.dom.select();
41957                 }
41958                 if(!this.selectByValue(this.value, true)){
41959                     this.select(0, true);
41960                 }
41961             }else{
41962                 this.selectNext();
41963                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
41964                     this.taTask.delay(this.typeAheadDelay);
41965                 }
41966             }
41967         }else{
41968             this.onEmptyResults();
41969         }
41970         //this.el.focus();
41971     },
41972     // private
41973     onLoadException : function()
41974     {
41975         this.collapse();
41976         Roo.log(this.store.reader.jsonData);
41977         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
41978             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
41979         }
41980         
41981         
41982     },
41983     // private
41984     onTypeAhead : function(){
41985         if(this.store.getCount() > 0){
41986             var r = this.store.getAt(0);
41987             var newValue = r.data[this.displayField];
41988             var len = newValue.length;
41989             var selStart = this.getRawValue().length;
41990             if(selStart != len){
41991                 this.setRawValue(newValue);
41992                 this.selectText(selStart, newValue.length);
41993             }
41994         }
41995     },
41996
41997     // private
41998     onSelect : function(record, index){
41999         if(this.fireEvent('beforeselect', this, record, index) !== false){
42000             this.setFromData(index > -1 ? record.data : false);
42001             this.collapse();
42002             this.fireEvent('select', this, record, index);
42003         }
42004     },
42005
42006     /**
42007      * Returns the currently selected field value or empty string if no value is set.
42008      * @return {String} value The selected value
42009      */
42010     getValue : function(){
42011         if(this.valueField){
42012             return typeof this.value != 'undefined' ? this.value : '';
42013         }
42014         return Roo.form.ComboBox.superclass.getValue.call(this);
42015     },
42016
42017     /**
42018      * Clears any text/value currently set in the field
42019      */
42020     clearValue : function(){
42021         if(this.hiddenField){
42022             this.hiddenField.value = '';
42023         }
42024         this.value = '';
42025         this.setRawValue('');
42026         this.lastSelectionText = '';
42027         
42028     },
42029
42030     /**
42031      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42032      * will be displayed in the field.  If the value does not match the data value of an existing item,
42033      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42034      * Otherwise the field will be blank (although the value will still be set).
42035      * @param {String} value The value to match
42036      */
42037     setValue : function(v){
42038         var text = v;
42039         if(this.valueField){
42040             var r = this.findRecord(this.valueField, v);
42041             if(r){
42042                 text = r.data[this.displayField];
42043             }else if(this.valueNotFoundText !== undefined){
42044                 text = this.valueNotFoundText;
42045             }
42046         }
42047         this.lastSelectionText = text;
42048         if(this.hiddenField){
42049             this.hiddenField.value = v;
42050         }
42051         Roo.form.ComboBox.superclass.setValue.call(this, text);
42052         this.value = v;
42053     },
42054     /**
42055      * @property {Object} the last set data for the element
42056      */
42057     
42058     lastData : false,
42059     /**
42060      * Sets the value of the field based on a object which is related to the record format for the store.
42061      * @param {Object} value the value to set as. or false on reset?
42062      */
42063     setFromData : function(o){
42064         var dv = ''; // display value
42065         var vv = ''; // value value..
42066         this.lastData = o;
42067         if (this.displayField) {
42068             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42069         } else {
42070             // this is an error condition!!!
42071             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42072         }
42073         
42074         if(this.valueField){
42075             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42076         }
42077         if(this.hiddenField){
42078             this.hiddenField.value = vv;
42079             
42080             this.lastSelectionText = dv;
42081             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42082             this.value = vv;
42083             return;
42084         }
42085         // no hidden field.. - we store the value in 'value', but still display
42086         // display field!!!!
42087         this.lastSelectionText = dv;
42088         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42089         this.value = vv;
42090         
42091         
42092     },
42093     // private
42094     reset : function(){
42095         // overridden so that last data is reset..
42096         this.setValue(this.resetValue);
42097         this.originalValue = this.getValue();
42098         this.clearInvalid();
42099         this.lastData = false;
42100         if (this.view) {
42101             this.view.clearSelections();
42102         }
42103     },
42104     // private
42105     findRecord : function(prop, value){
42106         var record;
42107         if(this.store.getCount() > 0){
42108             this.store.each(function(r){
42109                 if(r.data[prop] == value){
42110                     record = r;
42111                     return false;
42112                 }
42113                 return true;
42114             });
42115         }
42116         return record;
42117     },
42118     
42119     getName: function()
42120     {
42121         // returns hidden if it's set..
42122         if (!this.rendered) {return ''};
42123         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42124         
42125     },
42126     // private
42127     onViewMove : function(e, t){
42128         this.inKeyMode = false;
42129     },
42130
42131     // private
42132     onViewOver : function(e, t){
42133         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42134             return;
42135         }
42136         var item = this.view.findItemFromChild(t);
42137         if(item){
42138             var index = this.view.indexOf(item);
42139             this.select(index, false);
42140         }
42141     },
42142
42143     // private
42144     onViewClick : function(doFocus)
42145     {
42146         var index = this.view.getSelectedIndexes()[0];
42147         var r = this.store.getAt(index);
42148         if(r){
42149             this.onSelect(r, index);
42150         }
42151         if(doFocus !== false && !this.blockFocus){
42152             this.el.focus();
42153         }
42154     },
42155
42156     // private
42157     restrictHeight : function(){
42158         this.innerList.dom.style.height = '';
42159         var inner = this.innerList.dom;
42160         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42161         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42162         this.list.beginUpdate();
42163         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42164         this.list.alignTo(this.el, this.listAlign);
42165         this.list.endUpdate();
42166     },
42167
42168     // private
42169     onEmptyResults : function(){
42170         this.collapse();
42171     },
42172
42173     /**
42174      * Returns true if the dropdown list is expanded, else false.
42175      */
42176     isExpanded : function(){
42177         return this.list.isVisible();
42178     },
42179
42180     /**
42181      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42182      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42183      * @param {String} value The data value of the item to select
42184      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42185      * selected item if it is not currently in view (defaults to true)
42186      * @return {Boolean} True if the value matched an item in the list, else false
42187      */
42188     selectByValue : function(v, scrollIntoView){
42189         if(v !== undefined && v !== null){
42190             var r = this.findRecord(this.valueField || this.displayField, v);
42191             if(r){
42192                 this.select(this.store.indexOf(r), scrollIntoView);
42193                 return true;
42194             }
42195         }
42196         return false;
42197     },
42198
42199     /**
42200      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42201      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42202      * @param {Number} index The zero-based index of the list item to select
42203      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42204      * selected item if it is not currently in view (defaults to true)
42205      */
42206     select : function(index, scrollIntoView){
42207         this.selectedIndex = index;
42208         this.view.select(index);
42209         if(scrollIntoView !== false){
42210             var el = this.view.getNode(index);
42211             if(el){
42212                 this.innerList.scrollChildIntoView(el, false);
42213             }
42214         }
42215     },
42216
42217     // private
42218     selectNext : function(){
42219         var ct = this.store.getCount();
42220         if(ct > 0){
42221             if(this.selectedIndex == -1){
42222                 this.select(0);
42223             }else if(this.selectedIndex < ct-1){
42224                 this.select(this.selectedIndex+1);
42225             }
42226         }
42227     },
42228
42229     // private
42230     selectPrev : function(){
42231         var ct = this.store.getCount();
42232         if(ct > 0){
42233             if(this.selectedIndex == -1){
42234                 this.select(0);
42235             }else if(this.selectedIndex != 0){
42236                 this.select(this.selectedIndex-1);
42237             }
42238         }
42239     },
42240
42241     // private
42242     onKeyUp : function(e){
42243         if(this.editable !== false && !e.isSpecialKey()){
42244             this.lastKey = e.getKey();
42245             this.dqTask.delay(this.queryDelay);
42246         }
42247     },
42248
42249     // private
42250     validateBlur : function(){
42251         return !this.list || !this.list.isVisible();   
42252     },
42253
42254     // private
42255     initQuery : function(){
42256         this.doQuery(this.getRawValue());
42257     },
42258
42259     // private
42260     doForce : function(){
42261         if(this.el.dom.value.length > 0){
42262             this.el.dom.value =
42263                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
42264              
42265         }
42266     },
42267
42268     /**
42269      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
42270      * query allowing the query action to be canceled if needed.
42271      * @param {String} query The SQL query to execute
42272      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
42273      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
42274      * saved in the current store (defaults to false)
42275      */
42276     doQuery : function(q, forceAll){
42277         if(q === undefined || q === null){
42278             q = '';
42279         }
42280         var qe = {
42281             query: q,
42282             forceAll: forceAll,
42283             combo: this,
42284             cancel:false
42285         };
42286         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
42287             return false;
42288         }
42289         q = qe.query;
42290         forceAll = qe.forceAll;
42291         if(forceAll === true || (q.length >= this.minChars)){
42292             if(this.lastQuery != q || this.alwaysQuery){
42293                 this.lastQuery = q;
42294                 if(this.mode == 'local'){
42295                     this.selectedIndex = -1;
42296                     if(forceAll){
42297                         this.store.clearFilter();
42298                     }else{
42299                         this.store.filter(this.displayField, q);
42300                     }
42301                     this.onLoad();
42302                 }else{
42303                     this.store.baseParams[this.queryParam] = q;
42304                     this.store.load({
42305                         params: this.getParams(q)
42306                     });
42307                     this.expand();
42308                 }
42309             }else{
42310                 this.selectedIndex = -1;
42311                 this.onLoad();   
42312             }
42313         }
42314     },
42315
42316     // private
42317     getParams : function(q){
42318         var p = {};
42319         //p[this.queryParam] = q;
42320         if(this.pageSize){
42321             p.start = 0;
42322             p.limit = this.pageSize;
42323         }
42324         return p;
42325     },
42326
42327     /**
42328      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
42329      */
42330     collapse : function(){
42331         if(!this.isExpanded()){
42332             return;
42333         }
42334         this.list.hide();
42335         Roo.get(document).un('mousedown', this.collapseIf, this);
42336         Roo.get(document).un('mousewheel', this.collapseIf, this);
42337         if (!this.editable) {
42338             Roo.get(document).un('keydown', this.listKeyPress, this);
42339         }
42340         this.fireEvent('collapse', this);
42341     },
42342
42343     // private
42344     collapseIf : function(e){
42345         if(!e.within(this.wrap) && !e.within(this.list)){
42346             this.collapse();
42347         }
42348     },
42349
42350     /**
42351      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
42352      */
42353     expand : function(){
42354         if(this.isExpanded() || !this.hasFocus){
42355             return;
42356         }
42357         this.list.alignTo(this.el, this.listAlign);
42358         this.list.show();
42359         Roo.get(document).on('mousedown', this.collapseIf, this);
42360         Roo.get(document).on('mousewheel', this.collapseIf, this);
42361         if (!this.editable) {
42362             Roo.get(document).on('keydown', this.listKeyPress, this);
42363         }
42364         
42365         this.fireEvent('expand', this);
42366     },
42367
42368     // private
42369     // Implements the default empty TriggerField.onTriggerClick function
42370     onTriggerClick : function(){
42371         if(this.disabled){
42372             return;
42373         }
42374         if(this.isExpanded()){
42375             this.collapse();
42376             if (!this.blockFocus) {
42377                 this.el.focus();
42378             }
42379             
42380         }else {
42381             this.hasFocus = true;
42382             if(this.triggerAction == 'all') {
42383                 this.doQuery(this.allQuery, true);
42384             } else {
42385                 this.doQuery(this.getRawValue());
42386             }
42387             if (!this.blockFocus) {
42388                 this.el.focus();
42389             }
42390         }
42391     },
42392     listKeyPress : function(e)
42393     {
42394         //Roo.log('listkeypress');
42395         // scroll to first matching element based on key pres..
42396         if (e.isSpecialKey()) {
42397             return false;
42398         }
42399         var k = String.fromCharCode(e.getKey()).toUpperCase();
42400         //Roo.log(k);
42401         var match  = false;
42402         var csel = this.view.getSelectedNodes();
42403         var cselitem = false;
42404         if (csel.length) {
42405             var ix = this.view.indexOf(csel[0]);
42406             cselitem  = this.store.getAt(ix);
42407             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
42408                 cselitem = false;
42409             }
42410             
42411         }
42412         
42413         this.store.each(function(v) { 
42414             if (cselitem) {
42415                 // start at existing selection.
42416                 if (cselitem.id == v.id) {
42417                     cselitem = false;
42418                 }
42419                 return;
42420             }
42421                 
42422             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
42423                 match = this.store.indexOf(v);
42424                 return false;
42425             }
42426         }, this);
42427         
42428         if (match === false) {
42429             return true; // no more action?
42430         }
42431         // scroll to?
42432         this.view.select(match);
42433         var sn = Roo.get(this.view.getSelectedNodes()[0]);
42434         sn.scrollIntoView(sn.dom.parentNode, false);
42435     } 
42436
42437     /** 
42438     * @cfg {Boolean} grow 
42439     * @hide 
42440     */
42441     /** 
42442     * @cfg {Number} growMin 
42443     * @hide 
42444     */
42445     /** 
42446     * @cfg {Number} growMax 
42447     * @hide 
42448     */
42449     /**
42450      * @hide
42451      * @method autoSize
42452      */
42453 });/*
42454  * Copyright(c) 2010-2012, Roo J Solutions Limited
42455  *
42456  * Licence LGPL
42457  *
42458  */
42459
42460 /**
42461  * @class Roo.form.ComboBoxArray
42462  * @extends Roo.form.TextField
42463  * A facebook style adder... for lists of email / people / countries  etc...
42464  * pick multiple items from a combo box, and shows each one.
42465  *
42466  *  Fred [x]  Brian [x]  [Pick another |v]
42467  *
42468  *
42469  *  For this to work: it needs various extra information
42470  *    - normal combo problay has
42471  *      name, hiddenName
42472  *    + displayField, valueField
42473  *
42474  *    For our purpose...
42475  *
42476  *
42477  *   If we change from 'extends' to wrapping...
42478  *   
42479  *  
42480  *
42481  
42482  
42483  * @constructor
42484  * Create a new ComboBoxArray.
42485  * @param {Object} config Configuration options
42486  */
42487  
42488
42489 Roo.form.ComboBoxArray = function(config)
42490 {
42491     this.addEvents({
42492         /**
42493          * @event beforeremove
42494          * Fires before remove the value from the list
42495              * @param {Roo.form.ComboBoxArray} _self This combo box array
42496              * @param {Roo.form.ComboBoxArray.Item} item removed item
42497              */
42498         'beforeremove' : true,
42499         /**
42500          * @event remove
42501          * Fires when remove the value from the list
42502              * @param {Roo.form.ComboBoxArray} _self This combo box array
42503              * @param {Roo.form.ComboBoxArray.Item} item removed item
42504              */
42505         'remove' : true
42506         
42507         
42508     });
42509     
42510     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
42511     
42512     this.items = new Roo.util.MixedCollection(false);
42513     
42514     // construct the child combo...
42515     
42516     
42517     
42518     
42519    
42520     
42521 }
42522
42523  
42524 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
42525
42526     /**
42527      * @cfg {Roo.form.Combo} combo The combo box that is wrapped
42528      */
42529     
42530     lastData : false,
42531     
42532     // behavies liek a hiddne field
42533     inputType:      'hidden',
42534     /**
42535      * @cfg {Number} width The width of the box that displays the selected element
42536      */ 
42537     width:          300,
42538
42539     
42540     
42541     /**
42542      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
42543      */
42544     name : false,
42545     /**
42546      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
42547      */
42548     hiddenName : false,
42549       /**
42550      * @cfg {String} seperator    The value seperator normally ',' 
42551      */
42552     seperator : ',',
42553     
42554     // private the array of items that are displayed..
42555     items  : false,
42556     // private - the hidden field el.
42557     hiddenEl : false,
42558     // private - the filed el..
42559     el : false,
42560     
42561     //validateValue : function() { return true; }, // all values are ok!
42562     //onAddClick: function() { },
42563     
42564     onRender : function(ct, position) 
42565     {
42566         
42567         // create the standard hidden element
42568         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
42569         
42570         
42571         // give fake names to child combo;
42572         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
42573         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
42574         
42575         this.combo = Roo.factory(this.combo, Roo.form);
42576         this.combo.onRender(ct, position);
42577         if (typeof(this.combo.width) != 'undefined') {
42578             this.combo.onResize(this.combo.width,0);
42579         }
42580         
42581         this.combo.initEvents();
42582         
42583         // assigned so form know we need to do this..
42584         this.store          = this.combo.store;
42585         this.valueField     = this.combo.valueField;
42586         this.displayField   = this.combo.displayField ;
42587         
42588         
42589         this.combo.wrap.addClass('x-cbarray-grp');
42590         
42591         var cbwrap = this.combo.wrap.createChild(
42592             {tag: 'div', cls: 'x-cbarray-cb'},
42593             this.combo.el.dom
42594         );
42595         
42596              
42597         this.hiddenEl = this.combo.wrap.createChild({
42598             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
42599         });
42600         this.el = this.combo.wrap.createChild({
42601             tag: 'input',  type:'hidden' , name: this.name, value : ''
42602         });
42603          //   this.el.dom.removeAttribute("name");
42604         
42605         
42606         this.outerWrap = this.combo.wrap;
42607         this.wrap = cbwrap;
42608         
42609         this.outerWrap.setWidth(this.width);
42610         this.outerWrap.dom.removeChild(this.el.dom);
42611         
42612         this.wrap.dom.appendChild(this.el.dom);
42613         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
42614         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
42615         
42616         this.combo.trigger.setStyle('position','relative');
42617         this.combo.trigger.setStyle('left', '0px');
42618         this.combo.trigger.setStyle('top', '2px');
42619         
42620         this.combo.el.setStyle('vertical-align', 'text-bottom');
42621         
42622         //this.trigger.setStyle('vertical-align', 'top');
42623         
42624         // this should use the code from combo really... on('add' ....)
42625         if (this.adder) {
42626             
42627         
42628             this.adder = this.outerWrap.createChild(
42629                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
42630             var _t = this;
42631             this.adder.on('click', function(e) {
42632                 _t.fireEvent('adderclick', this, e);
42633             }, _t);
42634         }
42635         //var _t = this;
42636         //this.adder.on('click', this.onAddClick, _t);
42637         
42638         
42639         this.combo.on('select', function(cb, rec, ix) {
42640             this.addItem(rec.data);
42641             
42642             cb.setValue('');
42643             cb.el.dom.value = '';
42644             //cb.lastData = rec.data;
42645             // add to list
42646             
42647         }, this);
42648         
42649         
42650     },
42651     
42652     
42653     getName: function()
42654     {
42655         // returns hidden if it's set..
42656         if (!this.rendered) {return ''};
42657         return  this.hiddenName ? this.hiddenName : this.name;
42658         
42659     },
42660     
42661     
42662     onResize: function(w, h){
42663         
42664         return;
42665         // not sure if this is needed..
42666         //this.combo.onResize(w,h);
42667         
42668         if(typeof w != 'number'){
42669             // we do not handle it!?!?
42670             return;
42671         }
42672         var tw = this.combo.trigger.getWidth();
42673         tw += this.addicon ? this.addicon.getWidth() : 0;
42674         tw += this.editicon ? this.editicon.getWidth() : 0;
42675         var x = w - tw;
42676         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
42677             
42678         this.combo.trigger.setStyle('left', '0px');
42679         
42680         if(this.list && this.listWidth === undefined){
42681             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
42682             this.list.setWidth(lw);
42683             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42684         }
42685         
42686     
42687         
42688     },
42689     
42690     addItem: function(rec)
42691     {
42692         var valueField = this.combo.valueField;
42693         var displayField = this.combo.displayField;
42694         
42695         if (this.items.indexOfKey(rec[valueField]) > -1) {
42696             //console.log("GOT " + rec.data.id);
42697             return;
42698         }
42699         
42700         var x = new Roo.form.ComboBoxArray.Item({
42701             //id : rec[this.idField],
42702             data : rec,
42703             displayField : displayField ,
42704             tipField : displayField ,
42705             cb : this
42706         });
42707         // use the 
42708         this.items.add(rec[valueField],x);
42709         // add it before the element..
42710         this.updateHiddenEl();
42711         x.render(this.outerWrap, this.wrap.dom);
42712         // add the image handler..
42713     },
42714     
42715     updateHiddenEl : function()
42716     {
42717         this.validate();
42718         if (!this.hiddenEl) {
42719             return;
42720         }
42721         var ar = [];
42722         var idField = this.combo.valueField;
42723         
42724         this.items.each(function(f) {
42725             ar.push(f.data[idField]);
42726         });
42727         this.hiddenEl.dom.value = ar.join(this.seperator);
42728         this.validate();
42729     },
42730     
42731     reset : function()
42732     {
42733         this.items.clear();
42734         
42735         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
42736            el.remove();
42737         });
42738         
42739         this.el.dom.value = '';
42740         if (this.hiddenEl) {
42741             this.hiddenEl.dom.value = '';
42742         }
42743         
42744     },
42745     getValue: function()
42746     {
42747         return this.hiddenEl ? this.hiddenEl.dom.value : '';
42748     },
42749     setValue: function(v) // not a valid action - must use addItems..
42750     {
42751         
42752         this.reset();
42753          
42754         if (this.store.isLocal && (typeof(v) == 'string')) {
42755             // then we can use the store to find the values..
42756             // comma seperated at present.. this needs to allow JSON based encoding..
42757             this.hiddenEl.value  = v;
42758             var v_ar = [];
42759             Roo.each(v.split(this.seperator), function(k) {
42760                 Roo.log("CHECK " + this.valueField + ',' + k);
42761                 var li = this.store.query(this.valueField, k);
42762                 if (!li.length) {
42763                     return;
42764                 }
42765                 var add = {};
42766                 add[this.valueField] = k;
42767                 add[this.displayField] = li.item(0).data[this.displayField];
42768                 
42769                 this.addItem(add);
42770             }, this) 
42771              
42772         }
42773         if (typeof(v) == 'object' ) {
42774             // then let's assume it's an array of objects..
42775             Roo.each(v, function(l) {
42776                 var add = l;
42777                 if (typeof(l) == 'string') {
42778                     add = {};
42779                     add[this.valueField] = l;
42780                     add[this.displayField] = l
42781                 }
42782                 this.addItem(add);
42783             }, this);
42784              
42785         }
42786         
42787         
42788     },
42789     setFromData: function(v)
42790     {
42791         // this recieves an object, if setValues is called.
42792         this.reset();
42793         this.el.dom.value = v[this.displayField];
42794         this.hiddenEl.dom.value = v[this.valueField];
42795         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
42796             return;
42797         }
42798         var kv = v[this.valueField];
42799         var dv = v[this.displayField];
42800         kv = typeof(kv) != 'string' ? '' : kv;
42801         dv = typeof(dv) != 'string' ? '' : dv;
42802         
42803         
42804         var keys = kv.split(this.seperator);
42805         var display = dv.split(this.seperator);
42806         for (var i = 0 ; i < keys.length; i++) {
42807             add = {};
42808             add[this.valueField] = keys[i];
42809             add[this.displayField] = display[i];
42810             this.addItem(add);
42811         }
42812       
42813         
42814     },
42815     
42816     /**
42817      * Validates the combox array value
42818      * @return {Boolean} True if the value is valid, else false
42819      */
42820     validate : function(){
42821         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
42822             this.clearInvalid();
42823             return true;
42824         }
42825         return false;
42826     },
42827     
42828     validateValue : function(value){
42829         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
42830         
42831     },
42832     
42833     /*@
42834      * overide
42835      * 
42836      */
42837     isDirty : function() {
42838         if(this.disabled) {
42839             return false;
42840         }
42841         
42842         try {
42843             var d = Roo.decode(String(this.originalValue));
42844         } catch (e) {
42845             return String(this.getValue()) !== String(this.originalValue);
42846         }
42847         
42848         var originalValue = [];
42849         
42850         for (var i = 0; i < d.length; i++){
42851             originalValue.push(d[i][this.valueField]);
42852         }
42853         
42854         return String(this.getValue()) !== String(originalValue.join(this.seperator));
42855         
42856     }
42857     
42858 });
42859
42860
42861
42862 /**
42863  * @class Roo.form.ComboBoxArray.Item
42864  * @extends Roo.BoxComponent
42865  * A selected item in the list
42866  *  Fred [x]  Brian [x]  [Pick another |v]
42867  * 
42868  * @constructor
42869  * Create a new item.
42870  * @param {Object} config Configuration options
42871  */
42872  
42873 Roo.form.ComboBoxArray.Item = function(config) {
42874     config.id = Roo.id();
42875     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
42876 }
42877
42878 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
42879     data : {},
42880     cb: false,
42881     displayField : false,
42882     tipField : false,
42883     
42884     
42885     defaultAutoCreate : {
42886         tag: 'div',
42887         cls: 'x-cbarray-item',
42888         cn : [ 
42889             { tag: 'div' },
42890             {
42891                 tag: 'img',
42892                 width:16,
42893                 height : 16,
42894                 src : Roo.BLANK_IMAGE_URL ,
42895                 align: 'center'
42896             }
42897         ]
42898         
42899     },
42900     
42901  
42902     onRender : function(ct, position)
42903     {
42904         Roo.form.Field.superclass.onRender.call(this, ct, position);
42905         
42906         if(!this.el){
42907             var cfg = this.getAutoCreate();
42908             this.el = ct.createChild(cfg, position);
42909         }
42910         
42911         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
42912         
42913         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
42914             this.cb.renderer(this.data) :
42915             String.format('{0}',this.data[this.displayField]);
42916         
42917             
42918         this.el.child('div').dom.setAttribute('qtip',
42919                         String.format('{0}',this.data[this.tipField])
42920         );
42921         
42922         this.el.child('img').on('click', this.remove, this);
42923         
42924     },
42925    
42926     remove : function()
42927     {
42928         if(this.cb.disabled){
42929             return;
42930         }
42931         
42932         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
42933             this.cb.items.remove(this);
42934             this.el.child('img').un('click', this.remove, this);
42935             this.el.remove();
42936             this.cb.updateHiddenEl();
42937
42938             this.cb.fireEvent('remove', this.cb, this);
42939         }
42940         
42941     }
42942 });/*
42943  * RooJS Library 1.1.1
42944  * Copyright(c) 2008-2011  Alan Knowles
42945  *
42946  * License - LGPL
42947  */
42948  
42949
42950 /**
42951  * @class Roo.form.ComboNested
42952  * @extends Roo.form.ComboBox
42953  * A combobox for that allows selection of nested items in a list,
42954  * eg.
42955  *
42956  *  Book
42957  *    -> red
42958  *    -> green
42959  *  Table
42960  *    -> square
42961  *      ->red
42962  *      ->green
42963  *    -> rectangle
42964  *      ->green
42965  *      
42966  * 
42967  * @constructor
42968  * Create a new ComboNested
42969  * @param {Object} config Configuration options
42970  */
42971 Roo.form.ComboNested = function(config){
42972     Roo.form.ComboCheck.superclass.constructor.call(this, config);
42973     // should verify some data...
42974     // like
42975     // hiddenName = required..
42976     // displayField = required
42977     // valudField == required
42978     var req= [ 'hiddenName', 'displayField', 'valueField' ];
42979     var _t = this;
42980     Roo.each(req, function(e) {
42981         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
42982             throw "Roo.form.ComboNested : missing value for: " + e;
42983         }
42984     });
42985      
42986     
42987 };
42988
42989 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
42990    
42991     /*
42992      * @config {Number} max Number of columns to show
42993      */
42994     
42995     maxColumns : 3,
42996    
42997     list : null, // the outermost div..
42998     innerLists : null, // the
42999     views : null,
43000     stores : null,
43001     // private
43002     loadingChildren : false,
43003     
43004     onRender : function(ct, position)
43005     {
43006         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43007         
43008         if(this.hiddenName){
43009             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43010                     'before', true);
43011             this.hiddenField.value =
43012                 this.hiddenValue !== undefined ? this.hiddenValue :
43013                 this.value !== undefined ? this.value : '';
43014
43015             // prevent input submission
43016             this.el.dom.removeAttribute('name');
43017              
43018              
43019         }
43020         
43021         if(Roo.isGecko){
43022             this.el.dom.setAttribute('autocomplete', 'off');
43023         }
43024
43025         var cls = 'x-combo-list';
43026
43027         this.list = new Roo.Layer({
43028             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43029         });
43030
43031         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43032         this.list.setWidth(lw);
43033         this.list.swallowEvent('mousewheel');
43034         this.assetHeight = 0;
43035
43036         if(this.title){
43037             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43038             this.assetHeight += this.header.getHeight();
43039         }
43040         this.innerLists = [];
43041         this.views = [];
43042         this.stores = [];
43043         for (var i =0 ; i < this.maxColumns; i++) {
43044             this.onRenderList( cls, i);
43045         }
43046         
43047         // always needs footer, as we are going to have an 'OK' button.
43048         this.footer = this.list.createChild({cls:cls+'-ft'});
43049         this.pageTb = new Roo.Toolbar(this.footer);  
43050         var _this = this;
43051         this.pageTb.add(  {
43052             
43053             text: 'Done',
43054             handler: function()
43055             {
43056                 _this.collapse();
43057             }
43058         });
43059         
43060         if ( this.allowBlank && !this.disableClear) {
43061             
43062             this.pageTb.add(new Roo.Toolbar.Fill(), {
43063                 cls: 'x-btn-icon x-btn-clear',
43064                 text: '&#160;',
43065                 handler: function()
43066                 {
43067                     _this.collapse();
43068                     _this.clearValue();
43069                     _this.onSelect(false, -1);
43070                 }
43071             });
43072         }
43073         if (this.footer) {
43074             this.assetHeight += this.footer.getHeight();
43075         }
43076         
43077     },
43078     onRenderList : function (  cls, i)
43079     {
43080         
43081         var lw = Math.floor(
43082                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43083         );
43084         
43085         this.list.setWidth(lw); // default to '1'
43086
43087         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43088         //il.on('mouseover', this.onViewOver, this, { list:  i });
43089         //il.on('mousemove', this.onViewMove, this, { list:  i });
43090         il.setWidth(lw);
43091         il.setStyle({ 'overflow-x' : 'hidden'});
43092
43093         if(!this.tpl){
43094             this.tpl = new Roo.Template({
43095                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43096                 isEmpty: function (value, allValues) {
43097                     //Roo.log(value);
43098                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43099                     return dl ? 'has-children' : 'no-children'
43100                 }
43101             });
43102         }
43103         
43104         var store  = this.store;
43105         if (i > 0) {
43106             store  = new Roo.data.SimpleStore({
43107                 //fields : this.store.reader.meta.fields,
43108                 reader : this.store.reader,
43109                 data : [ ]
43110             });
43111         }
43112         this.stores[i]  = store;
43113                   
43114         var view = this.views[i] = new Roo.View(
43115             il,
43116             this.tpl,
43117             {
43118                 singleSelect:true,
43119                 store: store,
43120                 selectedClass: this.selectedClass
43121             }
43122         );
43123         view.getEl().setWidth(lw);
43124         view.getEl().setStyle({
43125             position: i < 1 ? 'relative' : 'absolute',
43126             top: 0,
43127             left: (i * lw ) + 'px',
43128             display : i > 0 ? 'none' : 'block'
43129         });
43130         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43131         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43132         //view.on('click', this.onViewClick, this, { list : i });
43133
43134         store.on('beforeload', this.onBeforeLoad, this);
43135         store.on('load',  this.onLoad, this, { list  : i});
43136         store.on('loadexception', this.onLoadException, this);
43137
43138         // hide the other vies..
43139         
43140         
43141         
43142     },
43143       
43144     restrictHeight : function()
43145     {
43146         var mh = 0;
43147         Roo.each(this.innerLists, function(il,i) {
43148             var el = this.views[i].getEl();
43149             el.dom.style.height = '';
43150             var inner = el.dom;
43151             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43152             // only adjust heights on other ones..
43153             mh = Math.max(h, mh);
43154             if (i < 1) {
43155                 
43156                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43157                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43158                
43159             }
43160             
43161             
43162         }, this);
43163         
43164         this.list.beginUpdate();
43165         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43166         this.list.alignTo(this.el, this.listAlign);
43167         this.list.endUpdate();
43168         
43169     },
43170      
43171     
43172     // -- store handlers..
43173     // private
43174     onBeforeLoad : function()
43175     {
43176         if(!this.hasFocus){
43177             return;
43178         }
43179         this.innerLists[0].update(this.loadingText ?
43180                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43181         this.restrictHeight();
43182         this.selectedIndex = -1;
43183     },
43184     // private
43185     onLoad : function(a,b,c,d)
43186     {
43187         if (!this.loadingChildren) {
43188             // then we are loading the top level. - hide the children
43189             for (var i = 1;i < this.views.length; i++) {
43190                 this.views[i].getEl().setStyle({ display : 'none' });
43191             }
43192             var lw = Math.floor(
43193                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43194             );
43195         
43196              this.list.setWidth(lw); // default to '1'
43197
43198             
43199         }
43200         if(!this.hasFocus){
43201             return;
43202         }
43203         
43204         if(this.store.getCount() > 0) {
43205             this.expand();
43206             this.restrictHeight();   
43207         } else {
43208             this.onEmptyResults();
43209         }
43210         
43211         if (!this.loadingChildren) {
43212             this.selectActive();
43213         }
43214         /*
43215         this.stores[1].loadData([]);
43216         this.stores[2].loadData([]);
43217         this.views
43218         */    
43219     
43220         //this.el.focus();
43221     },
43222     
43223     
43224     // private
43225     onLoadException : function()
43226     {
43227         this.collapse();
43228         Roo.log(this.store.reader.jsonData);
43229         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
43230             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
43231         }
43232         
43233         
43234     },
43235     // no cleaning of leading spaces on blur here.
43236     cleanLeadingSpace : function(e) { },
43237     
43238
43239     onSelectChange : function (view, sels, opts )
43240     {
43241         var ix = view.getSelectedIndexes();
43242          
43243         if (opts.list > this.maxColumns - 2) {
43244             if (view.store.getCount()<  1) {
43245                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
43246
43247             } else  {
43248                 if (ix.length) {
43249                     // used to clear ?? but if we are loading unselected 
43250                     this.setFromData(view.store.getAt(ix[0]).data);
43251                 }
43252                 
43253             }
43254             
43255             return;
43256         }
43257         
43258         if (!ix.length) {
43259             // this get's fired when trigger opens..
43260            // this.setFromData({});
43261             var str = this.stores[opts.list+1];
43262             str.data.clear(); // removeall wihtout the fire events..
43263             return;
43264         }
43265         
43266         var rec = view.store.getAt(ix[0]);
43267          
43268         this.setFromData(rec.data);
43269         this.fireEvent('select', this, rec, ix[0]);
43270         
43271         var lw = Math.floor(
43272              (
43273                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
43274              ) / this.maxColumns
43275         );
43276         this.loadingChildren = true;
43277         this.stores[opts.list+1].loadDataFromChildren( rec );
43278         this.loadingChildren = false;
43279         var dl = this.stores[opts.list+1]. getTotalCount();
43280         
43281         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
43282         
43283         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
43284         for (var i = opts.list+2; i < this.views.length;i++) {
43285             this.views[i].getEl().setStyle({ display : 'none' });
43286         }
43287         
43288         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
43289         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
43290         
43291         if (this.isLoading) {
43292            // this.selectActive(opts.list);
43293         }
43294          
43295     },
43296     
43297     
43298     
43299     
43300     onDoubleClick : function()
43301     {
43302         this.collapse(); //??
43303     },
43304     
43305      
43306     
43307     
43308     
43309     // private
43310     recordToStack : function(store, prop, value, stack)
43311     {
43312         var cstore = new Roo.data.SimpleStore({
43313             //fields : this.store.reader.meta.fields, // we need array reader.. for
43314             reader : this.store.reader,
43315             data : [ ]
43316         });
43317         var _this = this;
43318         var record  = false;
43319         var srec = false;
43320         if(store.getCount() < 1){
43321             return false;
43322         }
43323         store.each(function(r){
43324             if(r.data[prop] == value){
43325                 record = r;
43326             srec = r;
43327                 return false;
43328             }
43329             if (r.data.cn && r.data.cn.length) {
43330                 cstore.loadDataFromChildren( r);
43331                 var cret = _this.recordToStack(cstore, prop, value, stack);
43332                 if (cret !== false) {
43333                     record = cret;
43334                     srec = r;
43335                     return false;
43336                 }
43337             }
43338              
43339             return true;
43340         });
43341         if (record == false) {
43342             return false
43343         }
43344         stack.unshift(srec);
43345         return record;
43346     },
43347     
43348     /*
43349      * find the stack of stores that match our value.
43350      *
43351      * 
43352      */
43353     
43354     selectActive : function ()
43355     {
43356         // if store is not loaded, then we will need to wait for that to happen first.
43357         var stack = [];
43358         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
43359         for (var i = 0; i < stack.length; i++ ) {
43360             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
43361         }
43362         
43363     }
43364         
43365          
43366     
43367     
43368     
43369     
43370 });/*
43371  * Based on:
43372  * Ext JS Library 1.1.1
43373  * Copyright(c) 2006-2007, Ext JS, LLC.
43374  *
43375  * Originally Released Under LGPL - original licence link has changed is not relivant.
43376  *
43377  * Fork - LGPL
43378  * <script type="text/javascript">
43379  */
43380 /**
43381  * @class Roo.form.Checkbox
43382  * @extends Roo.form.Field
43383  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
43384  * @constructor
43385  * Creates a new Checkbox
43386  * @param {Object} config Configuration options
43387  */
43388 Roo.form.Checkbox = function(config){
43389     Roo.form.Checkbox.superclass.constructor.call(this, config);
43390     this.addEvents({
43391         /**
43392          * @event check
43393          * Fires when the checkbox is checked or unchecked.
43394              * @param {Roo.form.Checkbox} this This checkbox
43395              * @param {Boolean} checked The new checked value
43396              */
43397         check : true
43398     });
43399 };
43400
43401 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
43402     /**
43403      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
43404      */
43405     focusClass : undefined,
43406     /**
43407      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
43408      */
43409     fieldClass: "x-form-field",
43410     /**
43411      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
43412      */
43413     checked: false,
43414     /**
43415      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
43416      * {tag: "input", type: "checkbox", autocomplete: "off"})
43417      */
43418     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
43419     /**
43420      * @cfg {String} boxLabel The text that appears beside the checkbox
43421      */
43422     boxLabel : "",
43423     /**
43424      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
43425      */  
43426     inputValue : '1',
43427     /**
43428      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
43429      */
43430      valueOff: '0', // value when not checked..
43431
43432     actionMode : 'viewEl', 
43433     //
43434     // private
43435     itemCls : 'x-menu-check-item x-form-item',
43436     groupClass : 'x-menu-group-item',
43437     inputType : 'hidden',
43438     
43439     
43440     inSetChecked: false, // check that we are not calling self...
43441     
43442     inputElement: false, // real input element?
43443     basedOn: false, // ????
43444     
43445     isFormField: true, // not sure where this is needed!!!!
43446
43447     onResize : function(){
43448         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
43449         if(!this.boxLabel){
43450             this.el.alignTo(this.wrap, 'c-c');
43451         }
43452     },
43453
43454     initEvents : function(){
43455         Roo.form.Checkbox.superclass.initEvents.call(this);
43456         this.el.on("click", this.onClick,  this);
43457         this.el.on("change", this.onClick,  this);
43458     },
43459
43460
43461     getResizeEl : function(){
43462         return this.wrap;
43463     },
43464
43465     getPositionEl : function(){
43466         return this.wrap;
43467     },
43468
43469     // private
43470     onRender : function(ct, position){
43471         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43472         /*
43473         if(this.inputValue !== undefined){
43474             this.el.dom.value = this.inputValue;
43475         }
43476         */
43477         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43478         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43479         var viewEl = this.wrap.createChild({ 
43480             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43481         this.viewEl = viewEl;   
43482         this.wrap.on('click', this.onClick,  this); 
43483         
43484         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43485         this.el.on('propertychange', this.setFromHidden,  this);  //ie
43486         
43487         
43488         
43489         if(this.boxLabel){
43490             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43491         //    viewEl.on('click', this.onClick,  this); 
43492         }
43493         //if(this.checked){
43494             this.setChecked(this.checked);
43495         //}else{
43496             //this.checked = this.el.dom;
43497         //}
43498
43499     },
43500
43501     // private
43502     initValue : Roo.emptyFn,
43503
43504     /**
43505      * Returns the checked state of the checkbox.
43506      * @return {Boolean} True if checked, else false
43507      */
43508     getValue : function(){
43509         if(this.el){
43510             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
43511         }
43512         return this.valueOff;
43513         
43514     },
43515
43516         // private
43517     onClick : function(){ 
43518         if (this.disabled) {
43519             return;
43520         }
43521         this.setChecked(!this.checked);
43522
43523         //if(this.el.dom.checked != this.checked){
43524         //    this.setValue(this.el.dom.checked);
43525        // }
43526     },
43527
43528     /**
43529      * Sets the checked state of the checkbox.
43530      * On is always based on a string comparison between inputValue and the param.
43531      * @param {Boolean/String} value - the value to set 
43532      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
43533      */
43534     setValue : function(v,suppressEvent){
43535         
43536         
43537         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
43538         //if(this.el && this.el.dom){
43539         //    this.el.dom.checked = this.checked;
43540         //    this.el.dom.defaultChecked = this.checked;
43541         //}
43542         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
43543         //this.fireEvent("check", this, this.checked);
43544     },
43545     // private..
43546     setChecked : function(state,suppressEvent)
43547     {
43548         if (this.inSetChecked) {
43549             this.checked = state;
43550             return;
43551         }
43552         
43553     
43554         if(this.wrap){
43555             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
43556         }
43557         this.checked = state;
43558         if(suppressEvent !== true){
43559             this.fireEvent('check', this, state);
43560         }
43561         this.inSetChecked = true;
43562         this.el.dom.value = state ? this.inputValue : this.valueOff;
43563         this.inSetChecked = false;
43564         
43565     },
43566     // handle setting of hidden value by some other method!!?!?
43567     setFromHidden: function()
43568     {
43569         if(!this.el){
43570             return;
43571         }
43572         //console.log("SET FROM HIDDEN");
43573         //alert('setFrom hidden');
43574         this.setValue(this.el.dom.value);
43575     },
43576     
43577     onDestroy : function()
43578     {
43579         if(this.viewEl){
43580             Roo.get(this.viewEl).remove();
43581         }
43582          
43583         Roo.form.Checkbox.superclass.onDestroy.call(this);
43584     },
43585     
43586     setBoxLabel : function(str)
43587     {
43588         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
43589     }
43590
43591 });/*
43592  * Based on:
43593  * Ext JS Library 1.1.1
43594  * Copyright(c) 2006-2007, Ext JS, LLC.
43595  *
43596  * Originally Released Under LGPL - original licence link has changed is not relivant.
43597  *
43598  * Fork - LGPL
43599  * <script type="text/javascript">
43600  */
43601  
43602 /**
43603  * @class Roo.form.Radio
43604  * @extends Roo.form.Checkbox
43605  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
43606  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
43607  * @constructor
43608  * Creates a new Radio
43609  * @param {Object} config Configuration options
43610  */
43611 Roo.form.Radio = function(){
43612     Roo.form.Radio.superclass.constructor.apply(this, arguments);
43613 };
43614 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
43615     inputType: 'radio',
43616
43617     /**
43618      * If this radio is part of a group, it will return the selected value
43619      * @return {String}
43620      */
43621     getGroupValue : function(){
43622         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
43623     },
43624     
43625     
43626     onRender : function(ct, position){
43627         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
43628         
43629         if(this.inputValue !== undefined){
43630             this.el.dom.value = this.inputValue;
43631         }
43632          
43633         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
43634         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
43635         //var viewEl = this.wrap.createChild({ 
43636         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
43637         //this.viewEl = viewEl;   
43638         //this.wrap.on('click', this.onClick,  this); 
43639         
43640         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
43641         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
43642         
43643         
43644         
43645         if(this.boxLabel){
43646             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
43647         //    viewEl.on('click', this.onClick,  this); 
43648         }
43649          if(this.checked){
43650             this.el.dom.checked =   'checked' ;
43651         }
43652          
43653     } 
43654     
43655     
43656 });//<script type="text/javascript">
43657
43658 /*
43659  * Based  Ext JS Library 1.1.1
43660  * Copyright(c) 2006-2007, Ext JS, LLC.
43661  * LGPL
43662  *
43663  */
43664  
43665 /**
43666  * @class Roo.HtmlEditorCore
43667  * @extends Roo.Component
43668  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
43669  *
43670  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
43671  */
43672
43673 Roo.HtmlEditorCore = function(config){
43674     
43675     
43676     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
43677     
43678     
43679     this.addEvents({
43680         /**
43681          * @event initialize
43682          * Fires when the editor is fully initialized (including the iframe)
43683          * @param {Roo.HtmlEditorCore} this
43684          */
43685         initialize: true,
43686         /**
43687          * @event activate
43688          * Fires when the editor is first receives the focus. Any insertion must wait
43689          * until after this event.
43690          * @param {Roo.HtmlEditorCore} this
43691          */
43692         activate: true,
43693          /**
43694          * @event beforesync
43695          * Fires before the textarea is updated with content from the editor iframe. Return false
43696          * to cancel the sync.
43697          * @param {Roo.HtmlEditorCore} this
43698          * @param {String} html
43699          */
43700         beforesync: true,
43701          /**
43702          * @event beforepush
43703          * Fires before the iframe editor is updated with content from the textarea. Return false
43704          * to cancel the push.
43705          * @param {Roo.HtmlEditorCore} this
43706          * @param {String} html
43707          */
43708         beforepush: true,
43709          /**
43710          * @event sync
43711          * Fires when the textarea is updated with content from the editor iframe.
43712          * @param {Roo.HtmlEditorCore} this
43713          * @param {String} html
43714          */
43715         sync: true,
43716          /**
43717          * @event push
43718          * Fires when the iframe editor is updated with content from the textarea.
43719          * @param {Roo.HtmlEditorCore} this
43720          * @param {String} html
43721          */
43722         push: true,
43723         
43724         /**
43725          * @event editorevent
43726          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
43727          * @param {Roo.HtmlEditorCore} this
43728          */
43729         editorevent: true
43730         
43731     });
43732     
43733     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
43734     
43735     // defaults : white / black...
43736     this.applyBlacklists();
43737     
43738     
43739     
43740 };
43741
43742
43743 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
43744
43745
43746      /**
43747      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
43748      */
43749     
43750     owner : false,
43751     
43752      /**
43753      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
43754      *                        Roo.resizable.
43755      */
43756     resizable : false,
43757      /**
43758      * @cfg {Number} height (in pixels)
43759      */   
43760     height: 300,
43761    /**
43762      * @cfg {Number} width (in pixels)
43763      */   
43764     width: 500,
43765     
43766     /**
43767      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
43768      * 
43769      */
43770     stylesheets: false,
43771     
43772     // id of frame..
43773     frameId: false,
43774     
43775     // private properties
43776     validationEvent : false,
43777     deferHeight: true,
43778     initialized : false,
43779     activated : false,
43780     sourceEditMode : false,
43781     onFocus : Roo.emptyFn,
43782     iframePad:3,
43783     hideMode:'offsets',
43784     
43785     clearUp: true,
43786     
43787     // blacklist + whitelisted elements..
43788     black: false,
43789     white: false,
43790      
43791     bodyCls : '',
43792
43793     /**
43794      * Protected method that will not generally be called directly. It
43795      * is called when the editor initializes the iframe with HTML contents. Override this method if you
43796      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
43797      */
43798     getDocMarkup : function(){
43799         // body styles..
43800         var st = '';
43801         
43802         // inherit styels from page...?? 
43803         if (this.stylesheets === false) {
43804             
43805             Roo.get(document.head).select('style').each(function(node) {
43806                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43807             });
43808             
43809             Roo.get(document.head).select('link').each(function(node) { 
43810                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
43811             });
43812             
43813         } else if (!this.stylesheets.length) {
43814                 // simple..
43815                 st = '<style type="text/css">' +
43816                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43817                    '</style>';
43818         } else {
43819             for (var i in this.stylesheets) { 
43820                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
43821             }
43822             
43823         }
43824         
43825         st +=  '<style type="text/css">' +
43826             'IMG { cursor: pointer } ' +
43827         '</style>';
43828
43829         var cls = 'roo-htmleditor-body';
43830         
43831         if(this.bodyCls.length){
43832             cls += ' ' + this.bodyCls;
43833         }
43834         
43835         return '<html><head>' + st  +
43836             //<style type="text/css">' +
43837             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
43838             //'</style>' +
43839             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
43840     },
43841
43842     // private
43843     onRender : function(ct, position)
43844     {
43845         var _t = this;
43846         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
43847         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
43848         
43849         
43850         this.el.dom.style.border = '0 none';
43851         this.el.dom.setAttribute('tabIndex', -1);
43852         this.el.addClass('x-hidden hide');
43853         
43854         
43855         
43856         if(Roo.isIE){ // fix IE 1px bogus margin
43857             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
43858         }
43859        
43860         
43861         this.frameId = Roo.id();
43862         
43863          
43864         
43865         var iframe = this.owner.wrap.createChild({
43866             tag: 'iframe',
43867             cls: 'form-control', // bootstrap..
43868             id: this.frameId,
43869             name: this.frameId,
43870             frameBorder : 'no',
43871             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
43872         }, this.el
43873         );
43874         
43875         
43876         this.iframe = iframe.dom;
43877
43878          this.assignDocWin();
43879         
43880         this.doc.designMode = 'on';
43881        
43882         this.doc.open();
43883         this.doc.write(this.getDocMarkup());
43884         this.doc.close();
43885
43886         
43887         var task = { // must defer to wait for browser to be ready
43888             run : function(){
43889                 //console.log("run task?" + this.doc.readyState);
43890                 this.assignDocWin();
43891                 if(this.doc.body || this.doc.readyState == 'complete'){
43892                     try {
43893                         this.doc.designMode="on";
43894                     } catch (e) {
43895                         return;
43896                     }
43897                     Roo.TaskMgr.stop(task);
43898                     this.initEditor.defer(10, this);
43899                 }
43900             },
43901             interval : 10,
43902             duration: 10000,
43903             scope: this
43904         };
43905         Roo.TaskMgr.start(task);
43906
43907     },
43908
43909     // private
43910     onResize : function(w, h)
43911     {
43912          Roo.log('resize: ' +w + ',' + h );
43913         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
43914         if(!this.iframe){
43915             return;
43916         }
43917         if(typeof w == 'number'){
43918             
43919             this.iframe.style.width = w + 'px';
43920         }
43921         if(typeof h == 'number'){
43922             
43923             this.iframe.style.height = h + 'px';
43924             if(this.doc){
43925                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
43926             }
43927         }
43928         
43929     },
43930
43931     /**
43932      * Toggles the editor between standard and source edit mode.
43933      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
43934      */
43935     toggleSourceEdit : function(sourceEditMode){
43936         
43937         this.sourceEditMode = sourceEditMode === true;
43938         
43939         if(this.sourceEditMode){
43940  
43941             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
43942             
43943         }else{
43944             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
43945             //this.iframe.className = '';
43946             this.deferFocus();
43947         }
43948         //this.setSize(this.owner.wrap.getSize());
43949         //this.fireEvent('editmodechange', this, this.sourceEditMode);
43950     },
43951
43952     
43953   
43954
43955     /**
43956      * Protected method that will not generally be called directly. If you need/want
43957      * custom HTML cleanup, this is the method you should override.
43958      * @param {String} html The HTML to be cleaned
43959      * return {String} The cleaned HTML
43960      */
43961     cleanHtml : function(html){
43962         html = String(html);
43963         if(html.length > 5){
43964             if(Roo.isSafari){ // strip safari nonsense
43965                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
43966             }
43967         }
43968         if(html == '&nbsp;'){
43969             html = '';
43970         }
43971         return html;
43972     },
43973
43974     /**
43975      * HTML Editor -> Textarea
43976      * Protected method that will not generally be called directly. Syncs the contents
43977      * of the editor iframe with the textarea.
43978      */
43979     syncValue : function(){
43980         if(this.initialized){
43981             var bd = (this.doc.body || this.doc.documentElement);
43982             //this.cleanUpPaste(); -- this is done else where and causes havoc..
43983             var html = bd.innerHTML;
43984             if(Roo.isSafari){
43985                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
43986                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
43987                 if(m && m[1]){
43988                     html = '<div style="'+m[0]+'">' + html + '</div>';
43989                 }
43990             }
43991             html = this.cleanHtml(html);
43992             // fix up the special chars.. normaly like back quotes in word...
43993             // however we do not want to do this with chinese..
43994             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
43995                 
43996                 var cc = match.charCodeAt();
43997
43998                 // Get the character value, handling surrogate pairs
43999                 if (match.length == 2) {
44000                     // It's a surrogate pair, calculate the Unicode code point
44001                     var high = match.charCodeAt(0) - 0xD800;
44002                     var low  = match.charCodeAt(1) - 0xDC00;
44003                     cc = (high * 0x400) + low + 0x10000;
44004                 }  else if (
44005                     (cc >= 0x4E00 && cc < 0xA000 ) ||
44006                     (cc >= 0x3400 && cc < 0x4E00 ) ||
44007                     (cc >= 0xf900 && cc < 0xfb00 )
44008                 ) {
44009                         return match;
44010                 }  
44011          
44012                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
44013                 return "&#" + cc + ";";
44014                 
44015                 
44016             });
44017             
44018             
44019              
44020             if(this.owner.fireEvent('beforesync', this, html) !== false){
44021                 this.el.dom.value = html;
44022                 this.owner.fireEvent('sync', this, html);
44023             }
44024         }
44025     },
44026
44027     /**
44028      * Protected method that will not generally be called directly. Pushes the value of the textarea
44029      * into the iframe editor.
44030      */
44031     pushValue : function(){
44032         if(this.initialized){
44033             var v = this.el.dom.value.trim();
44034             
44035 //            if(v.length < 1){
44036 //                v = '&#160;';
44037 //            }
44038             
44039             if(this.owner.fireEvent('beforepush', this, v) !== false){
44040                 var d = (this.doc.body || this.doc.documentElement);
44041                 d.innerHTML = v;
44042                 this.cleanUpPaste();
44043                 this.el.dom.value = d.innerHTML;
44044                 this.owner.fireEvent('push', this, v);
44045             }
44046         }
44047     },
44048
44049     // private
44050     deferFocus : function(){
44051         this.focus.defer(10, this);
44052     },
44053
44054     // doc'ed in Field
44055     focus : function(){
44056         if(this.win && !this.sourceEditMode){
44057             this.win.focus();
44058         }else{
44059             this.el.focus();
44060         }
44061     },
44062     
44063     assignDocWin: function()
44064     {
44065         var iframe = this.iframe;
44066         
44067          if(Roo.isIE){
44068             this.doc = iframe.contentWindow.document;
44069             this.win = iframe.contentWindow;
44070         } else {
44071 //            if (!Roo.get(this.frameId)) {
44072 //                return;
44073 //            }
44074 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44075 //            this.win = Roo.get(this.frameId).dom.contentWindow;
44076             
44077             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
44078                 return;
44079             }
44080             
44081             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
44082             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
44083         }
44084     },
44085     
44086     // private
44087     initEditor : function(){
44088         //console.log("INIT EDITOR");
44089         this.assignDocWin();
44090         
44091         
44092         
44093         this.doc.designMode="on";
44094         this.doc.open();
44095         this.doc.write(this.getDocMarkup());
44096         this.doc.close();
44097         
44098         var dbody = (this.doc.body || this.doc.documentElement);
44099         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
44100         // this copies styles from the containing element into thsi one..
44101         // not sure why we need all of this..
44102         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
44103         
44104         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
44105         //ss['background-attachment'] = 'fixed'; // w3c
44106         dbody.bgProperties = 'fixed'; // ie
44107         //Roo.DomHelper.applyStyles(dbody, ss);
44108         Roo.EventManager.on(this.doc, {
44109             //'mousedown': this.onEditorEvent,
44110             'mouseup': this.onEditorEvent,
44111             'dblclick': this.onEditorEvent,
44112             'click': this.onEditorEvent,
44113             'keyup': this.onEditorEvent,
44114             buffer:100,
44115             scope: this
44116         });
44117         if(Roo.isGecko){
44118             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
44119         }
44120         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
44121             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
44122         }
44123         this.initialized = true;
44124
44125         this.owner.fireEvent('initialize', this);
44126         this.pushValue();
44127     },
44128
44129     // private
44130     onDestroy : function(){
44131         
44132         
44133         
44134         if(this.rendered){
44135             
44136             //for (var i =0; i < this.toolbars.length;i++) {
44137             //    // fixme - ask toolbars for heights?
44138             //    this.toolbars[i].onDestroy();
44139            // }
44140             
44141             //this.wrap.dom.innerHTML = '';
44142             //this.wrap.remove();
44143         }
44144     },
44145
44146     // private
44147     onFirstFocus : function(){
44148         
44149         this.assignDocWin();
44150         
44151         
44152         this.activated = true;
44153          
44154     
44155         if(Roo.isGecko){ // prevent silly gecko errors
44156             this.win.focus();
44157             var s = this.win.getSelection();
44158             if(!s.focusNode || s.focusNode.nodeType != 3){
44159                 var r = s.getRangeAt(0);
44160                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
44161                 r.collapse(true);
44162                 this.deferFocus();
44163             }
44164             try{
44165                 this.execCmd('useCSS', true);
44166                 this.execCmd('styleWithCSS', false);
44167             }catch(e){}
44168         }
44169         this.owner.fireEvent('activate', this);
44170     },
44171
44172     // private
44173     adjustFont: function(btn){
44174         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
44175         //if(Roo.isSafari){ // safari
44176         //    adjust *= 2;
44177        // }
44178         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
44179         if(Roo.isSafari){ // safari
44180             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
44181             v =  (v < 10) ? 10 : v;
44182             v =  (v > 48) ? 48 : v;
44183             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
44184             
44185         }
44186         
44187         
44188         v = Math.max(1, v+adjust);
44189         
44190         this.execCmd('FontSize', v  );
44191     },
44192
44193     onEditorEvent : function(e)
44194     {
44195         this.owner.fireEvent('editorevent', this, e);
44196       //  this.updateToolbar();
44197         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
44198     },
44199
44200     insertTag : function(tg)
44201     {
44202         // could be a bit smarter... -> wrap the current selected tRoo..
44203         if (tg.toLowerCase() == 'span' ||
44204             tg.toLowerCase() == 'code' ||
44205             tg.toLowerCase() == 'sup' ||
44206             tg.toLowerCase() == 'sub' 
44207             ) {
44208             
44209             range = this.createRange(this.getSelection());
44210             var wrappingNode = this.doc.createElement(tg.toLowerCase());
44211             wrappingNode.appendChild(range.extractContents());
44212             range.insertNode(wrappingNode);
44213
44214             return;
44215             
44216             
44217             
44218         }
44219         this.execCmd("formatblock",   tg);
44220         
44221     },
44222     
44223     insertText : function(txt)
44224     {
44225         
44226         
44227         var range = this.createRange();
44228         range.deleteContents();
44229                //alert(Sender.getAttribute('label'));
44230                
44231         range.insertNode(this.doc.createTextNode(txt));
44232     } ,
44233     
44234      
44235
44236     /**
44237      * Executes a Midas editor command on the editor document and performs necessary focus and
44238      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
44239      * @param {String} cmd The Midas command
44240      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44241      */
44242     relayCmd : function(cmd, value){
44243         this.win.focus();
44244         this.execCmd(cmd, value);
44245         this.owner.fireEvent('editorevent', this);
44246         //this.updateToolbar();
44247         this.owner.deferFocus();
44248     },
44249
44250     /**
44251      * Executes a Midas editor command directly on the editor document.
44252      * For visual commands, you should use {@link #relayCmd} instead.
44253      * <b>This should only be called after the editor is initialized.</b>
44254      * @param {String} cmd The Midas command
44255      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
44256      */
44257     execCmd : function(cmd, value){
44258         this.doc.execCommand(cmd, false, value === undefined ? null : value);
44259         this.syncValue();
44260     },
44261  
44262  
44263    
44264     /**
44265      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
44266      * to insert tRoo.
44267      * @param {String} text | dom node.. 
44268      */
44269     insertAtCursor : function(text)
44270     {
44271         
44272         if(!this.activated){
44273             return;
44274         }
44275         /*
44276         if(Roo.isIE){
44277             this.win.focus();
44278             var r = this.doc.selection.createRange();
44279             if(r){
44280                 r.collapse(true);
44281                 r.pasteHTML(text);
44282                 this.syncValue();
44283                 this.deferFocus();
44284             
44285             }
44286             return;
44287         }
44288         */
44289         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
44290             this.win.focus();
44291             
44292             
44293             // from jquery ui (MIT licenced)
44294             var range, node;
44295             var win = this.win;
44296             
44297             if (win.getSelection && win.getSelection().getRangeAt) {
44298                 range = win.getSelection().getRangeAt(0);
44299                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
44300                 range.insertNode(node);
44301             } else if (win.document.selection && win.document.selection.createRange) {
44302                 // no firefox support
44303                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44304                 win.document.selection.createRange().pasteHTML(txt);
44305             } else {
44306                 // no firefox support
44307                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
44308                 this.execCmd('InsertHTML', txt);
44309             } 
44310             
44311             this.syncValue();
44312             
44313             this.deferFocus();
44314         }
44315     },
44316  // private
44317     mozKeyPress : function(e){
44318         if(e.ctrlKey){
44319             var c = e.getCharCode(), cmd;
44320           
44321             if(c > 0){
44322                 c = String.fromCharCode(c).toLowerCase();
44323                 switch(c){
44324                     case 'b':
44325                         cmd = 'bold';
44326                         break;
44327                     case 'i':
44328                         cmd = 'italic';
44329                         break;
44330                     
44331                     case 'u':
44332                         cmd = 'underline';
44333                         break;
44334                     
44335                     case 'v':
44336                         this.cleanUpPaste.defer(100, this);
44337                         return;
44338                         
44339                 }
44340                 if(cmd){
44341                     this.win.focus();
44342                     this.execCmd(cmd);
44343                     this.deferFocus();
44344                     e.preventDefault();
44345                 }
44346                 
44347             }
44348         }
44349     },
44350
44351     // private
44352     fixKeys : function(){ // load time branching for fastest keydown performance
44353         if(Roo.isIE){
44354             return function(e){
44355                 var k = e.getKey(), r;
44356                 if(k == e.TAB){
44357                     e.stopEvent();
44358                     r = this.doc.selection.createRange();
44359                     if(r){
44360                         r.collapse(true);
44361                         r.pasteHTML('&#160;&#160;&#160;&#160;');
44362                         this.deferFocus();
44363                     }
44364                     return;
44365                 }
44366                 
44367                 if(k == e.ENTER){
44368                     r = this.doc.selection.createRange();
44369                     if(r){
44370                         var target = r.parentElement();
44371                         if(!target || target.tagName.toLowerCase() != 'li'){
44372                             e.stopEvent();
44373                             r.pasteHTML('<br />');
44374                             r.collapse(false);
44375                             r.select();
44376                         }
44377                     }
44378                 }
44379                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44380                     this.cleanUpPaste.defer(100, this);
44381                     return;
44382                 }
44383                 
44384                 
44385             };
44386         }else if(Roo.isOpera){
44387             return function(e){
44388                 var k = e.getKey();
44389                 if(k == e.TAB){
44390                     e.stopEvent();
44391                     this.win.focus();
44392                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
44393                     this.deferFocus();
44394                 }
44395                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44396                     this.cleanUpPaste.defer(100, this);
44397                     return;
44398                 }
44399                 
44400             };
44401         }else if(Roo.isSafari){
44402             return function(e){
44403                 var k = e.getKey();
44404                 
44405                 if(k == e.TAB){
44406                     e.stopEvent();
44407                     this.execCmd('InsertText','\t');
44408                     this.deferFocus();
44409                     return;
44410                 }
44411                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
44412                     this.cleanUpPaste.defer(100, this);
44413                     return;
44414                 }
44415                 
44416              };
44417         }
44418     }(),
44419     
44420     getAllAncestors: function()
44421     {
44422         var p = this.getSelectedNode();
44423         var a = [];
44424         if (!p) {
44425             a.push(p); // push blank onto stack..
44426             p = this.getParentElement();
44427         }
44428         
44429         
44430         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
44431             a.push(p);
44432             p = p.parentNode;
44433         }
44434         a.push(this.doc.body);
44435         return a;
44436     },
44437     lastSel : false,
44438     lastSelNode : false,
44439     
44440     
44441     getSelection : function() 
44442     {
44443         this.assignDocWin();
44444         return Roo.isIE ? this.doc.selection : this.win.getSelection();
44445     },
44446     
44447     getSelectedNode: function() 
44448     {
44449         // this may only work on Gecko!!!
44450         
44451         // should we cache this!!!!
44452         
44453         
44454         
44455          
44456         var range = this.createRange(this.getSelection()).cloneRange();
44457         
44458         if (Roo.isIE) {
44459             var parent = range.parentElement();
44460             while (true) {
44461                 var testRange = range.duplicate();
44462                 testRange.moveToElementText(parent);
44463                 if (testRange.inRange(range)) {
44464                     break;
44465                 }
44466                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
44467                     break;
44468                 }
44469                 parent = parent.parentElement;
44470             }
44471             return parent;
44472         }
44473         
44474         // is ancestor a text element.
44475         var ac =  range.commonAncestorContainer;
44476         if (ac.nodeType == 3) {
44477             ac = ac.parentNode;
44478         }
44479         
44480         var ar = ac.childNodes;
44481          
44482         var nodes = [];
44483         var other_nodes = [];
44484         var has_other_nodes = false;
44485         for (var i=0;i<ar.length;i++) {
44486             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
44487                 continue;
44488             }
44489             // fullly contained node.
44490             
44491             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
44492                 nodes.push(ar[i]);
44493                 continue;
44494             }
44495             
44496             // probably selected..
44497             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
44498                 other_nodes.push(ar[i]);
44499                 continue;
44500             }
44501             // outer..
44502             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
44503                 continue;
44504             }
44505             
44506             
44507             has_other_nodes = true;
44508         }
44509         if (!nodes.length && other_nodes.length) {
44510             nodes= other_nodes;
44511         }
44512         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
44513             return false;
44514         }
44515         
44516         return nodes[0];
44517     },
44518     createRange: function(sel)
44519     {
44520         // this has strange effects when using with 
44521         // top toolbar - not sure if it's a great idea.
44522         //this.editor.contentWindow.focus();
44523         if (typeof sel != "undefined") {
44524             try {
44525                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
44526             } catch(e) {
44527                 return this.doc.createRange();
44528             }
44529         } else {
44530             return this.doc.createRange();
44531         }
44532     },
44533     getParentElement: function()
44534     {
44535         
44536         this.assignDocWin();
44537         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
44538         
44539         var range = this.createRange(sel);
44540          
44541         try {
44542             var p = range.commonAncestorContainer;
44543             while (p.nodeType == 3) { // text node
44544                 p = p.parentNode;
44545             }
44546             return p;
44547         } catch (e) {
44548             return null;
44549         }
44550     
44551     },
44552     /***
44553      *
44554      * Range intersection.. the hard stuff...
44555      *  '-1' = before
44556      *  '0' = hits..
44557      *  '1' = after.
44558      *         [ -- selected range --- ]
44559      *   [fail]                        [fail]
44560      *
44561      *    basically..
44562      *      if end is before start or  hits it. fail.
44563      *      if start is after end or hits it fail.
44564      *
44565      *   if either hits (but other is outside. - then it's not 
44566      *   
44567      *    
44568      **/
44569     
44570     
44571     // @see http://www.thismuchiknow.co.uk/?p=64.
44572     rangeIntersectsNode : function(range, node)
44573     {
44574         var nodeRange = node.ownerDocument.createRange();
44575         try {
44576             nodeRange.selectNode(node);
44577         } catch (e) {
44578             nodeRange.selectNodeContents(node);
44579         }
44580     
44581         var rangeStartRange = range.cloneRange();
44582         rangeStartRange.collapse(true);
44583     
44584         var rangeEndRange = range.cloneRange();
44585         rangeEndRange.collapse(false);
44586     
44587         var nodeStartRange = nodeRange.cloneRange();
44588         nodeStartRange.collapse(true);
44589     
44590         var nodeEndRange = nodeRange.cloneRange();
44591         nodeEndRange.collapse(false);
44592     
44593         return rangeStartRange.compareBoundaryPoints(
44594                  Range.START_TO_START, nodeEndRange) == -1 &&
44595                rangeEndRange.compareBoundaryPoints(
44596                  Range.START_TO_START, nodeStartRange) == 1;
44597         
44598          
44599     },
44600     rangeCompareNode : function(range, node)
44601     {
44602         var nodeRange = node.ownerDocument.createRange();
44603         try {
44604             nodeRange.selectNode(node);
44605         } catch (e) {
44606             nodeRange.selectNodeContents(node);
44607         }
44608         
44609         
44610         range.collapse(true);
44611     
44612         nodeRange.collapse(true);
44613      
44614         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
44615         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
44616          
44617         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
44618         
44619         var nodeIsBefore   =  ss == 1;
44620         var nodeIsAfter    = ee == -1;
44621         
44622         if (nodeIsBefore && nodeIsAfter) {
44623             return 0; // outer
44624         }
44625         if (!nodeIsBefore && nodeIsAfter) {
44626             return 1; //right trailed.
44627         }
44628         
44629         if (nodeIsBefore && !nodeIsAfter) {
44630             return 2;  // left trailed.
44631         }
44632         // fully contined.
44633         return 3;
44634     },
44635
44636     // private? - in a new class?
44637     cleanUpPaste :  function()
44638     {
44639         // cleans up the whole document..
44640         Roo.log('cleanuppaste');
44641         
44642         this.cleanUpChildren(this.doc.body);
44643         var clean = this.cleanWordChars(this.doc.body.innerHTML);
44644         if (clean != this.doc.body.innerHTML) {
44645             this.doc.body.innerHTML = clean;
44646         }
44647         
44648     },
44649     
44650     cleanWordChars : function(input) {// change the chars to hex code
44651         var he = Roo.HtmlEditorCore;
44652         
44653         var output = input;
44654         Roo.each(he.swapCodes, function(sw) { 
44655             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
44656             
44657             output = output.replace(swapper, sw[1]);
44658         });
44659         
44660         return output;
44661     },
44662     
44663     
44664     cleanUpChildren : function (n)
44665     {
44666         if (!n.childNodes.length) {
44667             return;
44668         }
44669         for (var i = n.childNodes.length-1; i > -1 ; i--) {
44670            this.cleanUpChild(n.childNodes[i]);
44671         }
44672     },
44673     
44674     
44675         
44676     
44677     cleanUpChild : function (node)
44678     {
44679         var ed = this;
44680         //console.log(node);
44681         if (node.nodeName == "#text") {
44682             // clean up silly Windows -- stuff?
44683             return; 
44684         }
44685         if (node.nodeName == "#comment") {
44686             node.parentNode.removeChild(node);
44687             // clean up silly Windows -- stuff?
44688             return; 
44689         }
44690         var lcname = node.tagName.toLowerCase();
44691         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
44692         // whitelist of tags..
44693         
44694         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
44695             // remove node.
44696             node.parentNode.removeChild(node);
44697             return;
44698             
44699         }
44700         
44701         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
44702         
44703         // spans with no attributes - just remove them..
44704         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
44705             remove_keep_children = true;
44706         }
44707         
44708         // remove <a name=....> as rendering on yahoo mailer is borked with this.
44709         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
44710         
44711         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
44712         //    remove_keep_children = true;
44713         //}
44714         
44715         if (remove_keep_children) {
44716             this.cleanUpChildren(node);
44717             // inserts everything just before this node...
44718             while (node.childNodes.length) {
44719                 var cn = node.childNodes[0];
44720                 node.removeChild(cn);
44721                 node.parentNode.insertBefore(cn, node);
44722             }
44723             node.parentNode.removeChild(node);
44724             return;
44725         }
44726         
44727         if (!node.attributes || !node.attributes.length) {
44728             
44729           
44730             
44731             
44732             this.cleanUpChildren(node);
44733             return;
44734         }
44735         
44736         function cleanAttr(n,v)
44737         {
44738             
44739             if (v.match(/^\./) || v.match(/^\//)) {
44740                 return;
44741             }
44742             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44743                 return;
44744             }
44745             if (v.match(/^#/)) {
44746                 return;
44747             }
44748             if (v.match(/^\{/)) { // allow template editing.
44749                 return;
44750             }
44751 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44752             node.removeAttribute(n);
44753             
44754         }
44755         
44756         var cwhite = this.cwhite;
44757         var cblack = this.cblack;
44758             
44759         function cleanStyle(n,v)
44760         {
44761             if (v.match(/expression/)) { //XSS?? should we even bother..
44762                 node.removeAttribute(n);
44763                 return;
44764             }
44765             
44766             var parts = v.split(/;/);
44767             var clean = [];
44768             
44769             Roo.each(parts, function(p) {
44770                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44771                 if (!p.length) {
44772                     return true;
44773                 }
44774                 var l = p.split(':').shift().replace(/\s+/g,'');
44775                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44776                 
44777                 if ( cwhite.length && cblack.indexOf(l) > -1) {
44778 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44779                     //node.removeAttribute(n);
44780                     return true;
44781                 }
44782                 //Roo.log()
44783                 // only allow 'c whitelisted system attributes'
44784                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
44785 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
44786                     //node.removeAttribute(n);
44787                     return true;
44788                 }
44789                 
44790                 
44791                  
44792                 
44793                 clean.push(p);
44794                 return true;
44795             });
44796             if (clean.length) { 
44797                 node.setAttribute(n, clean.join(';'));
44798             } else {
44799                 node.removeAttribute(n);
44800             }
44801             
44802         }
44803         
44804         
44805         for (var i = node.attributes.length-1; i > -1 ; i--) {
44806             var a = node.attributes[i];
44807             //console.log(a);
44808             
44809             if (a.name.toLowerCase().substr(0,2)=='on')  {
44810                 node.removeAttribute(a.name);
44811                 continue;
44812             }
44813             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
44814                 node.removeAttribute(a.name);
44815                 continue;
44816             }
44817             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
44818                 cleanAttr(a.name,a.value); // fixme..
44819                 continue;
44820             }
44821             if (a.name == 'style') {
44822                 cleanStyle(a.name,a.value);
44823                 continue;
44824             }
44825             /// clean up MS crap..
44826             // tecnically this should be a list of valid class'es..
44827             
44828             
44829             if (a.name == 'class') {
44830                 if (a.value.match(/^Mso/)) {
44831                     node.removeAttribute('class');
44832                 }
44833                 
44834                 if (a.value.match(/^body$/)) {
44835                     node.removeAttribute('class');
44836                 }
44837                 continue;
44838             }
44839             
44840             // style cleanup!?
44841             // class cleanup?
44842             
44843         }
44844         
44845         
44846         this.cleanUpChildren(node);
44847         
44848         
44849     },
44850     
44851     /**
44852      * Clean up MS wordisms...
44853      */
44854     cleanWord : function(node)
44855     {
44856         if (!node) {
44857             this.cleanWord(this.doc.body);
44858             return;
44859         }
44860         
44861         if(
44862                 node.nodeName == 'SPAN' &&
44863                 !node.hasAttributes() &&
44864                 node.childNodes.length == 1 &&
44865                 node.firstChild.nodeName == "#text"  
44866         ) {
44867             var textNode = node.firstChild;
44868             node.removeChild(textNode);
44869             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44870                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44871             }
44872             node.parentNode.insertBefore(textNode, node);
44873             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44874                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44875             }
44876             node.parentNode.removeChild(node);
44877         }
44878         
44879         if (node.nodeName == "#text") {
44880             // clean up silly Windows -- stuff?
44881             return; 
44882         }
44883         if (node.nodeName == "#comment") {
44884             node.parentNode.removeChild(node);
44885             // clean up silly Windows -- stuff?
44886             return; 
44887         }
44888         
44889         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44890             node.parentNode.removeChild(node);
44891             return;
44892         }
44893         //Roo.log(node.tagName);
44894         // remove - but keep children..
44895         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44896             //Roo.log('-- removed');
44897             while (node.childNodes.length) {
44898                 var cn = node.childNodes[0];
44899                 node.removeChild(cn);
44900                 node.parentNode.insertBefore(cn, node);
44901                 // move node to parent - and clean it..
44902                 this.cleanWord(cn);
44903             }
44904             node.parentNode.removeChild(node);
44905             /// no need to iterate chidlren = it's got none..
44906             //this.iterateChildren(node, this.cleanWord);
44907             return;
44908         }
44909         // clean styles
44910         if (node.className.length) {
44911             
44912             var cn = node.className.split(/\W+/);
44913             var cna = [];
44914             Roo.each(cn, function(cls) {
44915                 if (cls.match(/Mso[a-zA-Z]+/)) {
44916                     return;
44917                 }
44918                 cna.push(cls);
44919             });
44920             node.className = cna.length ? cna.join(' ') : '';
44921             if (!cna.length) {
44922                 node.removeAttribute("class");
44923             }
44924         }
44925         
44926         if (node.hasAttribute("lang")) {
44927             node.removeAttribute("lang");
44928         }
44929         
44930         if (node.hasAttribute("style")) {
44931             
44932             var styles = node.getAttribute("style").split(";");
44933             var nstyle = [];
44934             Roo.each(styles, function(s) {
44935                 if (!s.match(/:/)) {
44936                     return;
44937                 }
44938                 var kv = s.split(":");
44939                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44940                     return;
44941                 }
44942                 // what ever is left... we allow.
44943                 nstyle.push(s);
44944             });
44945             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44946             if (!nstyle.length) {
44947                 node.removeAttribute('style');
44948             }
44949         }
44950         this.iterateChildren(node, this.cleanWord);
44951         
44952         
44953         
44954     },
44955     /**
44956      * iterateChildren of a Node, calling fn each time, using this as the scole..
44957      * @param {DomNode} node node to iterate children of.
44958      * @param {Function} fn method of this class to call on each item.
44959      */
44960     iterateChildren : function(node, fn)
44961     {
44962         if (!node.childNodes.length) {
44963                 return;
44964         }
44965         for (var i = node.childNodes.length-1; i > -1 ; i--) {
44966            fn.call(this, node.childNodes[i])
44967         }
44968     },
44969     
44970     
44971     /**
44972      * cleanTableWidths.
44973      *
44974      * Quite often pasting from word etc.. results in tables with column and widths.
44975      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44976      *
44977      */
44978     cleanTableWidths : function(node)
44979     {
44980          
44981          
44982         if (!node) {
44983             this.cleanTableWidths(this.doc.body);
44984             return;
44985         }
44986         
44987         // ignore list...
44988         if (node.nodeName == "#text" || node.nodeName == "#comment") {
44989             return; 
44990         }
44991         Roo.log(node.tagName);
44992         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
44993             this.iterateChildren(node, this.cleanTableWidths);
44994             return;
44995         }
44996         if (node.hasAttribute('width')) {
44997             node.removeAttribute('width');
44998         }
44999         
45000          
45001         if (node.hasAttribute("style")) {
45002             // pretty basic...
45003             
45004             var styles = node.getAttribute("style").split(";");
45005             var nstyle = [];
45006             Roo.each(styles, function(s) {
45007                 if (!s.match(/:/)) {
45008                     return;
45009                 }
45010                 var kv = s.split(":");
45011                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45012                     return;
45013                 }
45014                 // what ever is left... we allow.
45015                 nstyle.push(s);
45016             });
45017             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45018             if (!nstyle.length) {
45019                 node.removeAttribute('style');
45020             }
45021         }
45022         
45023         this.iterateChildren(node, this.cleanTableWidths);
45024         
45025         
45026     },
45027     
45028     
45029     
45030     
45031     domToHTML : function(currentElement, depth, nopadtext) {
45032         
45033         depth = depth || 0;
45034         nopadtext = nopadtext || false;
45035     
45036         if (!currentElement) {
45037             return this.domToHTML(this.doc.body);
45038         }
45039         
45040         //Roo.log(currentElement);
45041         var j;
45042         var allText = false;
45043         var nodeName = currentElement.nodeName;
45044         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45045         
45046         if  (nodeName == '#text') {
45047             
45048             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
45049         }
45050         
45051         
45052         var ret = '';
45053         if (nodeName != 'BODY') {
45054              
45055             var i = 0;
45056             // Prints the node tagName, such as <A>, <IMG>, etc
45057             if (tagName) {
45058                 var attr = [];
45059                 for(i = 0; i < currentElement.attributes.length;i++) {
45060                     // quoting?
45061                     var aname = currentElement.attributes.item(i).name;
45062                     if (!currentElement.attributes.item(i).value.length) {
45063                         continue;
45064                     }
45065                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
45066                 }
45067                 
45068                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
45069             } 
45070             else {
45071                 
45072                 // eack
45073             }
45074         } else {
45075             tagName = false;
45076         }
45077         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45078             return ret;
45079         }
45080         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45081             nopadtext = true;
45082         }
45083         
45084         
45085         // Traverse the tree
45086         i = 0;
45087         var currentElementChild = currentElement.childNodes.item(i);
45088         var allText = true;
45089         var innerHTML  = '';
45090         lastnode = '';
45091         while (currentElementChild) {
45092             // Formatting code (indent the tree so it looks nice on the screen)
45093             var nopad = nopadtext;
45094             if (lastnode == 'SPAN') {
45095                 nopad  = true;
45096             }
45097             // text
45098             if  (currentElementChild.nodeName == '#text') {
45099                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
45100                 toadd = nopadtext ? toadd : toadd.trim();
45101                 if (!nopad && toadd.length > 80) {
45102                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
45103                 }
45104                 innerHTML  += toadd;
45105                 
45106                 i++;
45107                 currentElementChild = currentElement.childNodes.item(i);
45108                 lastNode = '';
45109                 continue;
45110             }
45111             allText = false;
45112             
45113             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
45114                 
45115             // Recursively traverse the tree structure of the child node
45116             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
45117             lastnode = currentElementChild.nodeName;
45118             i++;
45119             currentElementChild=currentElement.childNodes.item(i);
45120         }
45121         
45122         ret += innerHTML;
45123         
45124         if (!allText) {
45125                 // The remaining code is mostly for formatting the tree
45126             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
45127         }
45128         
45129         
45130         if (tagName) {
45131             ret+= "</"+tagName+">";
45132         }
45133         return ret;
45134         
45135     },
45136         
45137     applyBlacklists : function()
45138     {
45139         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
45140         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
45141         
45142         this.white = [];
45143         this.black = [];
45144         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
45145             if (b.indexOf(tag) > -1) {
45146                 return;
45147             }
45148             this.white.push(tag);
45149             
45150         }, this);
45151         
45152         Roo.each(w, function(tag) {
45153             if (b.indexOf(tag) > -1) {
45154                 return;
45155             }
45156             if (this.white.indexOf(tag) > -1) {
45157                 return;
45158             }
45159             this.white.push(tag);
45160             
45161         }, this);
45162         
45163         
45164         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
45165             if (w.indexOf(tag) > -1) {
45166                 return;
45167             }
45168             this.black.push(tag);
45169             
45170         }, this);
45171         
45172         Roo.each(b, function(tag) {
45173             if (w.indexOf(tag) > -1) {
45174                 return;
45175             }
45176             if (this.black.indexOf(tag) > -1) {
45177                 return;
45178             }
45179             this.black.push(tag);
45180             
45181         }, this);
45182         
45183         
45184         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
45185         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
45186         
45187         this.cwhite = [];
45188         this.cblack = [];
45189         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
45190             if (b.indexOf(tag) > -1) {
45191                 return;
45192             }
45193             this.cwhite.push(tag);
45194             
45195         }, this);
45196         
45197         Roo.each(w, function(tag) {
45198             if (b.indexOf(tag) > -1) {
45199                 return;
45200             }
45201             if (this.cwhite.indexOf(tag) > -1) {
45202                 return;
45203             }
45204             this.cwhite.push(tag);
45205             
45206         }, this);
45207         
45208         
45209         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
45210             if (w.indexOf(tag) > -1) {
45211                 return;
45212             }
45213             this.cblack.push(tag);
45214             
45215         }, this);
45216         
45217         Roo.each(b, function(tag) {
45218             if (w.indexOf(tag) > -1) {
45219                 return;
45220             }
45221             if (this.cblack.indexOf(tag) > -1) {
45222                 return;
45223             }
45224             this.cblack.push(tag);
45225             
45226         }, this);
45227     },
45228     
45229     setStylesheets : function(stylesheets)
45230     {
45231         if(typeof(stylesheets) == 'string'){
45232             Roo.get(this.iframe.contentDocument.head).createChild({
45233                 tag : 'link',
45234                 rel : 'stylesheet',
45235                 type : 'text/css',
45236                 href : stylesheets
45237             });
45238             
45239             return;
45240         }
45241         var _this = this;
45242      
45243         Roo.each(stylesheets, function(s) {
45244             if(!s.length){
45245                 return;
45246             }
45247             
45248             Roo.get(_this.iframe.contentDocument.head).createChild({
45249                 tag : 'link',
45250                 rel : 'stylesheet',
45251                 type : 'text/css',
45252                 href : s
45253             });
45254         });
45255
45256         
45257     },
45258     
45259     removeStylesheets : function()
45260     {
45261         var _this = this;
45262         
45263         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
45264             s.remove();
45265         });
45266     },
45267     
45268     setStyle : function(style)
45269     {
45270         Roo.get(this.iframe.contentDocument.head).createChild({
45271             tag : 'style',
45272             type : 'text/css',
45273             html : style
45274         });
45275
45276         return;
45277     }
45278     
45279     // hide stuff that is not compatible
45280     /**
45281      * @event blur
45282      * @hide
45283      */
45284     /**
45285      * @event change
45286      * @hide
45287      */
45288     /**
45289      * @event focus
45290      * @hide
45291      */
45292     /**
45293      * @event specialkey
45294      * @hide
45295      */
45296     /**
45297      * @cfg {String} fieldClass @hide
45298      */
45299     /**
45300      * @cfg {String} focusClass @hide
45301      */
45302     /**
45303      * @cfg {String} autoCreate @hide
45304      */
45305     /**
45306      * @cfg {String} inputType @hide
45307      */
45308     /**
45309      * @cfg {String} invalidClass @hide
45310      */
45311     /**
45312      * @cfg {String} invalidText @hide
45313      */
45314     /**
45315      * @cfg {String} msgFx @hide
45316      */
45317     /**
45318      * @cfg {String} validateOnBlur @hide
45319      */
45320 });
45321
45322 Roo.HtmlEditorCore.white = [
45323         'area', 'br', 'img', 'input', 'hr', 'wbr',
45324         
45325        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
45326        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
45327        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
45328        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
45329        'table',   'ul',         'xmp', 
45330        
45331        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
45332       'thead',   'tr', 
45333      
45334       'dir', 'menu', 'ol', 'ul', 'dl',
45335        
45336       'embed',  'object'
45337 ];
45338
45339
45340 Roo.HtmlEditorCore.black = [
45341     //    'embed',  'object', // enable - backend responsiblity to clean thiese
45342         'applet', // 
45343         'base',   'basefont', 'bgsound', 'blink',  'body', 
45344         'frame',  'frameset', 'head',    'html',   'ilayer', 
45345         'iframe', 'layer',  'link',     'meta',    'object',   
45346         'script', 'style' ,'title',  'xml' // clean later..
45347 ];
45348 Roo.HtmlEditorCore.clean = [
45349     'script', 'style', 'title', 'xml'
45350 ];
45351 Roo.HtmlEditorCore.remove = [
45352     'font'
45353 ];
45354 // attributes..
45355
45356 Roo.HtmlEditorCore.ablack = [
45357     'on'
45358 ];
45359     
45360 Roo.HtmlEditorCore.aclean = [ 
45361     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
45362 ];
45363
45364 // protocols..
45365 Roo.HtmlEditorCore.pwhite= [
45366         'http',  'https',  'mailto'
45367 ];
45368
45369 // white listed style attributes.
45370 Roo.HtmlEditorCore.cwhite= [
45371       //  'text-align', /// default is to allow most things..
45372       
45373          
45374 //        'font-size'//??
45375 ];
45376
45377 // black listed style attributes.
45378 Roo.HtmlEditorCore.cblack= [
45379       //  'font-size' -- this can be set by the project 
45380 ];
45381
45382
45383 Roo.HtmlEditorCore.swapCodes   =[ 
45384     [    8211, "&#8211;" ], 
45385     [    8212, "&#8212;" ], 
45386     [    8216,  "'" ],  
45387     [    8217, "'" ],  
45388     [    8220, '"' ],  
45389     [    8221, '"' ],  
45390     [    8226, "*" ],  
45391     [    8230, "..." ]
45392 ]; 
45393
45394     //<script type="text/javascript">
45395
45396 /*
45397  * Ext JS Library 1.1.1
45398  * Copyright(c) 2006-2007, Ext JS, LLC.
45399  * Licence LGPL
45400  * 
45401  */
45402  
45403  
45404 Roo.form.HtmlEditor = function(config){
45405     
45406     
45407     
45408     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
45409     
45410     if (!this.toolbars) {
45411         this.toolbars = [];
45412     }
45413     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
45414     
45415     
45416 };
45417
45418 /**
45419  * @class Roo.form.HtmlEditor
45420  * @extends Roo.form.Field
45421  * Provides a lightweight HTML Editor component.
45422  *
45423  * This has been tested on Fireforx / Chrome.. IE may not be so great..
45424  * 
45425  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
45426  * supported by this editor.</b><br/><br/>
45427  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
45428  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45429  */
45430 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
45431     /**
45432      * @cfg {Boolean} clearUp
45433      */
45434     clearUp : true,
45435       /**
45436      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
45437      */
45438     toolbars : false,
45439    
45440      /**
45441      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45442      *                        Roo.resizable.
45443      */
45444     resizable : false,
45445      /**
45446      * @cfg {Number} height (in pixels)
45447      */   
45448     height: 300,
45449    /**
45450      * @cfg {Number} width (in pixels)
45451      */   
45452     width: 500,
45453     
45454     /**
45455      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45456      * 
45457      */
45458     stylesheets: false,
45459     
45460     
45461      /**
45462      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
45463      * 
45464      */
45465     cblack: false,
45466     /**
45467      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
45468      * 
45469      */
45470     cwhite: false,
45471     
45472      /**
45473      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
45474      * 
45475      */
45476     black: false,
45477     /**
45478      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
45479      * 
45480      */
45481     white: false,
45482     
45483     // id of frame..
45484     frameId: false,
45485     
45486     // private properties
45487     validationEvent : false,
45488     deferHeight: true,
45489     initialized : false,
45490     activated : false,
45491     
45492     onFocus : Roo.emptyFn,
45493     iframePad:3,
45494     hideMode:'offsets',
45495     
45496     actionMode : 'container', // defaults to hiding it...
45497     
45498     defaultAutoCreate : { // modified by initCompnoent..
45499         tag: "textarea",
45500         style:"width:500px;height:300px;",
45501         autocomplete: "new-password"
45502     },
45503
45504     // private
45505     initComponent : function(){
45506         this.addEvents({
45507             /**
45508              * @event initialize
45509              * Fires when the editor is fully initialized (including the iframe)
45510              * @param {HtmlEditor} this
45511              */
45512             initialize: true,
45513             /**
45514              * @event activate
45515              * Fires when the editor is first receives the focus. Any insertion must wait
45516              * until after this event.
45517              * @param {HtmlEditor} this
45518              */
45519             activate: true,
45520              /**
45521              * @event beforesync
45522              * Fires before the textarea is updated with content from the editor iframe. Return false
45523              * to cancel the sync.
45524              * @param {HtmlEditor} this
45525              * @param {String} html
45526              */
45527             beforesync: true,
45528              /**
45529              * @event beforepush
45530              * Fires before the iframe editor is updated with content from the textarea. Return false
45531              * to cancel the push.
45532              * @param {HtmlEditor} this
45533              * @param {String} html
45534              */
45535             beforepush: true,
45536              /**
45537              * @event sync
45538              * Fires when the textarea is updated with content from the editor iframe.
45539              * @param {HtmlEditor} this
45540              * @param {String} html
45541              */
45542             sync: true,
45543              /**
45544              * @event push
45545              * Fires when the iframe editor is updated with content from the textarea.
45546              * @param {HtmlEditor} this
45547              * @param {String} html
45548              */
45549             push: true,
45550              /**
45551              * @event editmodechange
45552              * Fires when the editor switches edit modes
45553              * @param {HtmlEditor} this
45554              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
45555              */
45556             editmodechange: true,
45557             /**
45558              * @event editorevent
45559              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45560              * @param {HtmlEditor} this
45561              */
45562             editorevent: true,
45563             /**
45564              * @event firstfocus
45565              * Fires when on first focus - needed by toolbars..
45566              * @param {HtmlEditor} this
45567              */
45568             firstfocus: true,
45569             /**
45570              * @event autosave
45571              * Auto save the htmlEditor value as a file into Events
45572              * @param {HtmlEditor} this
45573              */
45574             autosave: true,
45575             /**
45576              * @event savedpreview
45577              * preview the saved version of htmlEditor
45578              * @param {HtmlEditor} this
45579              */
45580             savedpreview: true,
45581             
45582             /**
45583             * @event stylesheetsclick
45584             * Fires when press the Sytlesheets button
45585             * @param {Roo.HtmlEditorCore} this
45586             */
45587             stylesheetsclick: true
45588         });
45589         this.defaultAutoCreate =  {
45590             tag: "textarea",
45591             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
45592             autocomplete: "new-password"
45593         };
45594     },
45595
45596     /**
45597      * Protected method that will not generally be called directly. It
45598      * is called when the editor creates its toolbar. Override this method if you need to
45599      * add custom toolbar buttons.
45600      * @param {HtmlEditor} editor
45601      */
45602     createToolbar : function(editor){
45603         Roo.log("create toolbars");
45604         if (!editor.toolbars || !editor.toolbars.length) {
45605             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
45606         }
45607         
45608         for (var i =0 ; i < editor.toolbars.length;i++) {
45609             editor.toolbars[i] = Roo.factory(
45610                     typeof(editor.toolbars[i]) == 'string' ?
45611                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
45612                 Roo.form.HtmlEditor);
45613             editor.toolbars[i].init(editor);
45614         }
45615          
45616         
45617     },
45618
45619      
45620     // private
45621     onRender : function(ct, position)
45622     {
45623         var _t = this;
45624         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
45625         
45626         this.wrap = this.el.wrap({
45627             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
45628         });
45629         
45630         this.editorcore.onRender(ct, position);
45631          
45632         if (this.resizable) {
45633             this.resizeEl = new Roo.Resizable(this.wrap, {
45634                 pinned : true,
45635                 wrap: true,
45636                 dynamic : true,
45637                 minHeight : this.height,
45638                 height: this.height,
45639                 handles : this.resizable,
45640                 width: this.width,
45641                 listeners : {
45642                     resize : function(r, w, h) {
45643                         _t.onResize(w,h); // -something
45644                     }
45645                 }
45646             });
45647             
45648         }
45649         this.createToolbar(this);
45650        
45651         
45652         if(!this.width){
45653             this.setSize(this.wrap.getSize());
45654         }
45655         if (this.resizeEl) {
45656             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
45657             // should trigger onReize..
45658         }
45659         
45660         this.keyNav = new Roo.KeyNav(this.el, {
45661             
45662             "tab" : function(e){
45663                 e.preventDefault();
45664                 
45665                 var value = this.getValue();
45666                 
45667                 var start = this.el.dom.selectionStart;
45668                 var end = this.el.dom.selectionEnd;
45669                 
45670                 if(!e.shiftKey){
45671                     
45672                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
45673                     this.el.dom.setSelectionRange(end + 1, end + 1);
45674                     return;
45675                 }
45676                 
45677                 var f = value.substring(0, start).split("\t");
45678                 
45679                 if(f.pop().length != 0){
45680                     return;
45681                 }
45682                 
45683                 this.setValue(f.join("\t") + value.substring(end));
45684                 this.el.dom.setSelectionRange(start - 1, start - 1);
45685                 
45686             },
45687             
45688             "home" : function(e){
45689                 e.preventDefault();
45690                 
45691                 var curr = this.el.dom.selectionStart;
45692                 var lines = this.getValue().split("\n");
45693                 
45694                 if(!lines.length){
45695                     return;
45696                 }
45697                 
45698                 if(e.ctrlKey){
45699                     this.el.dom.setSelectionRange(0, 0);
45700                     return;
45701                 }
45702                 
45703                 var pos = 0;
45704                 
45705                 for (var i = 0; i < lines.length;i++) {
45706                     pos += lines[i].length;
45707                     
45708                     if(i != 0){
45709                         pos += 1;
45710                     }
45711                     
45712                     if(pos < curr){
45713                         continue;
45714                     }
45715                     
45716                     pos -= lines[i].length;
45717                     
45718                     break;
45719                 }
45720                 
45721                 if(!e.shiftKey){
45722                     this.el.dom.setSelectionRange(pos, pos);
45723                     return;
45724                 }
45725                 
45726                 this.el.dom.selectionStart = pos;
45727                 this.el.dom.selectionEnd = curr;
45728             },
45729             
45730             "end" : function(e){
45731                 e.preventDefault();
45732                 
45733                 var curr = this.el.dom.selectionStart;
45734                 var lines = this.getValue().split("\n");
45735                 
45736                 if(!lines.length){
45737                     return;
45738                 }
45739                 
45740                 if(e.ctrlKey){
45741                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
45742                     return;
45743                 }
45744                 
45745                 var pos = 0;
45746                 
45747                 for (var i = 0; i < lines.length;i++) {
45748                     
45749                     pos += lines[i].length;
45750                     
45751                     if(i != 0){
45752                         pos += 1;
45753                     }
45754                     
45755                     if(pos < curr){
45756                         continue;
45757                     }
45758                     
45759                     break;
45760                 }
45761                 
45762                 if(!e.shiftKey){
45763                     this.el.dom.setSelectionRange(pos, pos);
45764                     return;
45765                 }
45766                 
45767                 this.el.dom.selectionStart = curr;
45768                 this.el.dom.selectionEnd = pos;
45769             },
45770
45771             scope : this,
45772
45773             doRelay : function(foo, bar, hname){
45774                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
45775             },
45776
45777             forceKeyDown: true
45778         });
45779         
45780 //        if(this.autosave && this.w){
45781 //            this.autoSaveFn = setInterval(this.autosave, 1000);
45782 //        }
45783     },
45784
45785     // private
45786     onResize : function(w, h)
45787     {
45788         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
45789         var ew = false;
45790         var eh = false;
45791         
45792         if(this.el ){
45793             if(typeof w == 'number'){
45794                 var aw = w - this.wrap.getFrameWidth('lr');
45795                 this.el.setWidth(this.adjustWidth('textarea', aw));
45796                 ew = aw;
45797             }
45798             if(typeof h == 'number'){
45799                 var tbh = 0;
45800                 for (var i =0; i < this.toolbars.length;i++) {
45801                     // fixme - ask toolbars for heights?
45802                     tbh += this.toolbars[i].tb.el.getHeight();
45803                     if (this.toolbars[i].footer) {
45804                         tbh += this.toolbars[i].footer.el.getHeight();
45805                     }
45806                 }
45807                 
45808                 
45809                 
45810                 
45811                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
45812                 ah -= 5; // knock a few pixes off for look..
45813 //                Roo.log(ah);
45814                 this.el.setHeight(this.adjustWidth('textarea', ah));
45815                 var eh = ah;
45816             }
45817         }
45818         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
45819         this.editorcore.onResize(ew,eh);
45820         
45821     },
45822
45823     /**
45824      * Toggles the editor between standard and source edit mode.
45825      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45826      */
45827     toggleSourceEdit : function(sourceEditMode)
45828     {
45829         this.editorcore.toggleSourceEdit(sourceEditMode);
45830         
45831         if(this.editorcore.sourceEditMode){
45832             Roo.log('editor - showing textarea');
45833             
45834 //            Roo.log('in');
45835 //            Roo.log(this.syncValue());
45836             this.editorcore.syncValue();
45837             this.el.removeClass('x-hidden');
45838             this.el.dom.removeAttribute('tabIndex');
45839             this.el.focus();
45840             
45841             for (var i = 0; i < this.toolbars.length; i++) {
45842                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45843                     this.toolbars[i].tb.hide();
45844                     this.toolbars[i].footer.hide();
45845                 }
45846             }
45847             
45848         }else{
45849             Roo.log('editor - hiding textarea');
45850 //            Roo.log('out')
45851 //            Roo.log(this.pushValue()); 
45852             this.editorcore.pushValue();
45853             
45854             this.el.addClass('x-hidden');
45855             this.el.dom.setAttribute('tabIndex', -1);
45856             
45857             for (var i = 0; i < this.toolbars.length; i++) {
45858                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
45859                     this.toolbars[i].tb.show();
45860                     this.toolbars[i].footer.show();
45861                 }
45862             }
45863             
45864             //this.deferFocus();
45865         }
45866         
45867         this.setSize(this.wrap.getSize());
45868         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
45869         
45870         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
45871     },
45872  
45873     // private (for BoxComponent)
45874     adjustSize : Roo.BoxComponent.prototype.adjustSize,
45875
45876     // private (for BoxComponent)
45877     getResizeEl : function(){
45878         return this.wrap;
45879     },
45880
45881     // private (for BoxComponent)
45882     getPositionEl : function(){
45883         return this.wrap;
45884     },
45885
45886     // private
45887     initEvents : function(){
45888         this.originalValue = this.getValue();
45889     },
45890
45891     /**
45892      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45893      * @method
45894      */
45895     markInvalid : Roo.emptyFn,
45896     /**
45897      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
45898      * @method
45899      */
45900     clearInvalid : Roo.emptyFn,
45901
45902     setValue : function(v){
45903         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
45904         this.editorcore.pushValue();
45905     },
45906
45907      
45908     // private
45909     deferFocus : function(){
45910         this.focus.defer(10, this);
45911     },
45912
45913     // doc'ed in Field
45914     focus : function(){
45915         this.editorcore.focus();
45916         
45917     },
45918       
45919
45920     // private
45921     onDestroy : function(){
45922         
45923         
45924         
45925         if(this.rendered){
45926             
45927             for (var i =0; i < this.toolbars.length;i++) {
45928                 // fixme - ask toolbars for heights?
45929                 this.toolbars[i].onDestroy();
45930             }
45931             
45932             this.wrap.dom.innerHTML = '';
45933             this.wrap.remove();
45934         }
45935     },
45936
45937     // private
45938     onFirstFocus : function(){
45939         //Roo.log("onFirstFocus");
45940         this.editorcore.onFirstFocus();
45941          for (var i =0; i < this.toolbars.length;i++) {
45942             this.toolbars[i].onFirstFocus();
45943         }
45944         
45945     },
45946     
45947     // private
45948     syncValue : function()
45949     {
45950         this.editorcore.syncValue();
45951     },
45952     
45953     pushValue : function()
45954     {
45955         this.editorcore.pushValue();
45956     },
45957     
45958     setStylesheets : function(stylesheets)
45959     {
45960         this.editorcore.setStylesheets(stylesheets);
45961     },
45962     
45963     removeStylesheets : function()
45964     {
45965         this.editorcore.removeStylesheets();
45966     }
45967      
45968     
45969     // hide stuff that is not compatible
45970     /**
45971      * @event blur
45972      * @hide
45973      */
45974     /**
45975      * @event change
45976      * @hide
45977      */
45978     /**
45979      * @event focus
45980      * @hide
45981      */
45982     /**
45983      * @event specialkey
45984      * @hide
45985      */
45986     /**
45987      * @cfg {String} fieldClass @hide
45988      */
45989     /**
45990      * @cfg {String} focusClass @hide
45991      */
45992     /**
45993      * @cfg {String} autoCreate @hide
45994      */
45995     /**
45996      * @cfg {String} inputType @hide
45997      */
45998     /**
45999      * @cfg {String} invalidClass @hide
46000      */
46001     /**
46002      * @cfg {String} invalidText @hide
46003      */
46004     /**
46005      * @cfg {String} msgFx @hide
46006      */
46007     /**
46008      * @cfg {String} validateOnBlur @hide
46009      */
46010 });
46011  
46012     // <script type="text/javascript">
46013 /*
46014  * Based on
46015  * Ext JS Library 1.1.1
46016  * Copyright(c) 2006-2007, Ext JS, LLC.
46017  *  
46018  
46019  */
46020
46021 /**
46022  * @class Roo.form.HtmlEditorToolbar1
46023  * Basic Toolbar
46024  * 
46025  * Usage:
46026  *
46027  new Roo.form.HtmlEditor({
46028     ....
46029     toolbars : [
46030         new Roo.form.HtmlEditorToolbar1({
46031             disable : { fonts: 1 , format: 1, ..., ... , ...],
46032             btns : [ .... ]
46033         })
46034     }
46035      
46036  * 
46037  * @cfg {Object} disable List of elements to disable..
46038  * @cfg {Array} btns List of additional buttons.
46039  * 
46040  * 
46041  * NEEDS Extra CSS? 
46042  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
46043  */
46044  
46045 Roo.form.HtmlEditor.ToolbarStandard = function(config)
46046 {
46047     
46048     Roo.apply(this, config);
46049     
46050     // default disabled, based on 'good practice'..
46051     this.disable = this.disable || {};
46052     Roo.applyIf(this.disable, {
46053         fontSize : true,
46054         colors : true,
46055         specialElements : true
46056     });
46057     
46058     
46059     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46060     // dont call parent... till later.
46061 }
46062
46063 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
46064     
46065     tb: false,
46066     
46067     rendered: false,
46068     
46069     editor : false,
46070     editorcore : false,
46071     /**
46072      * @cfg {Object} disable  List of toolbar elements to disable
46073          
46074      */
46075     disable : false,
46076     
46077     
46078      /**
46079      * @cfg {String} createLinkText The default text for the create link prompt
46080      */
46081     createLinkText : 'Please enter the URL for the link:',
46082     /**
46083      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
46084      */
46085     defaultLinkValue : 'http:/'+'/',
46086    
46087     
46088       /**
46089      * @cfg {Array} fontFamilies An array of available font families
46090      */
46091     fontFamilies : [
46092         'Arial',
46093         'Courier New',
46094         'Tahoma',
46095         'Times New Roman',
46096         'Verdana'
46097     ],
46098     
46099     specialChars : [
46100            "&#169;",
46101           "&#174;",     
46102           "&#8482;",    
46103           "&#163;" ,    
46104          // "&#8212;",    
46105           "&#8230;",    
46106           "&#247;" ,    
46107         //  "&#225;" ,     ?? a acute?
46108            "&#8364;"    , //Euro
46109        //   "&#8220;"    ,
46110         //  "&#8221;"    ,
46111         //  "&#8226;"    ,
46112           "&#176;"  //   , // degrees
46113
46114          // "&#233;"     , // e ecute
46115          // "&#250;"     , // u ecute?
46116     ],
46117     
46118     specialElements : [
46119         {
46120             text: "Insert Table",
46121             xtype: 'MenuItem',
46122             xns : Roo.Menu,
46123             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
46124                 
46125         },
46126         {    
46127             text: "Insert Image",
46128             xtype: 'MenuItem',
46129             xns : Roo.Menu,
46130             ihtml : '<img src="about:blank"/>'
46131             
46132         }
46133         
46134          
46135     ],
46136     
46137     
46138     inputElements : [ 
46139             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
46140             "input:submit", "input:button", "select", "textarea", "label" ],
46141     formats : [
46142         ["p"] ,  
46143         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
46144         ["pre"],[ "code"], 
46145         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
46146         ['div'],['span'],
46147         ['sup'],['sub']
46148     ],
46149     
46150     cleanStyles : [
46151         "font-size"
46152     ],
46153      /**
46154      * @cfg {String} defaultFont default font to use.
46155      */
46156     defaultFont: 'tahoma',
46157    
46158     fontSelect : false,
46159     
46160     
46161     formatCombo : false,
46162     
46163     init : function(editor)
46164     {
46165         this.editor = editor;
46166         this.editorcore = editor.editorcore ? editor.editorcore : editor;
46167         var editorcore = this.editorcore;
46168         
46169         var _t = this;
46170         
46171         var fid = editorcore.frameId;
46172         var etb = this;
46173         function btn(id, toggle, handler){
46174             var xid = fid + '-'+ id ;
46175             return {
46176                 id : xid,
46177                 cmd : id,
46178                 cls : 'x-btn-icon x-edit-'+id,
46179                 enableToggle:toggle !== false,
46180                 scope: _t, // was editor...
46181                 handler:handler||_t.relayBtnCmd,
46182                 clickEvent:'mousedown',
46183                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
46184                 tabIndex:-1
46185             };
46186         }
46187         
46188         
46189         
46190         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
46191         this.tb = tb;
46192          // stop form submits
46193         tb.el.on('click', function(e){
46194             e.preventDefault(); // what does this do?
46195         });
46196
46197         if(!this.disable.font) { // && !Roo.isSafari){
46198             /* why no safari for fonts 
46199             editor.fontSelect = tb.el.createChild({
46200                 tag:'select',
46201                 tabIndex: -1,
46202                 cls:'x-font-select',
46203                 html: this.createFontOptions()
46204             });
46205             
46206             editor.fontSelect.on('change', function(){
46207                 var font = editor.fontSelect.dom.value;
46208                 editor.relayCmd('fontname', font);
46209                 editor.deferFocus();
46210             }, editor);
46211             
46212             tb.add(
46213                 editor.fontSelect.dom,
46214                 '-'
46215             );
46216             */
46217             
46218         };
46219         if(!this.disable.formats){
46220             this.formatCombo = new Roo.form.ComboBox({
46221                 store: new Roo.data.SimpleStore({
46222                     id : 'tag',
46223                     fields: ['tag'],
46224                     data : this.formats // from states.js
46225                 }),
46226                 blockFocus : true,
46227                 name : '',
46228                 //autoCreate : {tag: "div",  size: "20"},
46229                 displayField:'tag',
46230                 typeAhead: false,
46231                 mode: 'local',
46232                 editable : false,
46233                 triggerAction: 'all',
46234                 emptyText:'Add tag',
46235                 selectOnFocus:true,
46236                 width:135,
46237                 listeners : {
46238                     'select': function(c, r, i) {
46239                         editorcore.insertTag(r.get('tag'));
46240                         editor.focus();
46241                     }
46242                 }
46243
46244             });
46245             tb.addField(this.formatCombo);
46246             
46247         }
46248         
46249         if(!this.disable.format){
46250             tb.add(
46251                 btn('bold'),
46252                 btn('italic'),
46253                 btn('underline'),
46254                 btn('strikethrough')
46255             );
46256         };
46257         if(!this.disable.fontSize){
46258             tb.add(
46259                 '-',
46260                 
46261                 
46262                 btn('increasefontsize', false, editorcore.adjustFont),
46263                 btn('decreasefontsize', false, editorcore.adjustFont)
46264             );
46265         };
46266         
46267         
46268         if(!this.disable.colors){
46269             tb.add(
46270                 '-', {
46271                     id:editorcore.frameId +'-forecolor',
46272                     cls:'x-btn-icon x-edit-forecolor',
46273                     clickEvent:'mousedown',
46274                     tooltip: this.buttonTips['forecolor'] || undefined,
46275                     tabIndex:-1,
46276                     menu : new Roo.menu.ColorMenu({
46277                         allowReselect: true,
46278                         focus: Roo.emptyFn,
46279                         value:'000000',
46280                         plain:true,
46281                         selectHandler: function(cp, color){
46282                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
46283                             editor.deferFocus();
46284                         },
46285                         scope: editorcore,
46286                         clickEvent:'mousedown'
46287                     })
46288                 }, {
46289                     id:editorcore.frameId +'backcolor',
46290                     cls:'x-btn-icon x-edit-backcolor',
46291                     clickEvent:'mousedown',
46292                     tooltip: this.buttonTips['backcolor'] || undefined,
46293                     tabIndex:-1,
46294                     menu : new Roo.menu.ColorMenu({
46295                         focus: Roo.emptyFn,
46296                         value:'FFFFFF',
46297                         plain:true,
46298                         allowReselect: true,
46299                         selectHandler: function(cp, color){
46300                             if(Roo.isGecko){
46301                                 editorcore.execCmd('useCSS', false);
46302                                 editorcore.execCmd('hilitecolor', color);
46303                                 editorcore.execCmd('useCSS', true);
46304                                 editor.deferFocus();
46305                             }else{
46306                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
46307                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
46308                                 editor.deferFocus();
46309                             }
46310                         },
46311                         scope:editorcore,
46312                         clickEvent:'mousedown'
46313                     })
46314                 }
46315             );
46316         };
46317         // now add all the items...
46318         
46319
46320         if(!this.disable.alignments){
46321             tb.add(
46322                 '-',
46323                 btn('justifyleft'),
46324                 btn('justifycenter'),
46325                 btn('justifyright')
46326             );
46327         };
46328
46329         //if(!Roo.isSafari){
46330             if(!this.disable.links){
46331                 tb.add(
46332                     '-',
46333                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
46334                 );
46335             };
46336
46337             if(!this.disable.lists){
46338                 tb.add(
46339                     '-',
46340                     btn('insertorderedlist'),
46341                     btn('insertunorderedlist')
46342                 );
46343             }
46344             if(!this.disable.sourceEdit){
46345                 tb.add(
46346                     '-',
46347                     btn('sourceedit', true, function(btn){
46348                         this.toggleSourceEdit(btn.pressed);
46349                     })
46350                 );
46351             }
46352         //}
46353         
46354         var smenu = { };
46355         // special menu.. - needs to be tidied up..
46356         if (!this.disable.special) {
46357             smenu = {
46358                 text: "&#169;",
46359                 cls: 'x-edit-none',
46360                 
46361                 menu : {
46362                     items : []
46363                 }
46364             };
46365             for (var i =0; i < this.specialChars.length; i++) {
46366                 smenu.menu.items.push({
46367                     
46368                     html: this.specialChars[i],
46369                     handler: function(a,b) {
46370                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
46371                         //editor.insertAtCursor(a.html);
46372                         
46373                     },
46374                     tabIndex:-1
46375                 });
46376             }
46377             
46378             
46379             tb.add(smenu);
46380             
46381             
46382         }
46383         
46384         var cmenu = { };
46385         if (!this.disable.cleanStyles) {
46386             cmenu = {
46387                 cls: 'x-btn-icon x-btn-clear',
46388                 
46389                 menu : {
46390                     items : []
46391                 }
46392             };
46393             for (var i =0; i < this.cleanStyles.length; i++) {
46394                 cmenu.menu.items.push({
46395                     actiontype : this.cleanStyles[i],
46396                     html: 'Remove ' + this.cleanStyles[i],
46397                     handler: function(a,b) {
46398 //                        Roo.log(a);
46399 //                        Roo.log(b);
46400                         var c = Roo.get(editorcore.doc.body);
46401                         c.select('[style]').each(function(s) {
46402                             s.dom.style.removeProperty(a.actiontype);
46403                         });
46404                         editorcore.syncValue();
46405                     },
46406                     tabIndex:-1
46407                 });
46408             }
46409              cmenu.menu.items.push({
46410                 actiontype : 'tablewidths',
46411                 html: 'Remove Table Widths',
46412                 handler: function(a,b) {
46413                     editorcore.cleanTableWidths();
46414                     editorcore.syncValue();
46415                 },
46416                 tabIndex:-1
46417             });
46418             cmenu.menu.items.push({
46419                 actiontype : 'word',
46420                 html: 'Remove MS Word Formating',
46421                 handler: function(a,b) {
46422                     editorcore.cleanWord();
46423                     editorcore.syncValue();
46424                 },
46425                 tabIndex:-1
46426             });
46427             
46428             cmenu.menu.items.push({
46429                 actiontype : 'all',
46430                 html: 'Remove All Styles',
46431                 handler: function(a,b) {
46432                     
46433                     var c = Roo.get(editorcore.doc.body);
46434                     c.select('[style]').each(function(s) {
46435                         s.dom.removeAttribute('style');
46436                     });
46437                     editorcore.syncValue();
46438                 },
46439                 tabIndex:-1
46440             });
46441             
46442             cmenu.menu.items.push({
46443                 actiontype : 'all',
46444                 html: 'Remove All CSS Classes',
46445                 handler: function(a,b) {
46446                     
46447                     var c = Roo.get(editorcore.doc.body);
46448                     c.select('[class]').each(function(s) {
46449                         s.dom.removeAttribute('class');
46450                     });
46451                     editorcore.cleanWord();
46452                     editorcore.syncValue();
46453                 },
46454                 tabIndex:-1
46455             });
46456             
46457              cmenu.menu.items.push({
46458                 actiontype : 'tidy',
46459                 html: 'Tidy HTML Source',
46460                 handler: function(a,b) {
46461                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
46462                     editorcore.syncValue();
46463                 },
46464                 tabIndex:-1
46465             });
46466             
46467             
46468             tb.add(cmenu);
46469         }
46470          
46471         if (!this.disable.specialElements) {
46472             var semenu = {
46473                 text: "Other;",
46474                 cls: 'x-edit-none',
46475                 menu : {
46476                     items : []
46477                 }
46478             };
46479             for (var i =0; i < this.specialElements.length; i++) {
46480                 semenu.menu.items.push(
46481                     Roo.apply({ 
46482                         handler: function(a,b) {
46483                             editor.insertAtCursor(this.ihtml);
46484                         }
46485                     }, this.specialElements[i])
46486                 );
46487                     
46488             }
46489             
46490             tb.add(semenu);
46491             
46492             
46493         }
46494          
46495         
46496         if (this.btns) {
46497             for(var i =0; i< this.btns.length;i++) {
46498                 var b = Roo.factory(this.btns[i],Roo.form);
46499                 b.cls =  'x-edit-none';
46500                 
46501                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
46502                     b.cls += ' x-init-enable';
46503                 }
46504                 
46505                 b.scope = editorcore;
46506                 tb.add(b);
46507             }
46508         
46509         }
46510         
46511         
46512         
46513         // disable everything...
46514         
46515         this.tb.items.each(function(item){
46516             
46517            if(
46518                 item.id != editorcore.frameId+ '-sourceedit' && 
46519                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
46520             ){
46521                 
46522                 item.disable();
46523             }
46524         });
46525         this.rendered = true;
46526         
46527         // the all the btns;
46528         editor.on('editorevent', this.updateToolbar, this);
46529         // other toolbars need to implement this..
46530         //editor.on('editmodechange', this.updateToolbar, this);
46531     },
46532     
46533     
46534     relayBtnCmd : function(btn) {
46535         this.editorcore.relayCmd(btn.cmd);
46536     },
46537     // private used internally
46538     createLink : function(){
46539         Roo.log("create link?");
46540         var url = prompt(this.createLinkText, this.defaultLinkValue);
46541         if(url && url != 'http:/'+'/'){
46542             this.editorcore.relayCmd('createlink', url);
46543         }
46544     },
46545
46546     
46547     /**
46548      * Protected method that will not generally be called directly. It triggers
46549      * a toolbar update by reading the markup state of the current selection in the editor.
46550      */
46551     updateToolbar: function(){
46552
46553         if(!this.editorcore.activated){
46554             this.editor.onFirstFocus();
46555             return;
46556         }
46557
46558         var btns = this.tb.items.map, 
46559             doc = this.editorcore.doc,
46560             frameId = this.editorcore.frameId;
46561
46562         if(!this.disable.font && !Roo.isSafari){
46563             /*
46564             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
46565             if(name != this.fontSelect.dom.value){
46566                 this.fontSelect.dom.value = name;
46567             }
46568             */
46569         }
46570         if(!this.disable.format){
46571             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
46572             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
46573             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
46574             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
46575         }
46576         if(!this.disable.alignments){
46577             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
46578             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
46579             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
46580         }
46581         if(!Roo.isSafari && !this.disable.lists){
46582             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
46583             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
46584         }
46585         
46586         var ans = this.editorcore.getAllAncestors();
46587         if (this.formatCombo) {
46588             
46589             
46590             var store = this.formatCombo.store;
46591             this.formatCombo.setValue("");
46592             for (var i =0; i < ans.length;i++) {
46593                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
46594                     // select it..
46595                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
46596                     break;
46597                 }
46598             }
46599         }
46600         
46601         
46602         
46603         // hides menus... - so this cant be on a menu...
46604         Roo.menu.MenuMgr.hideAll();
46605
46606         //this.editorsyncValue();
46607     },
46608    
46609     
46610     createFontOptions : function(){
46611         var buf = [], fs = this.fontFamilies, ff, lc;
46612         
46613         
46614         
46615         for(var i = 0, len = fs.length; i< len; i++){
46616             ff = fs[i];
46617             lc = ff.toLowerCase();
46618             buf.push(
46619                 '<option value="',lc,'" style="font-family:',ff,';"',
46620                     (this.defaultFont == lc ? ' selected="true">' : '>'),
46621                     ff,
46622                 '</option>'
46623             );
46624         }
46625         return buf.join('');
46626     },
46627     
46628     toggleSourceEdit : function(sourceEditMode){
46629         
46630         Roo.log("toolbar toogle");
46631         if(sourceEditMode === undefined){
46632             sourceEditMode = !this.sourceEditMode;
46633         }
46634         this.sourceEditMode = sourceEditMode === true;
46635         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
46636         // just toggle the button?
46637         if(btn.pressed !== this.sourceEditMode){
46638             btn.toggle(this.sourceEditMode);
46639             return;
46640         }
46641         
46642         if(sourceEditMode){
46643             Roo.log("disabling buttons");
46644             this.tb.items.each(function(item){
46645                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
46646                     item.disable();
46647                 }
46648             });
46649           
46650         }else{
46651             Roo.log("enabling buttons");
46652             if(this.editorcore.initialized){
46653                 this.tb.items.each(function(item){
46654                     item.enable();
46655                 });
46656             }
46657             
46658         }
46659         Roo.log("calling toggole on editor");
46660         // tell the editor that it's been pressed..
46661         this.editor.toggleSourceEdit(sourceEditMode);
46662        
46663     },
46664      /**
46665      * Object collection of toolbar tooltips for the buttons in the editor. The key
46666      * is the command id associated with that button and the value is a valid QuickTips object.
46667      * For example:
46668 <pre><code>
46669 {
46670     bold : {
46671         title: 'Bold (Ctrl+B)',
46672         text: 'Make the selected text bold.',
46673         cls: 'x-html-editor-tip'
46674     },
46675     italic : {
46676         title: 'Italic (Ctrl+I)',
46677         text: 'Make the selected text italic.',
46678         cls: 'x-html-editor-tip'
46679     },
46680     ...
46681 </code></pre>
46682     * @type Object
46683      */
46684     buttonTips : {
46685         bold : {
46686             title: 'Bold (Ctrl+B)',
46687             text: 'Make the selected text bold.',
46688             cls: 'x-html-editor-tip'
46689         },
46690         italic : {
46691             title: 'Italic (Ctrl+I)',
46692             text: 'Make the selected text italic.',
46693             cls: 'x-html-editor-tip'
46694         },
46695         underline : {
46696             title: 'Underline (Ctrl+U)',
46697             text: 'Underline the selected text.',
46698             cls: 'x-html-editor-tip'
46699         },
46700         strikethrough : {
46701             title: 'Strikethrough',
46702             text: 'Strikethrough the selected text.',
46703             cls: 'x-html-editor-tip'
46704         },
46705         increasefontsize : {
46706             title: 'Grow Text',
46707             text: 'Increase the font size.',
46708             cls: 'x-html-editor-tip'
46709         },
46710         decreasefontsize : {
46711             title: 'Shrink Text',
46712             text: 'Decrease the font size.',
46713             cls: 'x-html-editor-tip'
46714         },
46715         backcolor : {
46716             title: 'Text Highlight Color',
46717             text: 'Change the background color of the selected text.',
46718             cls: 'x-html-editor-tip'
46719         },
46720         forecolor : {
46721             title: 'Font Color',
46722             text: 'Change the color of the selected text.',
46723             cls: 'x-html-editor-tip'
46724         },
46725         justifyleft : {
46726             title: 'Align Text Left',
46727             text: 'Align text to the left.',
46728             cls: 'x-html-editor-tip'
46729         },
46730         justifycenter : {
46731             title: 'Center Text',
46732             text: 'Center text in the editor.',
46733             cls: 'x-html-editor-tip'
46734         },
46735         justifyright : {
46736             title: 'Align Text Right',
46737             text: 'Align text to the right.',
46738             cls: 'x-html-editor-tip'
46739         },
46740         insertunorderedlist : {
46741             title: 'Bullet List',
46742             text: 'Start a bulleted list.',
46743             cls: 'x-html-editor-tip'
46744         },
46745         insertorderedlist : {
46746             title: 'Numbered List',
46747             text: 'Start a numbered list.',
46748             cls: 'x-html-editor-tip'
46749         },
46750         createlink : {
46751             title: 'Hyperlink',
46752             text: 'Make the selected text a hyperlink.',
46753             cls: 'x-html-editor-tip'
46754         },
46755         sourceedit : {
46756             title: 'Source Edit',
46757             text: 'Switch to source editing mode.',
46758             cls: 'x-html-editor-tip'
46759         }
46760     },
46761     // private
46762     onDestroy : function(){
46763         if(this.rendered){
46764             
46765             this.tb.items.each(function(item){
46766                 if(item.menu){
46767                     item.menu.removeAll();
46768                     if(item.menu.el){
46769                         item.menu.el.destroy();
46770                     }
46771                 }
46772                 item.destroy();
46773             });
46774              
46775         }
46776     },
46777     onFirstFocus: function() {
46778         this.tb.items.each(function(item){
46779            item.enable();
46780         });
46781     }
46782 });
46783
46784
46785
46786
46787 // <script type="text/javascript">
46788 /*
46789  * Based on
46790  * Ext JS Library 1.1.1
46791  * Copyright(c) 2006-2007, Ext JS, LLC.
46792  *  
46793  
46794  */
46795
46796  
46797 /**
46798  * @class Roo.form.HtmlEditor.ToolbarContext
46799  * Context Toolbar
46800  * 
46801  * Usage:
46802  *
46803  new Roo.form.HtmlEditor({
46804     ....
46805     toolbars : [
46806         { xtype: 'ToolbarStandard', styles : {} }
46807         { xtype: 'ToolbarContext', disable : {} }
46808     ]
46809 })
46810
46811      
46812  * 
46813  * @config : {Object} disable List of elements to disable.. (not done yet.)
46814  * @config : {Object} styles  Map of styles available.
46815  * 
46816  */
46817
46818 Roo.form.HtmlEditor.ToolbarContext = function(config)
46819 {
46820     
46821     Roo.apply(this, config);
46822     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
46823     // dont call parent... till later.
46824     this.styles = this.styles || {};
46825 }
46826
46827  
46828
46829 Roo.form.HtmlEditor.ToolbarContext.types = {
46830     'IMG' : {
46831         width : {
46832             title: "Width",
46833             width: 40
46834         },
46835         height:  {
46836             title: "Height",
46837             width: 40
46838         },
46839         align: {
46840             title: "Align",
46841             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
46842             width : 80
46843             
46844         },
46845         border: {
46846             title: "Border",
46847             width: 40
46848         },
46849         alt: {
46850             title: "Alt",
46851             width: 120
46852         },
46853         src : {
46854             title: "Src",
46855             width: 220
46856         }
46857         
46858     },
46859     'A' : {
46860         name : {
46861             title: "Name",
46862             width: 50
46863         },
46864         target:  {
46865             title: "Target",
46866             width: 120
46867         },
46868         href:  {
46869             title: "Href",
46870             width: 220
46871         } // border?
46872         
46873     },
46874     'TABLE' : {
46875         rows : {
46876             title: "Rows",
46877             width: 20
46878         },
46879         cols : {
46880             title: "Cols",
46881             width: 20
46882         },
46883         width : {
46884             title: "Width",
46885             width: 40
46886         },
46887         height : {
46888             title: "Height",
46889             width: 40
46890         },
46891         border : {
46892             title: "Border",
46893             width: 20
46894         }
46895     },
46896     'TD' : {
46897         width : {
46898             title: "Width",
46899             width: 40
46900         },
46901         height : {
46902             title: "Height",
46903             width: 40
46904         },   
46905         align: {
46906             title: "Align",
46907             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
46908             width: 80
46909         },
46910         valign: {
46911             title: "Valign",
46912             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
46913             width: 80
46914         },
46915         colspan: {
46916             title: "Colspan",
46917             width: 20
46918             
46919         },
46920          'font-family'  : {
46921             title : "Font",
46922             style : 'fontFamily',
46923             displayField: 'display',
46924             optname : 'font-family',
46925             width: 140
46926         }
46927     },
46928     'INPUT' : {
46929         name : {
46930             title: "name",
46931             width: 120
46932         },
46933         value : {
46934             title: "Value",
46935             width: 120
46936         },
46937         width : {
46938             title: "Width",
46939             width: 40
46940         }
46941     },
46942     'LABEL' : {
46943         'for' : {
46944             title: "For",
46945             width: 120
46946         }
46947     },
46948     'TEXTAREA' : {
46949           name : {
46950             title: "name",
46951             width: 120
46952         },
46953         rows : {
46954             title: "Rows",
46955             width: 20
46956         },
46957         cols : {
46958             title: "Cols",
46959             width: 20
46960         }
46961     },
46962     'SELECT' : {
46963         name : {
46964             title: "name",
46965             width: 120
46966         },
46967         selectoptions : {
46968             title: "Options",
46969             width: 200
46970         }
46971     },
46972     
46973     // should we really allow this??
46974     // should this just be 
46975     'BODY' : {
46976         title : {
46977             title: "Title",
46978             width: 200,
46979             disabled : true
46980         }
46981     },
46982     'SPAN' : {
46983         'font-family'  : {
46984             title : "Font",
46985             style : 'fontFamily',
46986             displayField: 'display',
46987             optname : 'font-family',
46988             width: 140
46989         }
46990     },
46991     'DIV' : {
46992         'font-family'  : {
46993             title : "Font",
46994             style : 'fontFamily',
46995             displayField: 'display',
46996             optname : 'font-family',
46997             width: 140
46998         }
46999     },
47000      'P' : {
47001         'font-family'  : {
47002             title : "Font",
47003             style : 'fontFamily',
47004             displayField: 'display',
47005             optname : 'font-family',
47006             width: 140
47007         }
47008     },
47009     
47010     '*' : {
47011         // empty..
47012     }
47013
47014 };
47015
47016 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
47017 Roo.form.HtmlEditor.ToolbarContext.stores = false;
47018
47019 Roo.form.HtmlEditor.ToolbarContext.options = {
47020         'font-family'  : [ 
47021                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
47022                 [ 'Courier New', 'Courier New'],
47023                 [ 'Tahoma', 'Tahoma'],
47024                 [ 'Times New Roman,serif', 'Times'],
47025                 [ 'Verdana','Verdana' ]
47026         ]
47027 };
47028
47029 // fixme - these need to be configurable..
47030  
47031
47032 //Roo.form.HtmlEditor.ToolbarContext.types
47033
47034
47035 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
47036     
47037     tb: false,
47038     
47039     rendered: false,
47040     
47041     editor : false,
47042     editorcore : false,
47043     /**
47044      * @cfg {Object} disable  List of toolbar elements to disable
47045          
47046      */
47047     disable : false,
47048     /**
47049      * @cfg {Object} styles List of styles 
47050      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
47051      *
47052      * These must be defined in the page, so they get rendered correctly..
47053      * .headline { }
47054      * TD.underline { }
47055      * 
47056      */
47057     styles : false,
47058     
47059     options: false,
47060     
47061     toolbars : false,
47062     
47063     init : function(editor)
47064     {
47065         this.editor = editor;
47066         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47067         var editorcore = this.editorcore;
47068         
47069         var fid = editorcore.frameId;
47070         var etb = this;
47071         function btn(id, toggle, handler){
47072             var xid = fid + '-'+ id ;
47073             return {
47074                 id : xid,
47075                 cmd : id,
47076                 cls : 'x-btn-icon x-edit-'+id,
47077                 enableToggle:toggle !== false,
47078                 scope: editorcore, // was editor...
47079                 handler:handler||editorcore.relayBtnCmd,
47080                 clickEvent:'mousedown',
47081                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47082                 tabIndex:-1
47083             };
47084         }
47085         // create a new element.
47086         var wdiv = editor.wrap.createChild({
47087                 tag: 'div'
47088             }, editor.wrap.dom.firstChild.nextSibling, true);
47089         
47090         // can we do this more than once??
47091         
47092          // stop form submits
47093       
47094  
47095         // disable everything...
47096         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47097         this.toolbars = {};
47098            
47099         for (var i in  ty) {
47100           
47101             this.toolbars[i] = this.buildToolbar(ty[i],i);
47102         }
47103         this.tb = this.toolbars.BODY;
47104         this.tb.el.show();
47105         this.buildFooter();
47106         this.footer.show();
47107         editor.on('hide', function( ) { this.footer.hide() }, this);
47108         editor.on('show', function( ) { this.footer.show() }, this);
47109         
47110          
47111         this.rendered = true;
47112         
47113         // the all the btns;
47114         editor.on('editorevent', this.updateToolbar, this);
47115         // other toolbars need to implement this..
47116         //editor.on('editmodechange', this.updateToolbar, this);
47117     },
47118     
47119     
47120     
47121     /**
47122      * Protected method that will not generally be called directly. It triggers
47123      * a toolbar update by reading the markup state of the current selection in the editor.
47124      *
47125      * Note you can force an update by calling on('editorevent', scope, false)
47126      */
47127     updateToolbar: function(editor,ev,sel){
47128
47129         //Roo.log(ev);
47130         // capture mouse up - this is handy for selecting images..
47131         // perhaps should go somewhere else...
47132         if(!this.editorcore.activated){
47133              this.editor.onFirstFocus();
47134             return;
47135         }
47136         
47137         
47138         
47139         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
47140         // selectNode - might want to handle IE?
47141         if (ev &&
47142             (ev.type == 'mouseup' || ev.type == 'click' ) &&
47143             ev.target && ev.target.tagName == 'IMG') {
47144             // they have click on an image...
47145             // let's see if we can change the selection...
47146             sel = ev.target;
47147          
47148               var nodeRange = sel.ownerDocument.createRange();
47149             try {
47150                 nodeRange.selectNode(sel);
47151             } catch (e) {
47152                 nodeRange.selectNodeContents(sel);
47153             }
47154             //nodeRange.collapse(true);
47155             var s = this.editorcore.win.getSelection();
47156             s.removeAllRanges();
47157             s.addRange(nodeRange);
47158         }  
47159         
47160       
47161         var updateFooter = sel ? false : true;
47162         
47163         
47164         var ans = this.editorcore.getAllAncestors();
47165         
47166         // pick
47167         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
47168         
47169         if (!sel) { 
47170             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
47171             sel = sel ? sel : this.editorcore.doc.body;
47172             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
47173             
47174         }
47175         // pick a menu that exists..
47176         var tn = sel.tagName.toUpperCase();
47177         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
47178         
47179         tn = sel.tagName.toUpperCase();
47180         
47181         var lastSel = this.tb.selectedNode;
47182         
47183         this.tb.selectedNode = sel;
47184         
47185         // if current menu does not match..
47186         
47187         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
47188                 
47189             this.tb.el.hide();
47190             ///console.log("show: " + tn);
47191             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
47192             this.tb.el.show();
47193             // update name
47194             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
47195             
47196             
47197             // update attributes
47198             if (this.tb.fields) {
47199                 this.tb.fields.each(function(e) {
47200                     if (e.stylename) {
47201                         e.setValue(sel.style[e.stylename]);
47202                         return;
47203                     } 
47204                    e.setValue(sel.getAttribute(e.attrname));
47205                 });
47206             }
47207             
47208             var hasStyles = false;
47209             for(var i in this.styles) {
47210                 hasStyles = true;
47211                 break;
47212             }
47213             
47214             // update styles
47215             if (hasStyles) { 
47216                 var st = this.tb.fields.item(0);
47217                 
47218                 st.store.removeAll();
47219                
47220                 
47221                 var cn = sel.className.split(/\s+/);
47222                 
47223                 var avs = [];
47224                 if (this.styles['*']) {
47225                     
47226                     Roo.each(this.styles['*'], function(v) {
47227                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47228                     });
47229                 }
47230                 if (this.styles[tn]) { 
47231                     Roo.each(this.styles[tn], function(v) {
47232                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
47233                     });
47234                 }
47235                 
47236                 st.store.loadData(avs);
47237                 st.collapse();
47238                 st.setValue(cn);
47239             }
47240             // flag our selected Node.
47241             this.tb.selectedNode = sel;
47242            
47243            
47244             Roo.menu.MenuMgr.hideAll();
47245
47246         }
47247         
47248         if (!updateFooter) {
47249             //this.footDisp.dom.innerHTML = ''; 
47250             return;
47251         }
47252         // update the footer
47253         //
47254         var html = '';
47255         
47256         this.footerEls = ans.reverse();
47257         Roo.each(this.footerEls, function(a,i) {
47258             if (!a) { return; }
47259             html += html.length ? ' &gt; '  :  '';
47260             
47261             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
47262             
47263         });
47264        
47265         // 
47266         var sz = this.footDisp.up('td').getSize();
47267         this.footDisp.dom.style.width = (sz.width -10) + 'px';
47268         this.footDisp.dom.style.marginLeft = '5px';
47269         
47270         this.footDisp.dom.style.overflow = 'hidden';
47271         
47272         this.footDisp.dom.innerHTML = html;
47273             
47274         //this.editorsyncValue();
47275     },
47276      
47277     
47278    
47279        
47280     // private
47281     onDestroy : function(){
47282         if(this.rendered){
47283             
47284             this.tb.items.each(function(item){
47285                 if(item.menu){
47286                     item.menu.removeAll();
47287                     if(item.menu.el){
47288                         item.menu.el.destroy();
47289                     }
47290                 }
47291                 item.destroy();
47292             });
47293              
47294         }
47295     },
47296     onFirstFocus: function() {
47297         // need to do this for all the toolbars..
47298         this.tb.items.each(function(item){
47299            item.enable();
47300         });
47301     },
47302     buildToolbar: function(tlist, nm)
47303     {
47304         var editor = this.editor;
47305         var editorcore = this.editorcore;
47306          // create a new element.
47307         var wdiv = editor.wrap.createChild({
47308                 tag: 'div'
47309             }, editor.wrap.dom.firstChild.nextSibling, true);
47310         
47311        
47312         var tb = new Roo.Toolbar(wdiv);
47313         // add the name..
47314         
47315         tb.add(nm+ ":&nbsp;");
47316         
47317         var styles = [];
47318         for(var i in this.styles) {
47319             styles.push(i);
47320         }
47321         
47322         // styles...
47323         if (styles && styles.length) {
47324             
47325             // this needs a multi-select checkbox...
47326             tb.addField( new Roo.form.ComboBox({
47327                 store: new Roo.data.SimpleStore({
47328                     id : 'val',
47329                     fields: ['val', 'selected'],
47330                     data : [] 
47331                 }),
47332                 name : '-roo-edit-className',
47333                 attrname : 'className',
47334                 displayField: 'val',
47335                 typeAhead: false,
47336                 mode: 'local',
47337                 editable : false,
47338                 triggerAction: 'all',
47339                 emptyText:'Select Style',
47340                 selectOnFocus:true,
47341                 width: 130,
47342                 listeners : {
47343                     'select': function(c, r, i) {
47344                         // initial support only for on class per el..
47345                         tb.selectedNode.className =  r ? r.get('val') : '';
47346                         editorcore.syncValue();
47347                     }
47348                 }
47349     
47350             }));
47351         }
47352         
47353         var tbc = Roo.form.HtmlEditor.ToolbarContext;
47354         var tbops = tbc.options;
47355         
47356         for (var i in tlist) {
47357             
47358             var item = tlist[i];
47359             tb.add(item.title + ":&nbsp;");
47360             
47361             
47362             //optname == used so you can configure the options available..
47363             var opts = item.opts ? item.opts : false;
47364             if (item.optname) {
47365                 opts = tbops[item.optname];
47366            
47367             }
47368             
47369             if (opts) {
47370                 // opts == pulldown..
47371                 tb.addField( new Roo.form.ComboBox({
47372                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
47373                         id : 'val',
47374                         fields: ['val', 'display'],
47375                         data : opts  
47376                     }),
47377                     name : '-roo-edit-' + i,
47378                     attrname : i,
47379                     stylename : item.style ? item.style : false,
47380                     displayField: item.displayField ? item.displayField : 'val',
47381                     valueField :  'val',
47382                     typeAhead: false,
47383                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
47384                     editable : false,
47385                     triggerAction: 'all',
47386                     emptyText:'Select',
47387                     selectOnFocus:true,
47388                     width: item.width ? item.width  : 130,
47389                     listeners : {
47390                         'select': function(c, r, i) {
47391                             if (c.stylename) {
47392                                 tb.selectedNode.style[c.stylename] =  r.get('val');
47393                                 return;
47394                             }
47395                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
47396                         }
47397                     }
47398
47399                 }));
47400                 continue;
47401                     
47402                  
47403                 
47404                 tb.addField( new Roo.form.TextField({
47405                     name: i,
47406                     width: 100,
47407                     //allowBlank:false,
47408                     value: ''
47409                 }));
47410                 continue;
47411             }
47412             tb.addField( new Roo.form.TextField({
47413                 name: '-roo-edit-' + i,
47414                 attrname : i,
47415                 
47416                 width: item.width,
47417                 //allowBlank:true,
47418                 value: '',
47419                 listeners: {
47420                     'change' : function(f, nv, ov) {
47421                         tb.selectedNode.setAttribute(f.attrname, nv);
47422                         editorcore.syncValue();
47423                     }
47424                 }
47425             }));
47426              
47427         }
47428         
47429         var _this = this;
47430         
47431         if(nm == 'BODY'){
47432             tb.addSeparator();
47433         
47434             tb.addButton( {
47435                 text: 'Stylesheets',
47436
47437                 listeners : {
47438                     click : function ()
47439                     {
47440                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
47441                     }
47442                 }
47443             });
47444         }
47445         
47446         tb.addFill();
47447         tb.addButton( {
47448             text: 'Remove Tag',
47449     
47450             listeners : {
47451                 click : function ()
47452                 {
47453                     // remove
47454                     // undo does not work.
47455                      
47456                     var sn = tb.selectedNode;
47457                     
47458                     var pn = sn.parentNode;
47459                     
47460                     var stn =  sn.childNodes[0];
47461                     var en = sn.childNodes[sn.childNodes.length - 1 ];
47462                     while (sn.childNodes.length) {
47463                         var node = sn.childNodes[0];
47464                         sn.removeChild(node);
47465                         //Roo.log(node);
47466                         pn.insertBefore(node, sn);
47467                         
47468                     }
47469                     pn.removeChild(sn);
47470                     var range = editorcore.createRange();
47471         
47472                     range.setStart(stn,0);
47473                     range.setEnd(en,0); //????
47474                     //range.selectNode(sel);
47475                     
47476                     
47477                     var selection = editorcore.getSelection();
47478                     selection.removeAllRanges();
47479                     selection.addRange(range);
47480                     
47481                     
47482                     
47483                     //_this.updateToolbar(null, null, pn);
47484                     _this.updateToolbar(null, null, null);
47485                     _this.footDisp.dom.innerHTML = ''; 
47486                 }
47487             }
47488             
47489                     
47490                 
47491             
47492         });
47493         
47494         
47495         tb.el.on('click', function(e){
47496             e.preventDefault(); // what does this do?
47497         });
47498         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
47499         tb.el.hide();
47500         tb.name = nm;
47501         // dont need to disable them... as they will get hidden
47502         return tb;
47503          
47504         
47505     },
47506     buildFooter : function()
47507     {
47508         
47509         var fel = this.editor.wrap.createChild();
47510         this.footer = new Roo.Toolbar(fel);
47511         // toolbar has scrolly on left / right?
47512         var footDisp= new Roo.Toolbar.Fill();
47513         var _t = this;
47514         this.footer.add(
47515             {
47516                 text : '&lt;',
47517                 xtype: 'Button',
47518                 handler : function() {
47519                     _t.footDisp.scrollTo('left',0,true)
47520                 }
47521             }
47522         );
47523         this.footer.add( footDisp );
47524         this.footer.add( 
47525             {
47526                 text : '&gt;',
47527                 xtype: 'Button',
47528                 handler : function() {
47529                     // no animation..
47530                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
47531                 }
47532             }
47533         );
47534         var fel = Roo.get(footDisp.el);
47535         fel.addClass('x-editor-context');
47536         this.footDispWrap = fel; 
47537         this.footDispWrap.overflow  = 'hidden';
47538         
47539         this.footDisp = fel.createChild();
47540         this.footDispWrap.on('click', this.onContextClick, this)
47541         
47542         
47543     },
47544     onContextClick : function (ev,dom)
47545     {
47546         ev.preventDefault();
47547         var  cn = dom.className;
47548         //Roo.log(cn);
47549         if (!cn.match(/x-ed-loc-/)) {
47550             return;
47551         }
47552         var n = cn.split('-').pop();
47553         var ans = this.footerEls;
47554         var sel = ans[n];
47555         
47556          // pick
47557         var range = this.editorcore.createRange();
47558         
47559         range.selectNodeContents(sel);
47560         //range.selectNode(sel);
47561         
47562         
47563         var selection = this.editorcore.getSelection();
47564         selection.removeAllRanges();
47565         selection.addRange(range);
47566         
47567         
47568         
47569         this.updateToolbar(null, null, sel);
47570         
47571         
47572     }
47573     
47574     
47575     
47576     
47577     
47578 });
47579
47580
47581
47582
47583
47584 /*
47585  * Based on:
47586  * Ext JS Library 1.1.1
47587  * Copyright(c) 2006-2007, Ext JS, LLC.
47588  *
47589  * Originally Released Under LGPL - original licence link has changed is not relivant.
47590  *
47591  * Fork - LGPL
47592  * <script type="text/javascript">
47593  */
47594  
47595 /**
47596  * @class Roo.form.BasicForm
47597  * @extends Roo.util.Observable
47598  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
47599  * @constructor
47600  * @param {String/HTMLElement/Roo.Element} el The form element or its id
47601  * @param {Object} config Configuration options
47602  */
47603 Roo.form.BasicForm = function(el, config){
47604     this.allItems = [];
47605     this.childForms = [];
47606     Roo.apply(this, config);
47607     /*
47608      * The Roo.form.Field items in this form.
47609      * @type MixedCollection
47610      */
47611      
47612      
47613     this.items = new Roo.util.MixedCollection(false, function(o){
47614         return o.id || (o.id = Roo.id());
47615     });
47616     this.addEvents({
47617         /**
47618          * @event beforeaction
47619          * Fires before any action is performed. Return false to cancel the action.
47620          * @param {Form} this
47621          * @param {Action} action The action to be performed
47622          */
47623         beforeaction: true,
47624         /**
47625          * @event actionfailed
47626          * Fires when an action fails.
47627          * @param {Form} this
47628          * @param {Action} action The action that failed
47629          */
47630         actionfailed : true,
47631         /**
47632          * @event actioncomplete
47633          * Fires when an action is completed.
47634          * @param {Form} this
47635          * @param {Action} action The action that completed
47636          */
47637         actioncomplete : true
47638     });
47639     if(el){
47640         this.initEl(el);
47641     }
47642     Roo.form.BasicForm.superclass.constructor.call(this);
47643     
47644     Roo.form.BasicForm.popover.apply();
47645 };
47646
47647 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
47648     /**
47649      * @cfg {String} method
47650      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
47651      */
47652     /**
47653      * @cfg {DataReader} reader
47654      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
47655      * This is optional as there is built-in support for processing JSON.
47656      */
47657     /**
47658      * @cfg {DataReader} errorReader
47659      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
47660      * This is completely optional as there is built-in support for processing JSON.
47661      */
47662     /**
47663      * @cfg {String} url
47664      * The URL to use for form actions if one isn't supplied in the action options.
47665      */
47666     /**
47667      * @cfg {Boolean} fileUpload
47668      * Set to true if this form is a file upload.
47669      */
47670      
47671     /**
47672      * @cfg {Object} baseParams
47673      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
47674      */
47675      /**
47676      
47677     /**
47678      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
47679      */
47680     timeout: 30,
47681
47682     // private
47683     activeAction : null,
47684
47685     /**
47686      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
47687      * or setValues() data instead of when the form was first created.
47688      */
47689     trackResetOnLoad : false,
47690     
47691     
47692     /**
47693      * childForms - used for multi-tab forms
47694      * @type {Array}
47695      */
47696     childForms : false,
47697     
47698     /**
47699      * allItems - full list of fields.
47700      * @type {Array}
47701      */
47702     allItems : false,
47703     
47704     /**
47705      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
47706      * element by passing it or its id or mask the form itself by passing in true.
47707      * @type Mixed
47708      */
47709     waitMsgTarget : false,
47710     
47711     /**
47712      * @type Boolean
47713      */
47714     disableMask : false,
47715     
47716     /**
47717      * @cfg {Boolean} errorMask (true|false) default false
47718      */
47719     errorMask : false,
47720     
47721     /**
47722      * @cfg {Number} maskOffset Default 100
47723      */
47724     maskOffset : 100,
47725
47726     // private
47727     initEl : function(el){
47728         this.el = Roo.get(el);
47729         this.id = this.el.id || Roo.id();
47730         this.el.on('submit', this.onSubmit, this);
47731         this.el.addClass('x-form');
47732     },
47733
47734     // private
47735     onSubmit : function(e){
47736         e.stopEvent();
47737     },
47738
47739     /**
47740      * Returns true if client-side validation on the form is successful.
47741      * @return Boolean
47742      */
47743     isValid : function(){
47744         var valid = true;
47745         var target = false;
47746         this.items.each(function(f){
47747             if(f.validate()){
47748                 return;
47749             }
47750             
47751             valid = false;
47752                 
47753             if(!target && f.el.isVisible(true)){
47754                 target = f;
47755             }
47756         });
47757         
47758         if(this.errorMask && !valid){
47759             Roo.form.BasicForm.popover.mask(this, target);
47760         }
47761         
47762         return valid;
47763     },
47764     /**
47765      * Returns array of invalid form fields.
47766      * @return Array
47767      */
47768     
47769     invalidFields : function()
47770     {
47771         var ret = [];
47772         this.items.each(function(f){
47773             if(f.validate()){
47774                 return;
47775             }
47776             ret.push(f);
47777             
47778         });
47779         
47780         return ret;
47781     },
47782     
47783     
47784     /**
47785      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
47786      * @return Boolean
47787      */
47788     isDirty : function(){
47789         var dirty = false;
47790         this.items.each(function(f){
47791            if(f.isDirty()){
47792                dirty = true;
47793                return false;
47794            }
47795         });
47796         return dirty;
47797     },
47798     
47799     /**
47800      * Returns true if any fields in this form have changed since their original load. (New version)
47801      * @return Boolean
47802      */
47803     
47804     hasChanged : function()
47805     {
47806         var dirty = false;
47807         this.items.each(function(f){
47808            if(f.hasChanged()){
47809                dirty = true;
47810                return false;
47811            }
47812         });
47813         return dirty;
47814         
47815     },
47816     /**
47817      * Resets all hasChanged to 'false' -
47818      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
47819      * So hasChanged storage is only to be used for this purpose
47820      * @return Boolean
47821      */
47822     resetHasChanged : function()
47823     {
47824         this.items.each(function(f){
47825            f.resetHasChanged();
47826         });
47827         
47828     },
47829     
47830     
47831     /**
47832      * Performs a predefined action (submit or load) or custom actions you define on this form.
47833      * @param {String} actionName The name of the action type
47834      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
47835      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
47836      * accept other config options):
47837      * <pre>
47838 Property          Type             Description
47839 ----------------  ---------------  ----------------------------------------------------------------------------------
47840 url               String           The url for the action (defaults to the form's url)
47841 method            String           The form method to use (defaults to the form's method, or POST if not defined)
47842 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
47843 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
47844                                    validate the form on the client (defaults to false)
47845      * </pre>
47846      * @return {BasicForm} this
47847      */
47848     doAction : function(action, options){
47849         if(typeof action == 'string'){
47850             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
47851         }
47852         if(this.fireEvent('beforeaction', this, action) !== false){
47853             this.beforeAction(action);
47854             action.run.defer(100, action);
47855         }
47856         return this;
47857     },
47858
47859     /**
47860      * Shortcut to do a submit action.
47861      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47862      * @return {BasicForm} this
47863      */
47864     submit : function(options){
47865         this.doAction('submit', options);
47866         return this;
47867     },
47868
47869     /**
47870      * Shortcut to do a load action.
47871      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
47872      * @return {BasicForm} this
47873      */
47874     load : function(options){
47875         this.doAction('load', options);
47876         return this;
47877     },
47878
47879     /**
47880      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
47881      * @param {Record} record The record to edit
47882      * @return {BasicForm} this
47883      */
47884     updateRecord : function(record){
47885         record.beginEdit();
47886         var fs = record.fields;
47887         fs.each(function(f){
47888             var field = this.findField(f.name);
47889             if(field){
47890                 record.set(f.name, field.getValue());
47891             }
47892         }, this);
47893         record.endEdit();
47894         return this;
47895     },
47896
47897     /**
47898      * Loads an Roo.data.Record into this form.
47899      * @param {Record} record The record to load
47900      * @return {BasicForm} this
47901      */
47902     loadRecord : function(record){
47903         this.setValues(record.data);
47904         return this;
47905     },
47906
47907     // private
47908     beforeAction : function(action){
47909         var o = action.options;
47910         
47911         if(!this.disableMask) {
47912             if(this.waitMsgTarget === true){
47913                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
47914             }else if(this.waitMsgTarget){
47915                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
47916                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
47917             }else {
47918                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
47919             }
47920         }
47921         
47922          
47923     },
47924
47925     // private
47926     afterAction : function(action, success){
47927         this.activeAction = null;
47928         var o = action.options;
47929         
47930         if(!this.disableMask) {
47931             if(this.waitMsgTarget === true){
47932                 this.el.unmask();
47933             }else if(this.waitMsgTarget){
47934                 this.waitMsgTarget.unmask();
47935             }else{
47936                 Roo.MessageBox.updateProgress(1);
47937                 Roo.MessageBox.hide();
47938             }
47939         }
47940         
47941         if(success){
47942             if(o.reset){
47943                 this.reset();
47944             }
47945             Roo.callback(o.success, o.scope, [this, action]);
47946             this.fireEvent('actioncomplete', this, action);
47947             
47948         }else{
47949             
47950             // failure condition..
47951             // we have a scenario where updates need confirming.
47952             // eg. if a locking scenario exists..
47953             // we look for { errors : { needs_confirm : true }} in the response.
47954             if (
47955                 (typeof(action.result) != 'undefined')  &&
47956                 (typeof(action.result.errors) != 'undefined')  &&
47957                 (typeof(action.result.errors.needs_confirm) != 'undefined')
47958            ){
47959                 var _t = this;
47960                 Roo.MessageBox.confirm(
47961                     "Change requires confirmation",
47962                     action.result.errorMsg,
47963                     function(r) {
47964                         if (r != 'yes') {
47965                             return;
47966                         }
47967                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
47968                     }
47969                     
47970                 );
47971                 
47972                 
47973                 
47974                 return;
47975             }
47976             
47977             Roo.callback(o.failure, o.scope, [this, action]);
47978             // show an error message if no failed handler is set..
47979             if (!this.hasListener('actionfailed')) {
47980                 Roo.MessageBox.alert("Error",
47981                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
47982                         action.result.errorMsg :
47983                         "Saving Failed, please check your entries or try again"
47984                 );
47985             }
47986             
47987             this.fireEvent('actionfailed', this, action);
47988         }
47989         
47990     },
47991
47992     /**
47993      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
47994      * @param {String} id The value to search for
47995      * @return Field
47996      */
47997     findField : function(id){
47998         var field = this.items.get(id);
47999         if(!field){
48000             this.items.each(function(f){
48001                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
48002                     field = f;
48003                     return false;
48004                 }
48005             });
48006         }
48007         return field || null;
48008     },
48009
48010     /**
48011      * Add a secondary form to this one, 
48012      * Used to provide tabbed forms. One form is primary, with hidden values 
48013      * which mirror the elements from the other forms.
48014      * 
48015      * @param {Roo.form.Form} form to add.
48016      * 
48017      */
48018     addForm : function(form)
48019     {
48020        
48021         if (this.childForms.indexOf(form) > -1) {
48022             // already added..
48023             return;
48024         }
48025         this.childForms.push(form);
48026         var n = '';
48027         Roo.each(form.allItems, function (fe) {
48028             
48029             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
48030             if (this.findField(n)) { // already added..
48031                 return;
48032             }
48033             var add = new Roo.form.Hidden({
48034                 name : n
48035             });
48036             add.render(this.el);
48037             
48038             this.add( add );
48039         }, this);
48040         
48041     },
48042     /**
48043      * Mark fields in this form invalid in bulk.
48044      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
48045      * @return {BasicForm} this
48046      */
48047     markInvalid : function(errors){
48048         if(errors instanceof Array){
48049             for(var i = 0, len = errors.length; i < len; i++){
48050                 var fieldError = errors[i];
48051                 var f = this.findField(fieldError.id);
48052                 if(f){
48053                     f.markInvalid(fieldError.msg);
48054                 }
48055             }
48056         }else{
48057             var field, id;
48058             for(id in errors){
48059                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
48060                     field.markInvalid(errors[id]);
48061                 }
48062             }
48063         }
48064         Roo.each(this.childForms || [], function (f) {
48065             f.markInvalid(errors);
48066         });
48067         
48068         return this;
48069     },
48070
48071     /**
48072      * Set values for fields in this form in bulk.
48073      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
48074      * @return {BasicForm} this
48075      */
48076     setValues : function(values){
48077         if(values instanceof Array){ // array of objects
48078             for(var i = 0, len = values.length; i < len; i++){
48079                 var v = values[i];
48080                 var f = this.findField(v.id);
48081                 if(f){
48082                     f.setValue(v.value);
48083                     if(this.trackResetOnLoad){
48084                         f.originalValue = f.getValue();
48085                     }
48086                 }
48087             }
48088         }else{ // object hash
48089             var field, id;
48090             for(id in values){
48091                 if(typeof values[id] != 'function' && (field = this.findField(id))){
48092                     
48093                     if (field.setFromData && 
48094                         field.valueField && 
48095                         field.displayField &&
48096                         // combos' with local stores can 
48097                         // be queried via setValue()
48098                         // to set their value..
48099                         (field.store && !field.store.isLocal)
48100                         ) {
48101                         // it's a combo
48102                         var sd = { };
48103                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
48104                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
48105                         field.setFromData(sd);
48106                         
48107                     } else {
48108                         field.setValue(values[id]);
48109                     }
48110                     
48111                     
48112                     if(this.trackResetOnLoad){
48113                         field.originalValue = field.getValue();
48114                     }
48115                 }
48116             }
48117         }
48118         this.resetHasChanged();
48119         
48120         
48121         Roo.each(this.childForms || [], function (f) {
48122             f.setValues(values);
48123             f.resetHasChanged();
48124         });
48125                 
48126         return this;
48127     },
48128  
48129     /**
48130      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
48131      * they are returned as an array.
48132      * @param {Boolean} asString
48133      * @return {Object}
48134      */
48135     getValues : function(asString){
48136         if (this.childForms) {
48137             // copy values from the child forms
48138             Roo.each(this.childForms, function (f) {
48139                 this.setValues(f.getValues());
48140             }, this);
48141         }
48142         
48143         // use formdata
48144         if (typeof(FormData) != 'undefined' && asString !== true) {
48145             // this relies on a 'recent' version of chrome apparently...
48146             try {
48147                 var fd = (new FormData(this.el.dom)).entries();
48148                 var ret = {};
48149                 var ent = fd.next();
48150                 while (!ent.done) {
48151                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
48152                     ent = fd.next();
48153                 };
48154                 return ret;
48155             } catch(e) {
48156                 
48157             }
48158             
48159         }
48160         
48161         
48162         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
48163         if(asString === true){
48164             return fs;
48165         }
48166         return Roo.urlDecode(fs);
48167     },
48168     
48169     /**
48170      * Returns the fields in this form as an object with key/value pairs. 
48171      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
48172      * @return {Object}
48173      */
48174     getFieldValues : function(with_hidden)
48175     {
48176         if (this.childForms) {
48177             // copy values from the child forms
48178             // should this call getFieldValues - probably not as we do not currently copy
48179             // hidden fields when we generate..
48180             Roo.each(this.childForms, function (f) {
48181                 this.setValues(f.getValues());
48182             }, this);
48183         }
48184         
48185         var ret = {};
48186         this.items.each(function(f){
48187             if (!f.getName()) {
48188                 return;
48189             }
48190             var v = f.getValue();
48191             if (f.inputType =='radio') {
48192                 if (typeof(ret[f.getName()]) == 'undefined') {
48193                     ret[f.getName()] = ''; // empty..
48194                 }
48195                 
48196                 if (!f.el.dom.checked) {
48197                     return;
48198                     
48199                 }
48200                 v = f.el.dom.value;
48201                 
48202             }
48203             
48204             // not sure if this supported any more..
48205             if ((typeof(v) == 'object') && f.getRawValue) {
48206                 v = f.getRawValue() ; // dates..
48207             }
48208             // combo boxes where name != hiddenName...
48209             if (f.name != f.getName()) {
48210                 ret[f.name] = f.getRawValue();
48211             }
48212             ret[f.getName()] = v;
48213         });
48214         
48215         return ret;
48216     },
48217
48218     /**
48219      * Clears all invalid messages in this form.
48220      * @return {BasicForm} this
48221      */
48222     clearInvalid : function(){
48223         this.items.each(function(f){
48224            f.clearInvalid();
48225         });
48226         
48227         Roo.each(this.childForms || [], function (f) {
48228             f.clearInvalid();
48229         });
48230         
48231         
48232         return this;
48233     },
48234
48235     /**
48236      * Resets this form.
48237      * @return {BasicForm} this
48238      */
48239     reset : function(){
48240         this.items.each(function(f){
48241             f.reset();
48242         });
48243         
48244         Roo.each(this.childForms || [], function (f) {
48245             f.reset();
48246         });
48247         this.resetHasChanged();
48248         
48249         return this;
48250     },
48251
48252     /**
48253      * Add Roo.form components to this form.
48254      * @param {Field} field1
48255      * @param {Field} field2 (optional)
48256      * @param {Field} etc (optional)
48257      * @return {BasicForm} this
48258      */
48259     add : function(){
48260         this.items.addAll(Array.prototype.slice.call(arguments, 0));
48261         return this;
48262     },
48263
48264
48265     /**
48266      * Removes a field from the items collection (does NOT remove its markup).
48267      * @param {Field} field
48268      * @return {BasicForm} this
48269      */
48270     remove : function(field){
48271         this.items.remove(field);
48272         return this;
48273     },
48274
48275     /**
48276      * Looks at the fields in this form, checks them for an id attribute,
48277      * and calls applyTo on the existing dom element with that id.
48278      * @return {BasicForm} this
48279      */
48280     render : function(){
48281         this.items.each(function(f){
48282             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
48283                 f.applyTo(f.id);
48284             }
48285         });
48286         return this;
48287     },
48288
48289     /**
48290      * Calls {@link Ext#apply} for all fields in this form with the passed object.
48291      * @param {Object} values
48292      * @return {BasicForm} this
48293      */
48294     applyToFields : function(o){
48295         this.items.each(function(f){
48296            Roo.apply(f, o);
48297         });
48298         return this;
48299     },
48300
48301     /**
48302      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
48303      * @param {Object} values
48304      * @return {BasicForm} this
48305      */
48306     applyIfToFields : function(o){
48307         this.items.each(function(f){
48308            Roo.applyIf(f, o);
48309         });
48310         return this;
48311     }
48312 });
48313
48314 // back compat
48315 Roo.BasicForm = Roo.form.BasicForm;
48316
48317 Roo.apply(Roo.form.BasicForm, {
48318     
48319     popover : {
48320         
48321         padding : 5,
48322         
48323         isApplied : false,
48324         
48325         isMasked : false,
48326         
48327         form : false,
48328         
48329         target : false,
48330         
48331         intervalID : false,
48332         
48333         maskEl : false,
48334         
48335         apply : function()
48336         {
48337             if(this.isApplied){
48338                 return;
48339             }
48340             
48341             this.maskEl = {
48342                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
48343                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
48344                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
48345                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
48346             };
48347             
48348             this.maskEl.top.enableDisplayMode("block");
48349             this.maskEl.left.enableDisplayMode("block");
48350             this.maskEl.bottom.enableDisplayMode("block");
48351             this.maskEl.right.enableDisplayMode("block");
48352             
48353             Roo.get(document.body).on('click', function(){
48354                 this.unmask();
48355             }, this);
48356             
48357             Roo.get(document.body).on('touchstart', function(){
48358                 this.unmask();
48359             }, this);
48360             
48361             this.isApplied = true
48362         },
48363         
48364         mask : function(form, target)
48365         {
48366             this.form = form;
48367             
48368             this.target = target;
48369             
48370             if(!this.form.errorMask || !target.el){
48371                 return;
48372             }
48373             
48374             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
48375             
48376             var ot = this.target.el.calcOffsetsTo(scrollable);
48377             
48378             var scrollTo = ot[1] - this.form.maskOffset;
48379             
48380             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
48381             
48382             scrollable.scrollTo('top', scrollTo);
48383             
48384             var el = this.target.wrap || this.target.el;
48385             
48386             var box = el.getBox();
48387             
48388             this.maskEl.top.setStyle('position', 'absolute');
48389             this.maskEl.top.setStyle('z-index', 10000);
48390             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
48391             this.maskEl.top.setLeft(0);
48392             this.maskEl.top.setTop(0);
48393             this.maskEl.top.show();
48394             
48395             this.maskEl.left.setStyle('position', 'absolute');
48396             this.maskEl.left.setStyle('z-index', 10000);
48397             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
48398             this.maskEl.left.setLeft(0);
48399             this.maskEl.left.setTop(box.y - this.padding);
48400             this.maskEl.left.show();
48401
48402             this.maskEl.bottom.setStyle('position', 'absolute');
48403             this.maskEl.bottom.setStyle('z-index', 10000);
48404             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
48405             this.maskEl.bottom.setLeft(0);
48406             this.maskEl.bottom.setTop(box.bottom + this.padding);
48407             this.maskEl.bottom.show();
48408
48409             this.maskEl.right.setStyle('position', 'absolute');
48410             this.maskEl.right.setStyle('z-index', 10000);
48411             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
48412             this.maskEl.right.setLeft(box.right + this.padding);
48413             this.maskEl.right.setTop(box.y - this.padding);
48414             this.maskEl.right.show();
48415
48416             this.intervalID = window.setInterval(function() {
48417                 Roo.form.BasicForm.popover.unmask();
48418             }, 10000);
48419
48420             window.onwheel = function(){ return false;};
48421             
48422             (function(){ this.isMasked = true; }).defer(500, this);
48423             
48424         },
48425         
48426         unmask : function()
48427         {
48428             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
48429                 return;
48430             }
48431             
48432             this.maskEl.top.setStyle('position', 'absolute');
48433             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
48434             this.maskEl.top.hide();
48435
48436             this.maskEl.left.setStyle('position', 'absolute');
48437             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
48438             this.maskEl.left.hide();
48439
48440             this.maskEl.bottom.setStyle('position', 'absolute');
48441             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
48442             this.maskEl.bottom.hide();
48443
48444             this.maskEl.right.setStyle('position', 'absolute');
48445             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
48446             this.maskEl.right.hide();
48447             
48448             window.onwheel = function(){ return true;};
48449             
48450             if(this.intervalID){
48451                 window.clearInterval(this.intervalID);
48452                 this.intervalID = false;
48453             }
48454             
48455             this.isMasked = false;
48456             
48457         }
48458         
48459     }
48460     
48461 });/*
48462  * Based on:
48463  * Ext JS Library 1.1.1
48464  * Copyright(c) 2006-2007, Ext JS, LLC.
48465  *
48466  * Originally Released Under LGPL - original licence link has changed is not relivant.
48467  *
48468  * Fork - LGPL
48469  * <script type="text/javascript">
48470  */
48471
48472 /**
48473  * @class Roo.form.Form
48474  * @extends Roo.form.BasicForm
48475  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
48476  * @constructor
48477  * @param {Object} config Configuration options
48478  */
48479 Roo.form.Form = function(config){
48480     var xitems =  [];
48481     if (config.items) {
48482         xitems = config.items;
48483         delete config.items;
48484     }
48485    
48486     
48487     Roo.form.Form.superclass.constructor.call(this, null, config);
48488     this.url = this.url || this.action;
48489     if(!this.root){
48490         this.root = new Roo.form.Layout(Roo.applyIf({
48491             id: Roo.id()
48492         }, config));
48493     }
48494     this.active = this.root;
48495     /**
48496      * Array of all the buttons that have been added to this form via {@link addButton}
48497      * @type Array
48498      */
48499     this.buttons = [];
48500     this.allItems = [];
48501     this.addEvents({
48502         /**
48503          * @event clientvalidation
48504          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
48505          * @param {Form} this
48506          * @param {Boolean} valid true if the form has passed client-side validation
48507          */
48508         clientvalidation: true,
48509         /**
48510          * @event rendered
48511          * Fires when the form is rendered
48512          * @param {Roo.form.Form} form
48513          */
48514         rendered : true
48515     });
48516     
48517     if (this.progressUrl) {
48518             // push a hidden field onto the list of fields..
48519             this.addxtype( {
48520                     xns: Roo.form, 
48521                     xtype : 'Hidden', 
48522                     name : 'UPLOAD_IDENTIFIER' 
48523             });
48524         }
48525         
48526     
48527     Roo.each(xitems, this.addxtype, this);
48528     
48529 };
48530
48531 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
48532     /**
48533      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
48534      */
48535     /**
48536      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
48537      */
48538     /**
48539      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
48540      */
48541     buttonAlign:'center',
48542
48543     /**
48544      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
48545      */
48546     minButtonWidth:75,
48547
48548     /**
48549      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
48550      * This property cascades to child containers if not set.
48551      */
48552     labelAlign:'left',
48553
48554     /**
48555      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
48556      * fires a looping event with that state. This is required to bind buttons to the valid
48557      * state using the config value formBind:true on the button.
48558      */
48559     monitorValid : false,
48560
48561     /**
48562      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
48563      */
48564     monitorPoll : 200,
48565     
48566     /**
48567      * @cfg {String} progressUrl - Url to return progress data 
48568      */
48569     
48570     progressUrl : false,
48571     /**
48572      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
48573      * sending a formdata with extra parameters - eg uploaded elements.
48574      */
48575     
48576     formData : false,
48577     
48578     /**
48579      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
48580      * fields are added and the column is closed. If no fields are passed the column remains open
48581      * until end() is called.
48582      * @param {Object} config The config to pass to the column
48583      * @param {Field} field1 (optional)
48584      * @param {Field} field2 (optional)
48585      * @param {Field} etc (optional)
48586      * @return Column The column container object
48587      */
48588     column : function(c){
48589         var col = new Roo.form.Column(c);
48590         this.start(col);
48591         if(arguments.length > 1){ // duplicate code required because of Opera
48592             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48593             this.end();
48594         }
48595         return col;
48596     },
48597
48598     /**
48599      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
48600      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
48601      * until end() is called.
48602      * @param {Object} config The config to pass to the fieldset
48603      * @param {Field} field1 (optional)
48604      * @param {Field} field2 (optional)
48605      * @param {Field} etc (optional)
48606      * @return FieldSet The fieldset container object
48607      */
48608     fieldset : function(c){
48609         var fs = new Roo.form.FieldSet(c);
48610         this.start(fs);
48611         if(arguments.length > 1){ // duplicate code required because of Opera
48612             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48613             this.end();
48614         }
48615         return fs;
48616     },
48617
48618     /**
48619      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
48620      * fields are added and the container is closed. If no fields are passed the container remains open
48621      * until end() is called.
48622      * @param {Object} config The config to pass to the Layout
48623      * @param {Field} field1 (optional)
48624      * @param {Field} field2 (optional)
48625      * @param {Field} etc (optional)
48626      * @return Layout The container object
48627      */
48628     container : function(c){
48629         var l = new Roo.form.Layout(c);
48630         this.start(l);
48631         if(arguments.length > 1){ // duplicate code required because of Opera
48632             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
48633             this.end();
48634         }
48635         return l;
48636     },
48637
48638     /**
48639      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
48640      * @param {Object} container A Roo.form.Layout or subclass of Layout
48641      * @return {Form} this
48642      */
48643     start : function(c){
48644         // cascade label info
48645         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
48646         this.active.stack.push(c);
48647         c.ownerCt = this.active;
48648         this.active = c;
48649         return this;
48650     },
48651
48652     /**
48653      * Closes the current open container
48654      * @return {Form} this
48655      */
48656     end : function(){
48657         if(this.active == this.root){
48658             return this;
48659         }
48660         this.active = this.active.ownerCt;
48661         return this;
48662     },
48663
48664     /**
48665      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
48666      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
48667      * as the label of the field.
48668      * @param {Field} field1
48669      * @param {Field} field2 (optional)
48670      * @param {Field} etc. (optional)
48671      * @return {Form} this
48672      */
48673     add : function(){
48674         this.active.stack.push.apply(this.active.stack, arguments);
48675         this.allItems.push.apply(this.allItems,arguments);
48676         var r = [];
48677         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
48678             if(a[i].isFormField){
48679                 r.push(a[i]);
48680             }
48681         }
48682         if(r.length > 0){
48683             Roo.form.Form.superclass.add.apply(this, r);
48684         }
48685         return this;
48686     },
48687     
48688
48689     
48690     
48691     
48692      /**
48693      * Find any element that has been added to a form, using it's ID or name
48694      * This can include framesets, columns etc. along with regular fields..
48695      * @param {String} id - id or name to find.
48696      
48697      * @return {Element} e - or false if nothing found.
48698      */
48699     findbyId : function(id)
48700     {
48701         var ret = false;
48702         if (!id) {
48703             return ret;
48704         }
48705         Roo.each(this.allItems, function(f){
48706             if (f.id == id || f.name == id ){
48707                 ret = f;
48708                 return false;
48709             }
48710         });
48711         return ret;
48712     },
48713
48714     
48715     
48716     /**
48717      * Render this form into the passed container. This should only be called once!
48718      * @param {String/HTMLElement/Element} container The element this component should be rendered into
48719      * @return {Form} this
48720      */
48721     render : function(ct)
48722     {
48723         
48724         
48725         
48726         ct = Roo.get(ct);
48727         var o = this.autoCreate || {
48728             tag: 'form',
48729             method : this.method || 'POST',
48730             id : this.id || Roo.id()
48731         };
48732         this.initEl(ct.createChild(o));
48733
48734         this.root.render(this.el);
48735         
48736        
48737              
48738         this.items.each(function(f){
48739             f.render('x-form-el-'+f.id);
48740         });
48741
48742         if(this.buttons.length > 0){
48743             // tables are required to maintain order and for correct IE layout
48744             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
48745                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
48746                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
48747             }}, null, true);
48748             var tr = tb.getElementsByTagName('tr')[0];
48749             for(var i = 0, len = this.buttons.length; i < len; i++) {
48750                 var b = this.buttons[i];
48751                 var td = document.createElement('td');
48752                 td.className = 'x-form-btn-td';
48753                 b.render(tr.appendChild(td));
48754             }
48755         }
48756         if(this.monitorValid){ // initialize after render
48757             this.startMonitoring();
48758         }
48759         this.fireEvent('rendered', this);
48760         return this;
48761     },
48762
48763     /**
48764      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
48765      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
48766      * object or a valid Roo.DomHelper element config
48767      * @param {Function} handler The function called when the button is clicked
48768      * @param {Object} scope (optional) The scope of the handler function
48769      * @return {Roo.Button}
48770      */
48771     addButton : function(config, handler, scope){
48772         var bc = {
48773             handler: handler,
48774             scope: scope,
48775             minWidth: this.minButtonWidth,
48776             hideParent:true
48777         };
48778         if(typeof config == "string"){
48779             bc.text = config;
48780         }else{
48781             Roo.apply(bc, config);
48782         }
48783         var btn = new Roo.Button(null, bc);
48784         this.buttons.push(btn);
48785         return btn;
48786     },
48787
48788      /**
48789      * Adds a series of form elements (using the xtype property as the factory method.
48790      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
48791      * @param {Object} config 
48792      */
48793     
48794     addxtype : function()
48795     {
48796         var ar = Array.prototype.slice.call(arguments, 0);
48797         var ret = false;
48798         for(var i = 0; i < ar.length; i++) {
48799             if (!ar[i]) {
48800                 continue; // skip -- if this happends something invalid got sent, we 
48801                 // should ignore it, as basically that interface element will not show up
48802                 // and that should be pretty obvious!!
48803             }
48804             
48805             if (Roo.form[ar[i].xtype]) {
48806                 ar[i].form = this;
48807                 var fe = Roo.factory(ar[i], Roo.form);
48808                 if (!ret) {
48809                     ret = fe;
48810                 }
48811                 fe.form = this;
48812                 if (fe.store) {
48813                     fe.store.form = this;
48814                 }
48815                 if (fe.isLayout) {  
48816                          
48817                     this.start(fe);
48818                     this.allItems.push(fe);
48819                     if (fe.items && fe.addxtype) {
48820                         fe.addxtype.apply(fe, fe.items);
48821                         delete fe.items;
48822                     }
48823                      this.end();
48824                     continue;
48825                 }
48826                 
48827                 
48828                  
48829                 this.add(fe);
48830               //  console.log('adding ' + ar[i].xtype);
48831             }
48832             if (ar[i].xtype == 'Button') {  
48833                 //console.log('adding button');
48834                 //console.log(ar[i]);
48835                 this.addButton(ar[i]);
48836                 this.allItems.push(fe);
48837                 continue;
48838             }
48839             
48840             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
48841                 alert('end is not supported on xtype any more, use items');
48842             //    this.end();
48843             //    //console.log('adding end');
48844             }
48845             
48846         }
48847         return ret;
48848     },
48849     
48850     /**
48851      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
48852      * option "monitorValid"
48853      */
48854     startMonitoring : function(){
48855         if(!this.bound){
48856             this.bound = true;
48857             Roo.TaskMgr.start({
48858                 run : this.bindHandler,
48859                 interval : this.monitorPoll || 200,
48860                 scope: this
48861             });
48862         }
48863     },
48864
48865     /**
48866      * Stops monitoring of the valid state of this form
48867      */
48868     stopMonitoring : function(){
48869         this.bound = false;
48870     },
48871
48872     // private
48873     bindHandler : function(){
48874         if(!this.bound){
48875             return false; // stops binding
48876         }
48877         var valid = true;
48878         this.items.each(function(f){
48879             if(!f.isValid(true)){
48880                 valid = false;
48881                 return false;
48882             }
48883         });
48884         for(var i = 0, len = this.buttons.length; i < len; i++){
48885             var btn = this.buttons[i];
48886             if(btn.formBind === true && btn.disabled === valid){
48887                 btn.setDisabled(!valid);
48888             }
48889         }
48890         this.fireEvent('clientvalidation', this, valid);
48891     }
48892     
48893     
48894     
48895     
48896     
48897     
48898     
48899     
48900 });
48901
48902
48903 // back compat
48904 Roo.Form = Roo.form.Form;
48905 /*
48906  * Based on:
48907  * Ext JS Library 1.1.1
48908  * Copyright(c) 2006-2007, Ext JS, LLC.
48909  *
48910  * Originally Released Under LGPL - original licence link has changed is not relivant.
48911  *
48912  * Fork - LGPL
48913  * <script type="text/javascript">
48914  */
48915
48916 // as we use this in bootstrap.
48917 Roo.namespace('Roo.form');
48918  /**
48919  * @class Roo.form.Action
48920  * Internal Class used to handle form actions
48921  * @constructor
48922  * @param {Roo.form.BasicForm} el The form element or its id
48923  * @param {Object} config Configuration options
48924  */
48925
48926  
48927  
48928 // define the action interface
48929 Roo.form.Action = function(form, options){
48930     this.form = form;
48931     this.options = options || {};
48932 };
48933 /**
48934  * Client Validation Failed
48935  * @const 
48936  */
48937 Roo.form.Action.CLIENT_INVALID = 'client';
48938 /**
48939  * Server Validation Failed
48940  * @const 
48941  */
48942 Roo.form.Action.SERVER_INVALID = 'server';
48943  /**
48944  * Connect to Server Failed
48945  * @const 
48946  */
48947 Roo.form.Action.CONNECT_FAILURE = 'connect';
48948 /**
48949  * Reading Data from Server Failed
48950  * @const 
48951  */
48952 Roo.form.Action.LOAD_FAILURE = 'load';
48953
48954 Roo.form.Action.prototype = {
48955     type : 'default',
48956     failureType : undefined,
48957     response : undefined,
48958     result : undefined,
48959
48960     // interface method
48961     run : function(options){
48962
48963     },
48964
48965     // interface method
48966     success : function(response){
48967
48968     },
48969
48970     // interface method
48971     handleResponse : function(response){
48972
48973     },
48974
48975     // default connection failure
48976     failure : function(response){
48977         
48978         this.response = response;
48979         this.failureType = Roo.form.Action.CONNECT_FAILURE;
48980         this.form.afterAction(this, false);
48981     },
48982
48983     processResponse : function(response){
48984         this.response = response;
48985         if(!response.responseText){
48986             return true;
48987         }
48988         this.result = this.handleResponse(response);
48989         return this.result;
48990     },
48991
48992     // utility functions used internally
48993     getUrl : function(appendParams){
48994         var url = this.options.url || this.form.url || this.form.el.dom.action;
48995         if(appendParams){
48996             var p = this.getParams();
48997             if(p){
48998                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
48999             }
49000         }
49001         return url;
49002     },
49003
49004     getMethod : function(){
49005         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
49006     },
49007
49008     getParams : function(){
49009         var bp = this.form.baseParams;
49010         var p = this.options.params;
49011         if(p){
49012             if(typeof p == "object"){
49013                 p = Roo.urlEncode(Roo.applyIf(p, bp));
49014             }else if(typeof p == 'string' && bp){
49015                 p += '&' + Roo.urlEncode(bp);
49016             }
49017         }else if(bp){
49018             p = Roo.urlEncode(bp);
49019         }
49020         return p;
49021     },
49022
49023     createCallback : function(){
49024         return {
49025             success: this.success,
49026             failure: this.failure,
49027             scope: this,
49028             timeout: (this.form.timeout*1000),
49029             upload: this.form.fileUpload ? this.success : undefined
49030         };
49031     }
49032 };
49033
49034 Roo.form.Action.Submit = function(form, options){
49035     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
49036 };
49037
49038 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
49039     type : 'submit',
49040
49041     haveProgress : false,
49042     uploadComplete : false,
49043     
49044     // uploadProgress indicator.
49045     uploadProgress : function()
49046     {
49047         if (!this.form.progressUrl) {
49048             return;
49049         }
49050         
49051         if (!this.haveProgress) {
49052             Roo.MessageBox.progress("Uploading", "Uploading");
49053         }
49054         if (this.uploadComplete) {
49055            Roo.MessageBox.hide();
49056            return;
49057         }
49058         
49059         this.haveProgress = true;
49060    
49061         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
49062         
49063         var c = new Roo.data.Connection();
49064         c.request({
49065             url : this.form.progressUrl,
49066             params: {
49067                 id : uid
49068             },
49069             method: 'GET',
49070             success : function(req){
49071                //console.log(data);
49072                 var rdata = false;
49073                 var edata;
49074                 try  {
49075                    rdata = Roo.decode(req.responseText)
49076                 } catch (e) {
49077                     Roo.log("Invalid data from server..");
49078                     Roo.log(edata);
49079                     return;
49080                 }
49081                 if (!rdata || !rdata.success) {
49082                     Roo.log(rdata);
49083                     Roo.MessageBox.alert(Roo.encode(rdata));
49084                     return;
49085                 }
49086                 var data = rdata.data;
49087                 
49088                 if (this.uploadComplete) {
49089                    Roo.MessageBox.hide();
49090                    return;
49091                 }
49092                    
49093                 if (data){
49094                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
49095                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
49096                     );
49097                 }
49098                 this.uploadProgress.defer(2000,this);
49099             },
49100        
49101             failure: function(data) {
49102                 Roo.log('progress url failed ');
49103                 Roo.log(data);
49104             },
49105             scope : this
49106         });
49107            
49108     },
49109     
49110     
49111     run : function()
49112     {
49113         // run get Values on the form, so it syncs any secondary forms.
49114         this.form.getValues();
49115         
49116         var o = this.options;
49117         var method = this.getMethod();
49118         var isPost = method == 'POST';
49119         if(o.clientValidation === false || this.form.isValid()){
49120             
49121             if (this.form.progressUrl) {
49122                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
49123                     (new Date() * 1) + '' + Math.random());
49124                     
49125             } 
49126             
49127             
49128             Roo.Ajax.request(Roo.apply(this.createCallback(), {
49129                 form:this.form.el.dom,
49130                 url:this.getUrl(!isPost),
49131                 method: method,
49132                 params:isPost ? this.getParams() : null,
49133                 isUpload: this.form.fileUpload,
49134                 formData : this.form.formData
49135             }));
49136             
49137             this.uploadProgress();
49138
49139         }else if (o.clientValidation !== false){ // client validation failed
49140             this.failureType = Roo.form.Action.CLIENT_INVALID;
49141             this.form.afterAction(this, false);
49142         }
49143     },
49144
49145     success : function(response)
49146     {
49147         this.uploadComplete= true;
49148         if (this.haveProgress) {
49149             Roo.MessageBox.hide();
49150         }
49151         
49152         
49153         var result = this.processResponse(response);
49154         if(result === true || result.success){
49155             this.form.afterAction(this, true);
49156             return;
49157         }
49158         if(result.errors){
49159             this.form.markInvalid(result.errors);
49160             this.failureType = Roo.form.Action.SERVER_INVALID;
49161         }
49162         this.form.afterAction(this, false);
49163     },
49164     failure : function(response)
49165     {
49166         this.uploadComplete= true;
49167         if (this.haveProgress) {
49168             Roo.MessageBox.hide();
49169         }
49170         
49171         this.response = response;
49172         this.failureType = Roo.form.Action.CONNECT_FAILURE;
49173         this.form.afterAction(this, false);
49174     },
49175     
49176     handleResponse : function(response){
49177         if(this.form.errorReader){
49178             var rs = this.form.errorReader.read(response);
49179             var errors = [];
49180             if(rs.records){
49181                 for(var i = 0, len = rs.records.length; i < len; i++) {
49182                     var r = rs.records[i];
49183                     errors[i] = r.data;
49184                 }
49185             }
49186             if(errors.length < 1){
49187                 errors = null;
49188             }
49189             return {
49190                 success : rs.success,
49191                 errors : errors
49192             };
49193         }
49194         var ret = false;
49195         try {
49196             ret = Roo.decode(response.responseText);
49197         } catch (e) {
49198             ret = {
49199                 success: false,
49200                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
49201                 errors : []
49202             };
49203         }
49204         return ret;
49205         
49206     }
49207 });
49208
49209
49210 Roo.form.Action.Load = function(form, options){
49211     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
49212     this.reader = this.form.reader;
49213 };
49214
49215 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
49216     type : 'load',
49217
49218     run : function(){
49219         
49220         Roo.Ajax.request(Roo.apply(
49221                 this.createCallback(), {
49222                     method:this.getMethod(),
49223                     url:this.getUrl(false),
49224                     params:this.getParams()
49225         }));
49226     },
49227
49228     success : function(response){
49229         
49230         var result = this.processResponse(response);
49231         if(result === true || !result.success || !result.data){
49232             this.failureType = Roo.form.Action.LOAD_FAILURE;
49233             this.form.afterAction(this, false);
49234             return;
49235         }
49236         this.form.clearInvalid();
49237         this.form.setValues(result.data);
49238         this.form.afterAction(this, true);
49239     },
49240
49241     handleResponse : function(response){
49242         if(this.form.reader){
49243             var rs = this.form.reader.read(response);
49244             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
49245             return {
49246                 success : rs.success,
49247                 data : data
49248             };
49249         }
49250         return Roo.decode(response.responseText);
49251     }
49252 });
49253
49254 Roo.form.Action.ACTION_TYPES = {
49255     'load' : Roo.form.Action.Load,
49256     'submit' : Roo.form.Action.Submit
49257 };/*
49258  * Based on:
49259  * Ext JS Library 1.1.1
49260  * Copyright(c) 2006-2007, Ext JS, LLC.
49261  *
49262  * Originally Released Under LGPL - original licence link has changed is not relivant.
49263  *
49264  * Fork - LGPL
49265  * <script type="text/javascript">
49266  */
49267  
49268 /**
49269  * @class Roo.form.Layout
49270  * @extends Roo.Component
49271  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
49272  * @constructor
49273  * @param {Object} config Configuration options
49274  */
49275 Roo.form.Layout = function(config){
49276     var xitems = [];
49277     if (config.items) {
49278         xitems = config.items;
49279         delete config.items;
49280     }
49281     Roo.form.Layout.superclass.constructor.call(this, config);
49282     this.stack = [];
49283     Roo.each(xitems, this.addxtype, this);
49284      
49285 };
49286
49287 Roo.extend(Roo.form.Layout, Roo.Component, {
49288     /**
49289      * @cfg {String/Object} autoCreate
49290      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
49291      */
49292     /**
49293      * @cfg {String/Object/Function} style
49294      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
49295      * a function which returns such a specification.
49296      */
49297     /**
49298      * @cfg {String} labelAlign
49299      * Valid values are "left," "top" and "right" (defaults to "left")
49300      */
49301     /**
49302      * @cfg {Number} labelWidth
49303      * Fixed width in pixels of all field labels (defaults to undefined)
49304      */
49305     /**
49306      * @cfg {Boolean} clear
49307      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
49308      */
49309     clear : true,
49310     /**
49311      * @cfg {String} labelSeparator
49312      * The separator to use after field labels (defaults to ':')
49313      */
49314     labelSeparator : ':',
49315     /**
49316      * @cfg {Boolean} hideLabels
49317      * True to suppress the display of field labels in this layout (defaults to false)
49318      */
49319     hideLabels : false,
49320
49321     // private
49322     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
49323     
49324     isLayout : true,
49325     
49326     // private
49327     onRender : function(ct, position){
49328         if(this.el){ // from markup
49329             this.el = Roo.get(this.el);
49330         }else {  // generate
49331             var cfg = this.getAutoCreate();
49332             this.el = ct.createChild(cfg, position);
49333         }
49334         if(this.style){
49335             this.el.applyStyles(this.style);
49336         }
49337         if(this.labelAlign){
49338             this.el.addClass('x-form-label-'+this.labelAlign);
49339         }
49340         if(this.hideLabels){
49341             this.labelStyle = "display:none";
49342             this.elementStyle = "padding-left:0;";
49343         }else{
49344             if(typeof this.labelWidth == 'number'){
49345                 this.labelStyle = "width:"+this.labelWidth+"px;";
49346                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
49347             }
49348             if(this.labelAlign == 'top'){
49349                 this.labelStyle = "width:auto;";
49350                 this.elementStyle = "padding-left:0;";
49351             }
49352         }
49353         var stack = this.stack;
49354         var slen = stack.length;
49355         if(slen > 0){
49356             if(!this.fieldTpl){
49357                 var t = new Roo.Template(
49358                     '<div class="x-form-item {5}">',
49359                         '<label for="{0}" style="{2}">{1}{4}</label>',
49360                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49361                         '</div>',
49362                     '</div><div class="x-form-clear-left"></div>'
49363                 );
49364                 t.disableFormats = true;
49365                 t.compile();
49366                 Roo.form.Layout.prototype.fieldTpl = t;
49367             }
49368             for(var i = 0; i < slen; i++) {
49369                 if(stack[i].isFormField){
49370                     this.renderField(stack[i]);
49371                 }else{
49372                     this.renderComponent(stack[i]);
49373                 }
49374             }
49375         }
49376         if(this.clear){
49377             this.el.createChild({cls:'x-form-clear'});
49378         }
49379     },
49380
49381     // private
49382     renderField : function(f){
49383         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
49384                f.id, //0
49385                f.fieldLabel, //1
49386                f.labelStyle||this.labelStyle||'', //2
49387                this.elementStyle||'', //3
49388                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
49389                f.itemCls||this.itemCls||''  //5
49390        ], true).getPrevSibling());
49391     },
49392
49393     // private
49394     renderComponent : function(c){
49395         c.render(c.isLayout ? this.el : this.el.createChild());    
49396     },
49397     /**
49398      * Adds a object form elements (using the xtype property as the factory method.)
49399      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
49400      * @param {Object} config 
49401      */
49402     addxtype : function(o)
49403     {
49404         // create the lement.
49405         o.form = this.form;
49406         var fe = Roo.factory(o, Roo.form);
49407         this.form.allItems.push(fe);
49408         this.stack.push(fe);
49409         
49410         if (fe.isFormField) {
49411             this.form.items.add(fe);
49412         }
49413          
49414         return fe;
49415     }
49416 });
49417
49418 /**
49419  * @class Roo.form.Column
49420  * @extends Roo.form.Layout
49421  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
49422  * @constructor
49423  * @param {Object} config Configuration options
49424  */
49425 Roo.form.Column = function(config){
49426     Roo.form.Column.superclass.constructor.call(this, config);
49427 };
49428
49429 Roo.extend(Roo.form.Column, Roo.form.Layout, {
49430     /**
49431      * @cfg {Number/String} width
49432      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49433      */
49434     /**
49435      * @cfg {String/Object} autoCreate
49436      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
49437      */
49438
49439     // private
49440     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
49441
49442     // private
49443     onRender : function(ct, position){
49444         Roo.form.Column.superclass.onRender.call(this, ct, position);
49445         if(this.width){
49446             this.el.setWidth(this.width);
49447         }
49448     }
49449 });
49450
49451
49452 /**
49453  * @class Roo.form.Row
49454  * @extends Roo.form.Layout
49455  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
49456  * @constructor
49457  * @param {Object} config Configuration options
49458  */
49459
49460  
49461 Roo.form.Row = function(config){
49462     Roo.form.Row.superclass.constructor.call(this, config);
49463 };
49464  
49465 Roo.extend(Roo.form.Row, Roo.form.Layout, {
49466       /**
49467      * @cfg {Number/String} width
49468      * The fixed width of the column in pixels or CSS value (defaults to "auto")
49469      */
49470     /**
49471      * @cfg {Number/String} height
49472      * The fixed height of the column in pixels or CSS value (defaults to "auto")
49473      */
49474     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
49475     
49476     padWidth : 20,
49477     // private
49478     onRender : function(ct, position){
49479         //console.log('row render');
49480         if(!this.rowTpl){
49481             var t = new Roo.Template(
49482                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
49483                     '<label for="{0}" style="{2}">{1}{4}</label>',
49484                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
49485                     '</div>',
49486                 '</div>'
49487             );
49488             t.disableFormats = true;
49489             t.compile();
49490             Roo.form.Layout.prototype.rowTpl = t;
49491         }
49492         this.fieldTpl = this.rowTpl;
49493         
49494         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
49495         var labelWidth = 100;
49496         
49497         if ((this.labelAlign != 'top')) {
49498             if (typeof this.labelWidth == 'number') {
49499                 labelWidth = this.labelWidth
49500             }
49501             this.padWidth =  20 + labelWidth;
49502             
49503         }
49504         
49505         Roo.form.Column.superclass.onRender.call(this, ct, position);
49506         if(this.width){
49507             this.el.setWidth(this.width);
49508         }
49509         if(this.height){
49510             this.el.setHeight(this.height);
49511         }
49512     },
49513     
49514     // private
49515     renderField : function(f){
49516         f.fieldEl = this.fieldTpl.append(this.el, [
49517                f.id, f.fieldLabel,
49518                f.labelStyle||this.labelStyle||'',
49519                this.elementStyle||'',
49520                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
49521                f.itemCls||this.itemCls||'',
49522                f.width ? f.width + this.padWidth : 160 + this.padWidth
49523        ],true);
49524     }
49525 });
49526  
49527
49528 /**
49529  * @class Roo.form.FieldSet
49530  * @extends Roo.form.Layout
49531  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
49532  * @constructor
49533  * @param {Object} config Configuration options
49534  */
49535 Roo.form.FieldSet = function(config){
49536     Roo.form.FieldSet.superclass.constructor.call(this, config);
49537 };
49538
49539 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
49540     /**
49541      * @cfg {String} legend
49542      * The text to display as the legend for the FieldSet (defaults to '')
49543      */
49544     /**
49545      * @cfg {String/Object} autoCreate
49546      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
49547      */
49548
49549     // private
49550     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
49551
49552     // private
49553     onRender : function(ct, position){
49554         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
49555         if(this.legend){
49556             this.setLegend(this.legend);
49557         }
49558     },
49559
49560     // private
49561     setLegend : function(text){
49562         if(this.rendered){
49563             this.el.child('legend').update(text);
49564         }
49565     }
49566 });/*
49567  * Based on:
49568  * Ext JS Library 1.1.1
49569  * Copyright(c) 2006-2007, Ext JS, LLC.
49570  *
49571  * Originally Released Under LGPL - original licence link has changed is not relivant.
49572  *
49573  * Fork - LGPL
49574  * <script type="text/javascript">
49575  */
49576 /**
49577  * @class Roo.form.VTypes
49578  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
49579  * @singleton
49580  */
49581 Roo.form.VTypes = function(){
49582     // closure these in so they are only created once.
49583     var alpha = /^[a-zA-Z_]+$/;
49584     var alphanum = /^[a-zA-Z0-9_]+$/;
49585     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
49586     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
49587
49588     // All these messages and functions are configurable
49589     return {
49590         /**
49591          * The function used to validate email addresses
49592          * @param {String} value The email address
49593          */
49594         'email' : function(v){
49595             return email.test(v);
49596         },
49597         /**
49598          * The error text to display when the email validation function returns false
49599          * @type String
49600          */
49601         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
49602         /**
49603          * The keystroke filter mask to be applied on email input
49604          * @type RegExp
49605          */
49606         'emailMask' : /[a-z0-9_\.\-@]/i,
49607
49608         /**
49609          * The function used to validate URLs
49610          * @param {String} value The URL
49611          */
49612         'url' : function(v){
49613             return url.test(v);
49614         },
49615         /**
49616          * The error text to display when the url validation function returns false
49617          * @type String
49618          */
49619         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
49620         
49621         /**
49622          * The function used to validate alpha values
49623          * @param {String} value The value
49624          */
49625         'alpha' : function(v){
49626             return alpha.test(v);
49627         },
49628         /**
49629          * The error text to display when the alpha validation function returns false
49630          * @type String
49631          */
49632         'alphaText' : 'This field should only contain letters and _',
49633         /**
49634          * The keystroke filter mask to be applied on alpha input
49635          * @type RegExp
49636          */
49637         'alphaMask' : /[a-z_]/i,
49638
49639         /**
49640          * The function used to validate alphanumeric values
49641          * @param {String} value The value
49642          */
49643         'alphanum' : function(v){
49644             return alphanum.test(v);
49645         },
49646         /**
49647          * The error text to display when the alphanumeric validation function returns false
49648          * @type String
49649          */
49650         'alphanumText' : 'This field should only contain letters, numbers and _',
49651         /**
49652          * The keystroke filter mask to be applied on alphanumeric input
49653          * @type RegExp
49654          */
49655         'alphanumMask' : /[a-z0-9_]/i
49656     };
49657 }();//<script type="text/javascript">
49658
49659 /**
49660  * @class Roo.form.FCKeditor
49661  * @extends Roo.form.TextArea
49662  * Wrapper around the FCKEditor http://www.fckeditor.net
49663  * @constructor
49664  * Creates a new FCKeditor
49665  * @param {Object} config Configuration options
49666  */
49667 Roo.form.FCKeditor = function(config){
49668     Roo.form.FCKeditor.superclass.constructor.call(this, config);
49669     this.addEvents({
49670          /**
49671          * @event editorinit
49672          * Fired when the editor is initialized - you can add extra handlers here..
49673          * @param {FCKeditor} this
49674          * @param {Object} the FCK object.
49675          */
49676         editorinit : true
49677     });
49678     
49679     
49680 };
49681 Roo.form.FCKeditor.editors = { };
49682 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
49683 {
49684     //defaultAutoCreate : {
49685     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
49686     //},
49687     // private
49688     /**
49689      * @cfg {Object} fck options - see fck manual for details.
49690      */
49691     fckconfig : false,
49692     
49693     /**
49694      * @cfg {Object} fck toolbar set (Basic or Default)
49695      */
49696     toolbarSet : 'Basic',
49697     /**
49698      * @cfg {Object} fck BasePath
49699      */ 
49700     basePath : '/fckeditor/',
49701     
49702     
49703     frame : false,
49704     
49705     value : '',
49706     
49707    
49708     onRender : function(ct, position)
49709     {
49710         if(!this.el){
49711             this.defaultAutoCreate = {
49712                 tag: "textarea",
49713                 style:"width:300px;height:60px;",
49714                 autocomplete: "new-password"
49715             };
49716         }
49717         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
49718         /*
49719         if(this.grow){
49720             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
49721             if(this.preventScrollbars){
49722                 this.el.setStyle("overflow", "hidden");
49723             }
49724             this.el.setHeight(this.growMin);
49725         }
49726         */
49727         //console.log('onrender' + this.getId() );
49728         Roo.form.FCKeditor.editors[this.getId()] = this;
49729          
49730
49731         this.replaceTextarea() ;
49732         
49733     },
49734     
49735     getEditor : function() {
49736         return this.fckEditor;
49737     },
49738     /**
49739      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
49740      * @param {Mixed} value The value to set
49741      */
49742     
49743     
49744     setValue : function(value)
49745     {
49746         //console.log('setValue: ' + value);
49747         
49748         if(typeof(value) == 'undefined') { // not sure why this is happending...
49749             return;
49750         }
49751         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49752         
49753         //if(!this.el || !this.getEditor()) {
49754         //    this.value = value;
49755             //this.setValue.defer(100,this,[value]);    
49756         //    return;
49757         //} 
49758         
49759         if(!this.getEditor()) {
49760             return;
49761         }
49762         
49763         this.getEditor().SetData(value);
49764         
49765         //
49766
49767     },
49768
49769     /**
49770      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
49771      * @return {Mixed} value The field value
49772      */
49773     getValue : function()
49774     {
49775         
49776         if (this.frame && this.frame.dom.style.display == 'none') {
49777             return Roo.form.FCKeditor.superclass.getValue.call(this);
49778         }
49779         
49780         if(!this.el || !this.getEditor()) {
49781            
49782            // this.getValue.defer(100,this); 
49783             return this.value;
49784         }
49785        
49786         
49787         var value=this.getEditor().GetData();
49788         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
49789         return Roo.form.FCKeditor.superclass.getValue.call(this);
49790         
49791
49792     },
49793
49794     /**
49795      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
49796      * @return {Mixed} value The field value
49797      */
49798     getRawValue : function()
49799     {
49800         if (this.frame && this.frame.dom.style.display == 'none') {
49801             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49802         }
49803         
49804         if(!this.el || !this.getEditor()) {
49805             //this.getRawValue.defer(100,this); 
49806             return this.value;
49807             return;
49808         }
49809         
49810         
49811         
49812         var value=this.getEditor().GetData();
49813         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
49814         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
49815          
49816     },
49817     
49818     setSize : function(w,h) {
49819         
49820         
49821         
49822         //if (this.frame && this.frame.dom.style.display == 'none') {
49823         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49824         //    return;
49825         //}
49826         //if(!this.el || !this.getEditor()) {
49827         //    this.setSize.defer(100,this, [w,h]); 
49828         //    return;
49829         //}
49830         
49831         
49832         
49833         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
49834         
49835         this.frame.dom.setAttribute('width', w);
49836         this.frame.dom.setAttribute('height', h);
49837         this.frame.setSize(w,h);
49838         
49839     },
49840     
49841     toggleSourceEdit : function(value) {
49842         
49843       
49844          
49845         this.el.dom.style.display = value ? '' : 'none';
49846         this.frame.dom.style.display = value ?  'none' : '';
49847         
49848     },
49849     
49850     
49851     focus: function(tag)
49852     {
49853         if (this.frame.dom.style.display == 'none') {
49854             return Roo.form.FCKeditor.superclass.focus.call(this);
49855         }
49856         if(!this.el || !this.getEditor()) {
49857             this.focus.defer(100,this, [tag]); 
49858             return;
49859         }
49860         
49861         
49862         
49863         
49864         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
49865         this.getEditor().Focus();
49866         if (tgs.length) {
49867             if (!this.getEditor().Selection.GetSelection()) {
49868                 this.focus.defer(100,this, [tag]); 
49869                 return;
49870             }
49871             
49872             
49873             var r = this.getEditor().EditorDocument.createRange();
49874             r.setStart(tgs[0],0);
49875             r.setEnd(tgs[0],0);
49876             this.getEditor().Selection.GetSelection().removeAllRanges();
49877             this.getEditor().Selection.GetSelection().addRange(r);
49878             this.getEditor().Focus();
49879         }
49880         
49881     },
49882     
49883     
49884     
49885     replaceTextarea : function()
49886     {
49887         if ( document.getElementById( this.getId() + '___Frame' ) ) {
49888             return ;
49889         }
49890         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
49891         //{
49892             // We must check the elements firstly using the Id and then the name.
49893         var oTextarea = document.getElementById( this.getId() );
49894         
49895         var colElementsByName = document.getElementsByName( this.getId() ) ;
49896          
49897         oTextarea.style.display = 'none' ;
49898
49899         if ( oTextarea.tabIndex ) {            
49900             this.TabIndex = oTextarea.tabIndex ;
49901         }
49902         
49903         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
49904         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
49905         this.frame = Roo.get(this.getId() + '___Frame')
49906     },
49907     
49908     _getConfigHtml : function()
49909     {
49910         var sConfig = '' ;
49911
49912         for ( var o in this.fckconfig ) {
49913             sConfig += sConfig.length > 0  ? '&amp;' : '';
49914             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
49915         }
49916
49917         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
49918     },
49919     
49920     
49921     _getIFrameHtml : function()
49922     {
49923         var sFile = 'fckeditor.html' ;
49924         /* no idea what this is about..
49925         try
49926         {
49927             if ( (/fcksource=true/i).test( window.top.location.search ) )
49928                 sFile = 'fckeditor.original.html' ;
49929         }
49930         catch (e) { 
49931         */
49932
49933         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
49934         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
49935         
49936         
49937         var html = '<iframe id="' + this.getId() +
49938             '___Frame" src="' + sLink +
49939             '" width="' + this.width +
49940             '" height="' + this.height + '"' +
49941             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
49942             ' frameborder="0" scrolling="no"></iframe>' ;
49943
49944         return html ;
49945     },
49946     
49947     _insertHtmlBefore : function( html, element )
49948     {
49949         if ( element.insertAdjacentHTML )       {
49950             // IE
49951             element.insertAdjacentHTML( 'beforeBegin', html ) ;
49952         } else { // Gecko
49953             var oRange = document.createRange() ;
49954             oRange.setStartBefore( element ) ;
49955             var oFragment = oRange.createContextualFragment( html );
49956             element.parentNode.insertBefore( oFragment, element ) ;
49957         }
49958     }
49959     
49960     
49961   
49962     
49963     
49964     
49965     
49966
49967 });
49968
49969 //Roo.reg('fckeditor', Roo.form.FCKeditor);
49970
49971 function FCKeditor_OnComplete(editorInstance){
49972     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
49973     f.fckEditor = editorInstance;
49974     //console.log("loaded");
49975     f.fireEvent('editorinit', f, editorInstance);
49976
49977   
49978
49979  
49980
49981
49982
49983
49984
49985
49986
49987
49988
49989
49990
49991
49992
49993
49994
49995 //<script type="text/javascript">
49996 /**
49997  * @class Roo.form.GridField
49998  * @extends Roo.form.Field
49999  * Embed a grid (or editable grid into a form)
50000  * STATUS ALPHA
50001  * 
50002  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
50003  * it needs 
50004  * xgrid.store = Roo.data.Store
50005  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
50006  * xgrid.store.reader = Roo.data.JsonReader 
50007  * 
50008  * 
50009  * @constructor
50010  * Creates a new GridField
50011  * @param {Object} config Configuration options
50012  */
50013 Roo.form.GridField = function(config){
50014     Roo.form.GridField.superclass.constructor.call(this, config);
50015      
50016 };
50017
50018 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
50019     /**
50020      * @cfg {Number} width  - used to restrict width of grid..
50021      */
50022     width : 100,
50023     /**
50024      * @cfg {Number} height - used to restrict height of grid..
50025      */
50026     height : 50,
50027      /**
50028      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
50029          * 
50030          *}
50031      */
50032     xgrid : false, 
50033     /**
50034      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50035      * {tag: "input", type: "checkbox", autocomplete: "off"})
50036      */
50037    // defaultAutoCreate : { tag: 'div' },
50038     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
50039     /**
50040      * @cfg {String} addTitle Text to include for adding a title.
50041      */
50042     addTitle : false,
50043     //
50044     onResize : function(){
50045         Roo.form.Field.superclass.onResize.apply(this, arguments);
50046     },
50047
50048     initEvents : function(){
50049         // Roo.form.Checkbox.superclass.initEvents.call(this);
50050         // has no events...
50051        
50052     },
50053
50054
50055     getResizeEl : function(){
50056         return this.wrap;
50057     },
50058
50059     getPositionEl : function(){
50060         return this.wrap;
50061     },
50062
50063     // private
50064     onRender : function(ct, position){
50065         
50066         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
50067         var style = this.style;
50068         delete this.style;
50069         
50070         Roo.form.GridField.superclass.onRender.call(this, ct, position);
50071         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
50072         this.viewEl = this.wrap.createChild({ tag: 'div' });
50073         if (style) {
50074             this.viewEl.applyStyles(style);
50075         }
50076         if (this.width) {
50077             this.viewEl.setWidth(this.width);
50078         }
50079         if (this.height) {
50080             this.viewEl.setHeight(this.height);
50081         }
50082         //if(this.inputValue !== undefined){
50083         //this.setValue(this.value);
50084         
50085         
50086         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
50087         
50088         
50089         this.grid.render();
50090         this.grid.getDataSource().on('remove', this.refreshValue, this);
50091         this.grid.getDataSource().on('update', this.refreshValue, this);
50092         this.grid.on('afteredit', this.refreshValue, this);
50093  
50094     },
50095      
50096     
50097     /**
50098      * Sets the value of the item. 
50099      * @param {String} either an object  or a string..
50100      */
50101     setValue : function(v){
50102         //this.value = v;
50103         v = v || []; // empty set..
50104         // this does not seem smart - it really only affects memoryproxy grids..
50105         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
50106             var ds = this.grid.getDataSource();
50107             // assumes a json reader..
50108             var data = {}
50109             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
50110             ds.loadData( data);
50111         }
50112         // clear selection so it does not get stale.
50113         if (this.grid.sm) { 
50114             this.grid.sm.clearSelections();
50115         }
50116         
50117         Roo.form.GridField.superclass.setValue.call(this, v);
50118         this.refreshValue();
50119         // should load data in the grid really....
50120     },
50121     
50122     // private
50123     refreshValue: function() {
50124          var val = [];
50125         this.grid.getDataSource().each(function(r) {
50126             val.push(r.data);
50127         });
50128         this.el.dom.value = Roo.encode(val);
50129     }
50130     
50131      
50132     
50133     
50134 });/*
50135  * Based on:
50136  * Ext JS Library 1.1.1
50137  * Copyright(c) 2006-2007, Ext JS, LLC.
50138  *
50139  * Originally Released Under LGPL - original licence link has changed is not relivant.
50140  *
50141  * Fork - LGPL
50142  * <script type="text/javascript">
50143  */
50144 /**
50145  * @class Roo.form.DisplayField
50146  * @extends Roo.form.Field
50147  * A generic Field to display non-editable data.
50148  * @cfg {Boolean} closable (true|false) default false
50149  * @constructor
50150  * Creates a new Display Field item.
50151  * @param {Object} config Configuration options
50152  */
50153 Roo.form.DisplayField = function(config){
50154     Roo.form.DisplayField.superclass.constructor.call(this, config);
50155     
50156     this.addEvents({
50157         /**
50158          * @event close
50159          * Fires after the click the close btn
50160              * @param {Roo.form.DisplayField} this
50161              */
50162         close : true
50163     });
50164 };
50165
50166 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
50167     inputType:      'hidden',
50168     allowBlank:     true,
50169     readOnly:         true,
50170     
50171  
50172     /**
50173      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50174      */
50175     focusClass : undefined,
50176     /**
50177      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50178      */
50179     fieldClass: 'x-form-field',
50180     
50181      /**
50182      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
50183      */
50184     valueRenderer: undefined,
50185     
50186     width: 100,
50187     /**
50188      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50189      * {tag: "input", type: "checkbox", autocomplete: "off"})
50190      */
50191      
50192  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
50193  
50194     closable : false,
50195     
50196     onResize : function(){
50197         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
50198         
50199     },
50200
50201     initEvents : function(){
50202         // Roo.form.Checkbox.superclass.initEvents.call(this);
50203         // has no events...
50204         
50205         if(this.closable){
50206             this.closeEl.on('click', this.onClose, this);
50207         }
50208        
50209     },
50210
50211
50212     getResizeEl : function(){
50213         return this.wrap;
50214     },
50215
50216     getPositionEl : function(){
50217         return this.wrap;
50218     },
50219
50220     // private
50221     onRender : function(ct, position){
50222         
50223         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
50224         //if(this.inputValue !== undefined){
50225         this.wrap = this.el.wrap();
50226         
50227         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
50228         
50229         if(this.closable){
50230             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
50231         }
50232         
50233         if (this.bodyStyle) {
50234             this.viewEl.applyStyles(this.bodyStyle);
50235         }
50236         //this.viewEl.setStyle('padding', '2px');
50237         
50238         this.setValue(this.value);
50239         
50240     },
50241 /*
50242     // private
50243     initValue : Roo.emptyFn,
50244
50245   */
50246
50247         // private
50248     onClick : function(){
50249         
50250     },
50251
50252     /**
50253      * Sets the checked state of the checkbox.
50254      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
50255      */
50256     setValue : function(v){
50257         this.value = v;
50258         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
50259         // this might be called before we have a dom element..
50260         if (!this.viewEl) {
50261             return;
50262         }
50263         this.viewEl.dom.innerHTML = html;
50264         Roo.form.DisplayField.superclass.setValue.call(this, v);
50265
50266     },
50267     
50268     onClose : function(e)
50269     {
50270         e.preventDefault();
50271         
50272         this.fireEvent('close', this);
50273     }
50274 });/*
50275  * 
50276  * Licence- LGPL
50277  * 
50278  */
50279
50280 /**
50281  * @class Roo.form.DayPicker
50282  * @extends Roo.form.Field
50283  * A Day picker show [M] [T] [W] ....
50284  * @constructor
50285  * Creates a new Day Picker
50286  * @param {Object} config Configuration options
50287  */
50288 Roo.form.DayPicker= function(config){
50289     Roo.form.DayPicker.superclass.constructor.call(this, config);
50290      
50291 };
50292
50293 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
50294     /**
50295      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
50296      */
50297     focusClass : undefined,
50298     /**
50299      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
50300      */
50301     fieldClass: "x-form-field",
50302    
50303     /**
50304      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
50305      * {tag: "input", type: "checkbox", autocomplete: "off"})
50306      */
50307     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
50308     
50309    
50310     actionMode : 'viewEl', 
50311     //
50312     // private
50313  
50314     inputType : 'hidden',
50315     
50316      
50317     inputElement: false, // real input element?
50318     basedOn: false, // ????
50319     
50320     isFormField: true, // not sure where this is needed!!!!
50321
50322     onResize : function(){
50323         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
50324         if(!this.boxLabel){
50325             this.el.alignTo(this.wrap, 'c-c');
50326         }
50327     },
50328
50329     initEvents : function(){
50330         Roo.form.Checkbox.superclass.initEvents.call(this);
50331         this.el.on("click", this.onClick,  this);
50332         this.el.on("change", this.onClick,  this);
50333     },
50334
50335
50336     getResizeEl : function(){
50337         return this.wrap;
50338     },
50339
50340     getPositionEl : function(){
50341         return this.wrap;
50342     },
50343
50344     
50345     // private
50346     onRender : function(ct, position){
50347         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
50348        
50349         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
50350         
50351         var r1 = '<table><tr>';
50352         var r2 = '<tr class="x-form-daypick-icons">';
50353         for (var i=0; i < 7; i++) {
50354             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
50355             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
50356         }
50357         
50358         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
50359         viewEl.select('img').on('click', this.onClick, this);
50360         this.viewEl = viewEl;   
50361         
50362         
50363         // this will not work on Chrome!!!
50364         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
50365         this.el.on('propertychange', this.setFromHidden,  this);  //ie
50366         
50367         
50368           
50369
50370     },
50371
50372     // private
50373     initValue : Roo.emptyFn,
50374
50375     /**
50376      * Returns the checked state of the checkbox.
50377      * @return {Boolean} True if checked, else false
50378      */
50379     getValue : function(){
50380         return this.el.dom.value;
50381         
50382     },
50383
50384         // private
50385     onClick : function(e){ 
50386         //this.setChecked(!this.checked);
50387         Roo.get(e.target).toggleClass('x-menu-item-checked');
50388         this.refreshValue();
50389         //if(this.el.dom.checked != this.checked){
50390         //    this.setValue(this.el.dom.checked);
50391        // }
50392     },
50393     
50394     // private
50395     refreshValue : function()
50396     {
50397         var val = '';
50398         this.viewEl.select('img',true).each(function(e,i,n)  {
50399             val += e.is(".x-menu-item-checked") ? String(n) : '';
50400         });
50401         this.setValue(val, true);
50402     },
50403
50404     /**
50405      * Sets the checked state of the checkbox.
50406      * On is always based on a string comparison between inputValue and the param.
50407      * @param {Boolean/String} value - the value to set 
50408      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
50409      */
50410     setValue : function(v,suppressEvent){
50411         if (!this.el.dom) {
50412             return;
50413         }
50414         var old = this.el.dom.value ;
50415         this.el.dom.value = v;
50416         if (suppressEvent) {
50417             return ;
50418         }
50419          
50420         // update display..
50421         this.viewEl.select('img',true).each(function(e,i,n)  {
50422             
50423             var on = e.is(".x-menu-item-checked");
50424             var newv = v.indexOf(String(n)) > -1;
50425             if (on != newv) {
50426                 e.toggleClass('x-menu-item-checked');
50427             }
50428             
50429         });
50430         
50431         
50432         this.fireEvent('change', this, v, old);
50433         
50434         
50435     },
50436    
50437     // handle setting of hidden value by some other method!!?!?
50438     setFromHidden: function()
50439     {
50440         if(!this.el){
50441             return;
50442         }
50443         //console.log("SET FROM HIDDEN");
50444         //alert('setFrom hidden');
50445         this.setValue(this.el.dom.value);
50446     },
50447     
50448     onDestroy : function()
50449     {
50450         if(this.viewEl){
50451             Roo.get(this.viewEl).remove();
50452         }
50453          
50454         Roo.form.DayPicker.superclass.onDestroy.call(this);
50455     }
50456
50457 });/*
50458  * RooJS Library 1.1.1
50459  * Copyright(c) 2008-2011  Alan Knowles
50460  *
50461  * License - LGPL
50462  */
50463  
50464
50465 /**
50466  * @class Roo.form.ComboCheck
50467  * @extends Roo.form.ComboBox
50468  * A combobox for multiple select items.
50469  *
50470  * FIXME - could do with a reset button..
50471  * 
50472  * @constructor
50473  * Create a new ComboCheck
50474  * @param {Object} config Configuration options
50475  */
50476 Roo.form.ComboCheck = function(config){
50477     Roo.form.ComboCheck.superclass.constructor.call(this, config);
50478     // should verify some data...
50479     // like
50480     // hiddenName = required..
50481     // displayField = required
50482     // valudField == required
50483     var req= [ 'hiddenName', 'displayField', 'valueField' ];
50484     var _t = this;
50485     Roo.each(req, function(e) {
50486         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
50487             throw "Roo.form.ComboCheck : missing value for: " + e;
50488         }
50489     });
50490     
50491     
50492 };
50493
50494 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
50495      
50496      
50497     editable : false,
50498      
50499     selectedClass: 'x-menu-item-checked', 
50500     
50501     // private
50502     onRender : function(ct, position){
50503         var _t = this;
50504         
50505         
50506         
50507         if(!this.tpl){
50508             var cls = 'x-combo-list';
50509
50510             
50511             this.tpl =  new Roo.Template({
50512                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
50513                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
50514                    '<span>{' + this.displayField + '}</span>' +
50515                     '</div>' 
50516                 
50517             });
50518         }
50519  
50520         
50521         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
50522         this.view.singleSelect = false;
50523         this.view.multiSelect = true;
50524         this.view.toggleSelect = true;
50525         this.pageTb.add(new Roo.Toolbar.Fill(), {
50526             
50527             text: 'Done',
50528             handler: function()
50529             {
50530                 _t.collapse();
50531             }
50532         });
50533     },
50534     
50535     onViewOver : function(e, t){
50536         // do nothing...
50537         return;
50538         
50539     },
50540     
50541     onViewClick : function(doFocus,index){
50542         return;
50543         
50544     },
50545     select: function () {
50546         //Roo.log("SELECT CALLED");
50547     },
50548      
50549     selectByValue : function(xv, scrollIntoView){
50550         var ar = this.getValueArray();
50551         var sels = [];
50552         
50553         Roo.each(ar, function(v) {
50554             if(v === undefined || v === null){
50555                 return;
50556             }
50557             var r = this.findRecord(this.valueField, v);
50558             if(r){
50559                 sels.push(this.store.indexOf(r))
50560                 
50561             }
50562         },this);
50563         this.view.select(sels);
50564         return false;
50565     },
50566     
50567     
50568     
50569     onSelect : function(record, index){
50570        // Roo.log("onselect Called");
50571        // this is only called by the clear button now..
50572         this.view.clearSelections();
50573         this.setValue('[]');
50574         if (this.value != this.valueBefore) {
50575             this.fireEvent('change', this, this.value, this.valueBefore);
50576             this.valueBefore = this.value;
50577         }
50578     },
50579     getValueArray : function()
50580     {
50581         var ar = [] ;
50582         
50583         try {
50584             //Roo.log(this.value);
50585             if (typeof(this.value) == 'undefined') {
50586                 return [];
50587             }
50588             var ar = Roo.decode(this.value);
50589             return  ar instanceof Array ? ar : []; //?? valid?
50590             
50591         } catch(e) {
50592             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
50593             return [];
50594         }
50595          
50596     },
50597     expand : function ()
50598     {
50599         
50600         Roo.form.ComboCheck.superclass.expand.call(this);
50601         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
50602         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
50603         
50604
50605     },
50606     
50607     collapse : function(){
50608         Roo.form.ComboCheck.superclass.collapse.call(this);
50609         var sl = this.view.getSelectedIndexes();
50610         var st = this.store;
50611         var nv = [];
50612         var tv = [];
50613         var r;
50614         Roo.each(sl, function(i) {
50615             r = st.getAt(i);
50616             nv.push(r.get(this.valueField));
50617         },this);
50618         this.setValue(Roo.encode(nv));
50619         if (this.value != this.valueBefore) {
50620
50621             this.fireEvent('change', this, this.value, this.valueBefore);
50622             this.valueBefore = this.value;
50623         }
50624         
50625     },
50626     
50627     setValue : function(v){
50628         // Roo.log(v);
50629         this.value = v;
50630         
50631         var vals = this.getValueArray();
50632         var tv = [];
50633         Roo.each(vals, function(k) {
50634             var r = this.findRecord(this.valueField, k);
50635             if(r){
50636                 tv.push(r.data[this.displayField]);
50637             }else if(this.valueNotFoundText !== undefined){
50638                 tv.push( this.valueNotFoundText );
50639             }
50640         },this);
50641        // Roo.log(tv);
50642         
50643         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
50644         this.hiddenField.value = v;
50645         this.value = v;
50646     }
50647     
50648 });/*
50649  * Based on:
50650  * Ext JS Library 1.1.1
50651  * Copyright(c) 2006-2007, Ext JS, LLC.
50652  *
50653  * Originally Released Under LGPL - original licence link has changed is not relivant.
50654  *
50655  * Fork - LGPL
50656  * <script type="text/javascript">
50657  */
50658  
50659 /**
50660  * @class Roo.form.Signature
50661  * @extends Roo.form.Field
50662  * Signature field.  
50663  * @constructor
50664  * 
50665  * @param {Object} config Configuration options
50666  */
50667
50668 Roo.form.Signature = function(config){
50669     Roo.form.Signature.superclass.constructor.call(this, config);
50670     
50671     this.addEvents({// not in used??
50672          /**
50673          * @event confirm
50674          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
50675              * @param {Roo.form.Signature} combo This combo box
50676              */
50677         'confirm' : true,
50678         /**
50679          * @event reset
50680          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
50681              * @param {Roo.form.ComboBox} combo This combo box
50682              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
50683              */
50684         'reset' : true
50685     });
50686 };
50687
50688 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
50689     /**
50690      * @cfg {Object} labels Label to use when rendering a form.
50691      * defaults to 
50692      * labels : { 
50693      *      clear : "Clear",
50694      *      confirm : "Confirm"
50695      *  }
50696      */
50697     labels : { 
50698         clear : "Clear",
50699         confirm : "Confirm"
50700     },
50701     /**
50702      * @cfg {Number} width The signature panel width (defaults to 300)
50703      */
50704     width: 300,
50705     /**
50706      * @cfg {Number} height The signature panel height (defaults to 100)
50707      */
50708     height : 100,
50709     /**
50710      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
50711      */
50712     allowBlank : false,
50713     
50714     //private
50715     // {Object} signPanel The signature SVG panel element (defaults to {})
50716     signPanel : {},
50717     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
50718     isMouseDown : false,
50719     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
50720     isConfirmed : false,
50721     // {String} signatureTmp SVG mapping string (defaults to empty string)
50722     signatureTmp : '',
50723     
50724     
50725     defaultAutoCreate : { // modified by initCompnoent..
50726         tag: "input",
50727         type:"hidden"
50728     },
50729
50730     // private
50731     onRender : function(ct, position){
50732         
50733         Roo.form.Signature.superclass.onRender.call(this, ct, position);
50734         
50735         this.wrap = this.el.wrap({
50736             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
50737         });
50738         
50739         this.createToolbar(this);
50740         this.signPanel = this.wrap.createChild({
50741                 tag: 'div',
50742                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
50743             }, this.el
50744         );
50745             
50746         this.svgID = Roo.id();
50747         this.svgEl = this.signPanel.createChild({
50748               xmlns : 'http://www.w3.org/2000/svg',
50749               tag : 'svg',
50750               id : this.svgID + "-svg",
50751               width: this.width,
50752               height: this.height,
50753               viewBox: '0 0 '+this.width+' '+this.height,
50754               cn : [
50755                 {
50756                     tag: "rect",
50757                     id: this.svgID + "-svg-r",
50758                     width: this.width,
50759                     height: this.height,
50760                     fill: "#ffa"
50761                 },
50762                 {
50763                     tag: "line",
50764                     id: this.svgID + "-svg-l",
50765                     x1: "0", // start
50766                     y1: (this.height*0.8), // start set the line in 80% of height
50767                     x2: this.width, // end
50768                     y2: (this.height*0.8), // end set the line in 80% of height
50769                     'stroke': "#666",
50770                     'stroke-width': "1",
50771                     'stroke-dasharray': "3",
50772                     'shape-rendering': "crispEdges",
50773                     'pointer-events': "none"
50774                 },
50775                 {
50776                     tag: "path",
50777                     id: this.svgID + "-svg-p",
50778                     'stroke': "navy",
50779                     'stroke-width': "3",
50780                     'fill': "none",
50781                     'pointer-events': 'none'
50782                 }
50783               ]
50784         });
50785         this.createSVG();
50786         this.svgBox = this.svgEl.dom.getScreenCTM();
50787     },
50788     createSVG : function(){ 
50789         var svg = this.signPanel;
50790         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
50791         var t = this;
50792
50793         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
50794         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
50795         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
50796         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
50797         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
50798         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
50799         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
50800         
50801     },
50802     isTouchEvent : function(e){
50803         return e.type.match(/^touch/);
50804     },
50805     getCoords : function (e) {
50806         var pt    = this.svgEl.dom.createSVGPoint();
50807         pt.x = e.clientX; 
50808         pt.y = e.clientY;
50809         if (this.isTouchEvent(e)) {
50810             pt.x =  e.targetTouches[0].clientX;
50811             pt.y = e.targetTouches[0].clientY;
50812         }
50813         var a = this.svgEl.dom.getScreenCTM();
50814         var b = a.inverse();
50815         var mx = pt.matrixTransform(b);
50816         return mx.x + ',' + mx.y;
50817     },
50818     //mouse event headler 
50819     down : function (e) {
50820         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
50821         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
50822         
50823         this.isMouseDown = true;
50824         
50825         e.preventDefault();
50826     },
50827     move : function (e) {
50828         if (this.isMouseDown) {
50829             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
50830             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
50831         }
50832         
50833         e.preventDefault();
50834     },
50835     up : function (e) {
50836         this.isMouseDown = false;
50837         var sp = this.signatureTmp.split(' ');
50838         
50839         if(sp.length > 1){
50840             if(!sp[sp.length-2].match(/^L/)){
50841                 sp.pop();
50842                 sp.pop();
50843                 sp.push("");
50844                 this.signatureTmp = sp.join(" ");
50845             }
50846         }
50847         if(this.getValue() != this.signatureTmp){
50848             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50849             this.isConfirmed = false;
50850         }
50851         e.preventDefault();
50852     },
50853     
50854     /**
50855      * Protected method that will not generally be called directly. It
50856      * is called when the editor creates its toolbar. Override this method if you need to
50857      * add custom toolbar buttons.
50858      * @param {HtmlEditor} editor
50859      */
50860     createToolbar : function(editor){
50861          function btn(id, toggle, handler){
50862             var xid = fid + '-'+ id ;
50863             return {
50864                 id : xid,
50865                 cmd : id,
50866                 cls : 'x-btn-icon x-edit-'+id,
50867                 enableToggle:toggle !== false,
50868                 scope: editor, // was editor...
50869                 handler:handler||editor.relayBtnCmd,
50870                 clickEvent:'mousedown',
50871                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
50872                 tabIndex:-1
50873             };
50874         }
50875         
50876         
50877         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
50878         this.tb = tb;
50879         this.tb.add(
50880            {
50881                 cls : ' x-signature-btn x-signature-'+id,
50882                 scope: editor, // was editor...
50883                 handler: this.reset,
50884                 clickEvent:'mousedown',
50885                 text: this.labels.clear
50886             },
50887             {
50888                  xtype : 'Fill',
50889                  xns: Roo.Toolbar
50890             }, 
50891             {
50892                 cls : '  x-signature-btn x-signature-'+id,
50893                 scope: editor, // was editor...
50894                 handler: this.confirmHandler,
50895                 clickEvent:'mousedown',
50896                 text: this.labels.confirm
50897             }
50898         );
50899     
50900     },
50901     //public
50902     /**
50903      * when user is clicked confirm then show this image.....
50904      * 
50905      * @return {String} Image Data URI
50906      */
50907     getImageDataURI : function(){
50908         var svg = this.svgEl.dom.parentNode.innerHTML;
50909         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
50910         return src; 
50911     },
50912     /**
50913      * 
50914      * @return {Boolean} this.isConfirmed
50915      */
50916     getConfirmed : function(){
50917         return this.isConfirmed;
50918     },
50919     /**
50920      * 
50921      * @return {Number} this.width
50922      */
50923     getWidth : function(){
50924         return this.width;
50925     },
50926     /**
50927      * 
50928      * @return {Number} this.height
50929      */
50930     getHeight : function(){
50931         return this.height;
50932     },
50933     // private
50934     getSignature : function(){
50935         return this.signatureTmp;
50936     },
50937     // private
50938     reset : function(){
50939         this.signatureTmp = '';
50940         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50941         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
50942         this.isConfirmed = false;
50943         Roo.form.Signature.superclass.reset.call(this);
50944     },
50945     setSignature : function(s){
50946         this.signatureTmp = s;
50947         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
50948         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
50949         this.setValue(s);
50950         this.isConfirmed = false;
50951         Roo.form.Signature.superclass.reset.call(this);
50952     }, 
50953     test : function(){
50954 //        Roo.log(this.signPanel.dom.contentWindow.up())
50955     },
50956     //private
50957     setConfirmed : function(){
50958         
50959         
50960         
50961 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
50962     },
50963     // private
50964     confirmHandler : function(){
50965         if(!this.getSignature()){
50966             return;
50967         }
50968         
50969         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
50970         this.setValue(this.getSignature());
50971         this.isConfirmed = true;
50972         
50973         this.fireEvent('confirm', this);
50974     },
50975     // private
50976     // Subclasses should provide the validation implementation by overriding this
50977     validateValue : function(value){
50978         if(this.allowBlank){
50979             return true;
50980         }
50981         
50982         if(this.isConfirmed){
50983             return true;
50984         }
50985         return false;
50986     }
50987 });/*
50988  * Based on:
50989  * Ext JS Library 1.1.1
50990  * Copyright(c) 2006-2007, Ext JS, LLC.
50991  *
50992  * Originally Released Under LGPL - original licence link has changed is not relivant.
50993  *
50994  * Fork - LGPL
50995  * <script type="text/javascript">
50996  */
50997  
50998
50999 /**
51000  * @class Roo.form.ComboBox
51001  * @extends Roo.form.TriggerField
51002  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
51003  * @constructor
51004  * Create a new ComboBox.
51005  * @param {Object} config Configuration options
51006  */
51007 Roo.form.Select = function(config){
51008     Roo.form.Select.superclass.constructor.call(this, config);
51009      
51010 };
51011
51012 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
51013     /**
51014      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
51015      */
51016     /**
51017      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
51018      * rendering into an Roo.Editor, defaults to false)
51019      */
51020     /**
51021      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
51022      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
51023      */
51024     /**
51025      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
51026      */
51027     /**
51028      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
51029      * the dropdown list (defaults to undefined, with no header element)
51030      */
51031
51032      /**
51033      * @cfg {String/Roo.Template} tpl The template to use to render the output
51034      */
51035      
51036     // private
51037     defaultAutoCreate : {tag: "select"  },
51038     /**
51039      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
51040      */
51041     listWidth: undefined,
51042     /**
51043      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
51044      * mode = 'remote' or 'text' if mode = 'local')
51045      */
51046     displayField: undefined,
51047     /**
51048      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
51049      * mode = 'remote' or 'value' if mode = 'local'). 
51050      * Note: use of a valueField requires the user make a selection
51051      * in order for a value to be mapped.
51052      */
51053     valueField: undefined,
51054     
51055     
51056     /**
51057      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
51058      * field's data value (defaults to the underlying DOM element's name)
51059      */
51060     hiddenName: undefined,
51061     /**
51062      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
51063      */
51064     listClass: '',
51065     /**
51066      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
51067      */
51068     selectedClass: 'x-combo-selected',
51069     /**
51070      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
51071      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
51072      * which displays a downward arrow icon).
51073      */
51074     triggerClass : 'x-form-arrow-trigger',
51075     /**
51076      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
51077      */
51078     shadow:'sides',
51079     /**
51080      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
51081      * anchor positions (defaults to 'tl-bl')
51082      */
51083     listAlign: 'tl-bl?',
51084     /**
51085      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
51086      */
51087     maxHeight: 300,
51088     /**
51089      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
51090      * query specified by the allQuery config option (defaults to 'query')
51091      */
51092     triggerAction: 'query',
51093     /**
51094      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
51095      * (defaults to 4, does not apply if editable = false)
51096      */
51097     minChars : 4,
51098     /**
51099      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
51100      * delay (typeAheadDelay) if it matches a known value (defaults to false)
51101      */
51102     typeAhead: false,
51103     /**
51104      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
51105      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
51106      */
51107     queryDelay: 500,
51108     /**
51109      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
51110      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
51111      */
51112     pageSize: 0,
51113     /**
51114      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
51115      * when editable = true (defaults to false)
51116      */
51117     selectOnFocus:false,
51118     /**
51119      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
51120      */
51121     queryParam: 'query',
51122     /**
51123      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
51124      * when mode = 'remote' (defaults to 'Loading...')
51125      */
51126     loadingText: 'Loading...',
51127     /**
51128      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
51129      */
51130     resizable: false,
51131     /**
51132      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
51133      */
51134     handleHeight : 8,
51135     /**
51136      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
51137      * traditional select (defaults to true)
51138      */
51139     editable: true,
51140     /**
51141      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
51142      */
51143     allQuery: '',
51144     /**
51145      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
51146      */
51147     mode: 'remote',
51148     /**
51149      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
51150      * listWidth has a higher value)
51151      */
51152     minListWidth : 70,
51153     /**
51154      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
51155      * allow the user to set arbitrary text into the field (defaults to false)
51156      */
51157     forceSelection:false,
51158     /**
51159      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
51160      * if typeAhead = true (defaults to 250)
51161      */
51162     typeAheadDelay : 250,
51163     /**
51164      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
51165      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
51166      */
51167     valueNotFoundText : undefined,
51168     
51169     /**
51170      * @cfg {String} defaultValue The value displayed after loading the store.
51171      */
51172     defaultValue: '',
51173     
51174     /**
51175      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
51176      */
51177     blockFocus : false,
51178     
51179     /**
51180      * @cfg {Boolean} disableClear Disable showing of clear button.
51181      */
51182     disableClear : false,
51183     /**
51184      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
51185      */
51186     alwaysQuery : false,
51187     
51188     //private
51189     addicon : false,
51190     editicon: false,
51191     
51192     // element that contains real text value.. (when hidden is used..)
51193      
51194     // private
51195     onRender : function(ct, position){
51196         Roo.form.Field.prototype.onRender.call(this, ct, position);
51197         
51198         if(this.store){
51199             this.store.on('beforeload', this.onBeforeLoad, this);
51200             this.store.on('load', this.onLoad, this);
51201             this.store.on('loadexception', this.onLoadException, this);
51202             this.store.load({});
51203         }
51204         
51205         
51206         
51207     },
51208
51209     // private
51210     initEvents : function(){
51211         //Roo.form.ComboBox.superclass.initEvents.call(this);
51212  
51213     },
51214
51215     onDestroy : function(){
51216        
51217         if(this.store){
51218             this.store.un('beforeload', this.onBeforeLoad, this);
51219             this.store.un('load', this.onLoad, this);
51220             this.store.un('loadexception', this.onLoadException, this);
51221         }
51222         //Roo.form.ComboBox.superclass.onDestroy.call(this);
51223     },
51224
51225     // private
51226     fireKey : function(e){
51227         if(e.isNavKeyPress() && !this.list.isVisible()){
51228             this.fireEvent("specialkey", this, e);
51229         }
51230     },
51231
51232     // private
51233     onResize: function(w, h){
51234         
51235         return; 
51236     
51237         
51238     },
51239
51240     /**
51241      * Allow or prevent the user from directly editing the field text.  If false is passed,
51242      * the user will only be able to select from the items defined in the dropdown list.  This method
51243      * is the runtime equivalent of setting the 'editable' config option at config time.
51244      * @param {Boolean} value True to allow the user to directly edit the field text
51245      */
51246     setEditable : function(value){
51247          
51248     },
51249
51250     // private
51251     onBeforeLoad : function(){
51252         
51253         Roo.log("Select before load");
51254         return;
51255     
51256         this.innerList.update(this.loadingText ?
51257                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
51258         //this.restrictHeight();
51259         this.selectedIndex = -1;
51260     },
51261
51262     // private
51263     onLoad : function(){
51264
51265     
51266         var dom = this.el.dom;
51267         dom.innerHTML = '';
51268          var od = dom.ownerDocument;
51269          
51270         if (this.emptyText) {
51271             var op = od.createElement('option');
51272             op.setAttribute('value', '');
51273             op.innerHTML = String.format('{0}', this.emptyText);
51274             dom.appendChild(op);
51275         }
51276         if(this.store.getCount() > 0){
51277            
51278             var vf = this.valueField;
51279             var df = this.displayField;
51280             this.store.data.each(function(r) {
51281                 // which colmsn to use... testing - cdoe / title..
51282                 var op = od.createElement('option');
51283                 op.setAttribute('value', r.data[vf]);
51284                 op.innerHTML = String.format('{0}', r.data[df]);
51285                 dom.appendChild(op);
51286             });
51287             if (typeof(this.defaultValue != 'undefined')) {
51288                 this.setValue(this.defaultValue);
51289             }
51290             
51291              
51292         }else{
51293             //this.onEmptyResults();
51294         }
51295         //this.el.focus();
51296     },
51297     // private
51298     onLoadException : function()
51299     {
51300         dom.innerHTML = '';
51301             
51302         Roo.log("Select on load exception");
51303         return;
51304     
51305         this.collapse();
51306         Roo.log(this.store.reader.jsonData);
51307         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
51308             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
51309         }
51310         
51311         
51312     },
51313     // private
51314     onTypeAhead : function(){
51315          
51316     },
51317
51318     // private
51319     onSelect : function(record, index){
51320         Roo.log('on select?');
51321         return;
51322         if(this.fireEvent('beforeselect', this, record, index) !== false){
51323             this.setFromData(index > -1 ? record.data : false);
51324             this.collapse();
51325             this.fireEvent('select', this, record, index);
51326         }
51327     },
51328
51329     /**
51330      * Returns the currently selected field value or empty string if no value is set.
51331      * @return {String} value The selected value
51332      */
51333     getValue : function(){
51334         var dom = this.el.dom;
51335         this.value = dom.options[dom.selectedIndex].value;
51336         return this.value;
51337         
51338     },
51339
51340     /**
51341      * Clears any text/value currently set in the field
51342      */
51343     clearValue : function(){
51344         this.value = '';
51345         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
51346         
51347     },
51348
51349     /**
51350      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
51351      * will be displayed in the field.  If the value does not match the data value of an existing item,
51352      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
51353      * Otherwise the field will be blank (although the value will still be set).
51354      * @param {String} value The value to match
51355      */
51356     setValue : function(v){
51357         var d = this.el.dom;
51358         for (var i =0; i < d.options.length;i++) {
51359             if (v == d.options[i].value) {
51360                 d.selectedIndex = i;
51361                 this.value = v;
51362                 return;
51363             }
51364         }
51365         this.clearValue();
51366     },
51367     /**
51368      * @property {Object} the last set data for the element
51369      */
51370     
51371     lastData : false,
51372     /**
51373      * Sets the value of the field based on a object which is related to the record format for the store.
51374      * @param {Object} value the value to set as. or false on reset?
51375      */
51376     setFromData : function(o){
51377         Roo.log('setfrom data?');
51378          
51379         
51380         
51381     },
51382     // private
51383     reset : function(){
51384         this.clearValue();
51385     },
51386     // private
51387     findRecord : function(prop, value){
51388         
51389         return false;
51390     
51391         var record;
51392         if(this.store.getCount() > 0){
51393             this.store.each(function(r){
51394                 if(r.data[prop] == value){
51395                     record = r;
51396                     return false;
51397                 }
51398                 return true;
51399             });
51400         }
51401         return record;
51402     },
51403     
51404     getName: function()
51405     {
51406         // returns hidden if it's set..
51407         if (!this.rendered) {return ''};
51408         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
51409         
51410     },
51411      
51412
51413     
51414
51415     // private
51416     onEmptyResults : function(){
51417         Roo.log('empty results');
51418         //this.collapse();
51419     },
51420
51421     /**
51422      * Returns true if the dropdown list is expanded, else false.
51423      */
51424     isExpanded : function(){
51425         return false;
51426     },
51427
51428     /**
51429      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
51430      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51431      * @param {String} value The data value of the item to select
51432      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51433      * selected item if it is not currently in view (defaults to true)
51434      * @return {Boolean} True if the value matched an item in the list, else false
51435      */
51436     selectByValue : function(v, scrollIntoView){
51437         Roo.log('select By Value');
51438         return false;
51439     
51440         if(v !== undefined && v !== null){
51441             var r = this.findRecord(this.valueField || this.displayField, v);
51442             if(r){
51443                 this.select(this.store.indexOf(r), scrollIntoView);
51444                 return true;
51445             }
51446         }
51447         return false;
51448     },
51449
51450     /**
51451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
51452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
51453      * @param {Number} index The zero-based index of the list item to select
51454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
51455      * selected item if it is not currently in view (defaults to true)
51456      */
51457     select : function(index, scrollIntoView){
51458         Roo.log('select ');
51459         return  ;
51460         
51461         this.selectedIndex = index;
51462         this.view.select(index);
51463         if(scrollIntoView !== false){
51464             var el = this.view.getNode(index);
51465             if(el){
51466                 this.innerList.scrollChildIntoView(el, false);
51467             }
51468         }
51469     },
51470
51471       
51472
51473     // private
51474     validateBlur : function(){
51475         
51476         return;
51477         
51478     },
51479
51480     // private
51481     initQuery : function(){
51482         this.doQuery(this.getRawValue());
51483     },
51484
51485     // private
51486     doForce : function(){
51487         if(this.el.dom.value.length > 0){
51488             this.el.dom.value =
51489                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
51490              
51491         }
51492     },
51493
51494     /**
51495      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
51496      * query allowing the query action to be canceled if needed.
51497      * @param {String} query The SQL query to execute
51498      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
51499      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
51500      * saved in the current store (defaults to false)
51501      */
51502     doQuery : function(q, forceAll){
51503         
51504         Roo.log('doQuery?');
51505         if(q === undefined || q === null){
51506             q = '';
51507         }
51508         var qe = {
51509             query: q,
51510             forceAll: forceAll,
51511             combo: this,
51512             cancel:false
51513         };
51514         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
51515             return false;
51516         }
51517         q = qe.query;
51518         forceAll = qe.forceAll;
51519         if(forceAll === true || (q.length >= this.minChars)){
51520             if(this.lastQuery != q || this.alwaysQuery){
51521                 this.lastQuery = q;
51522                 if(this.mode == 'local'){
51523                     this.selectedIndex = -1;
51524                     if(forceAll){
51525                         this.store.clearFilter();
51526                     }else{
51527                         this.store.filter(this.displayField, q);
51528                     }
51529                     this.onLoad();
51530                 }else{
51531                     this.store.baseParams[this.queryParam] = q;
51532                     this.store.load({
51533                         params: this.getParams(q)
51534                     });
51535                     this.expand();
51536                 }
51537             }else{
51538                 this.selectedIndex = -1;
51539                 this.onLoad();   
51540             }
51541         }
51542     },
51543
51544     // private
51545     getParams : function(q){
51546         var p = {};
51547         //p[this.queryParam] = q;
51548         if(this.pageSize){
51549             p.start = 0;
51550             p.limit = this.pageSize;
51551         }
51552         return p;
51553     },
51554
51555     /**
51556      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
51557      */
51558     collapse : function(){
51559         
51560     },
51561
51562     // private
51563     collapseIf : function(e){
51564         
51565     },
51566
51567     /**
51568      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
51569      */
51570     expand : function(){
51571         
51572     } ,
51573
51574     // private
51575      
51576
51577     /** 
51578     * @cfg {Boolean} grow 
51579     * @hide 
51580     */
51581     /** 
51582     * @cfg {Number} growMin 
51583     * @hide 
51584     */
51585     /** 
51586     * @cfg {Number} growMax 
51587     * @hide 
51588     */
51589     /**
51590      * @hide
51591      * @method autoSize
51592      */
51593     
51594     setWidth : function()
51595     {
51596         
51597     },
51598     getResizeEl : function(){
51599         return this.el;
51600     }
51601 });//<script type="text/javasscript">
51602  
51603
51604 /**
51605  * @class Roo.DDView
51606  * A DnD enabled version of Roo.View.
51607  * @param {Element/String} container The Element in which to create the View.
51608  * @param {String} tpl The template string used to create the markup for each element of the View
51609  * @param {Object} config The configuration properties. These include all the config options of
51610  * {@link Roo.View} plus some specific to this class.<br>
51611  * <p>
51612  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
51613  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
51614  * <p>
51615  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
51616 .x-view-drag-insert-above {
51617         border-top:1px dotted #3366cc;
51618 }
51619 .x-view-drag-insert-below {
51620         border-bottom:1px dotted #3366cc;
51621 }
51622 </code></pre>
51623  * 
51624  */
51625  
51626 Roo.DDView = function(container, tpl, config) {
51627     Roo.DDView.superclass.constructor.apply(this, arguments);
51628     this.getEl().setStyle("outline", "0px none");
51629     this.getEl().unselectable();
51630     if (this.dragGroup) {
51631         this.setDraggable(this.dragGroup.split(","));
51632     }
51633     if (this.dropGroup) {
51634         this.setDroppable(this.dropGroup.split(","));
51635     }
51636     if (this.deletable) {
51637         this.setDeletable();
51638     }
51639     this.isDirtyFlag = false;
51640         this.addEvents({
51641                 "drop" : true
51642         });
51643 };
51644
51645 Roo.extend(Roo.DDView, Roo.View, {
51646 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
51647 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
51648 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
51649 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
51650
51651         isFormField: true,
51652
51653         reset: Roo.emptyFn,
51654         
51655         clearInvalid: Roo.form.Field.prototype.clearInvalid,
51656
51657         validate: function() {
51658                 return true;
51659         },
51660         
51661         destroy: function() {
51662                 this.purgeListeners();
51663                 this.getEl.removeAllListeners();
51664                 this.getEl().remove();
51665                 if (this.dragZone) {
51666                         if (this.dragZone.destroy) {
51667                                 this.dragZone.destroy();
51668                         }
51669                 }
51670                 if (this.dropZone) {
51671                         if (this.dropZone.destroy) {
51672                                 this.dropZone.destroy();
51673                         }
51674                 }
51675         },
51676
51677 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
51678         getName: function() {
51679                 return this.name;
51680         },
51681
51682 /**     Loads the View from a JSON string representing the Records to put into the Store. */
51683         setValue: function(v) {
51684                 if (!this.store) {
51685                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
51686                 }
51687                 var data = {};
51688                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
51689                 this.store.proxy = new Roo.data.MemoryProxy(data);
51690                 this.store.load();
51691         },
51692
51693 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
51694         getValue: function() {
51695                 var result = '(';
51696                 this.store.each(function(rec) {
51697                         result += rec.id + ',';
51698                 });
51699                 return result.substr(0, result.length - 1) + ')';
51700         },
51701         
51702         getIds: function() {
51703                 var i = 0, result = new Array(this.store.getCount());
51704                 this.store.each(function(rec) {
51705                         result[i++] = rec.id;
51706                 });
51707                 return result;
51708         },
51709         
51710         isDirty: function() {
51711                 return this.isDirtyFlag;
51712         },
51713
51714 /**
51715  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
51716  *      whole Element becomes the target, and this causes the drop gesture to append.
51717  */
51718     getTargetFromEvent : function(e) {
51719                 var target = e.getTarget();
51720                 while ((target !== null) && (target.parentNode != this.el.dom)) {
51721                 target = target.parentNode;
51722                 }
51723                 if (!target) {
51724                         target = this.el.dom.lastChild || this.el.dom;
51725                 }
51726                 return target;
51727     },
51728
51729 /**
51730  *      Create the drag data which consists of an object which has the property "ddel" as
51731  *      the drag proxy element. 
51732  */
51733     getDragData : function(e) {
51734         var target = this.findItemFromChild(e.getTarget());
51735                 if(target) {
51736                         this.handleSelection(e);
51737                         var selNodes = this.getSelectedNodes();
51738             var dragData = {
51739                 source: this,
51740                 copy: this.copy || (this.allowCopy && e.ctrlKey),
51741                 nodes: selNodes,
51742                 records: []
51743                         };
51744                         var selectedIndices = this.getSelectedIndexes();
51745                         for (var i = 0; i < selectedIndices.length; i++) {
51746                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
51747                         }
51748                         if (selNodes.length == 1) {
51749                                 dragData.ddel = target.cloneNode(true); // the div element
51750                         } else {
51751                                 var div = document.createElement('div'); // create the multi element drag "ghost"
51752                                 div.className = 'multi-proxy';
51753                                 for (var i = 0, len = selNodes.length; i < len; i++) {
51754                                         div.appendChild(selNodes[i].cloneNode(true));
51755                                 }
51756                                 dragData.ddel = div;
51757                         }
51758             //console.log(dragData)
51759             //console.log(dragData.ddel.innerHTML)
51760                         return dragData;
51761                 }
51762         //console.log('nodragData')
51763                 return false;
51764     },
51765     
51766 /**     Specify to which ddGroup items in this DDView may be dragged. */
51767     setDraggable: function(ddGroup) {
51768         if (ddGroup instanceof Array) {
51769                 Roo.each(ddGroup, this.setDraggable, this);
51770                 return;
51771         }
51772         if (this.dragZone) {
51773                 this.dragZone.addToGroup(ddGroup);
51774         } else {
51775                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
51776                                 containerScroll: true,
51777                                 ddGroup: ddGroup 
51778
51779                         });
51780 //                      Draggability implies selection. DragZone's mousedown selects the element.
51781                         if (!this.multiSelect) { this.singleSelect = true; }
51782
51783 //                      Wire the DragZone's handlers up to methods in *this*
51784                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
51785                 }
51786     },
51787
51788 /**     Specify from which ddGroup this DDView accepts drops. */
51789     setDroppable: function(ddGroup) {
51790         if (ddGroup instanceof Array) {
51791                 Roo.each(ddGroup, this.setDroppable, this);
51792                 return;
51793         }
51794         if (this.dropZone) {
51795                 this.dropZone.addToGroup(ddGroup);
51796         } else {
51797                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
51798                                 containerScroll: true,
51799                                 ddGroup: ddGroup
51800                         });
51801
51802 //                      Wire the DropZone's handlers up to methods in *this*
51803                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
51804                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
51805                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
51806                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
51807                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
51808                 }
51809     },
51810
51811 /**     Decide whether to drop above or below a View node. */
51812     getDropPoint : function(e, n, dd){
51813         if (n == this.el.dom) { return "above"; }
51814                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
51815                 var c = t + (b - t) / 2;
51816                 var y = Roo.lib.Event.getPageY(e);
51817                 if(y <= c) {
51818                         return "above";
51819                 }else{
51820                         return "below";
51821                 }
51822     },
51823
51824     onNodeEnter : function(n, dd, e, data){
51825                 return false;
51826     },
51827     
51828     onNodeOver : function(n, dd, e, data){
51829                 var pt = this.getDropPoint(e, n, dd);
51830                 // set the insert point style on the target node
51831                 var dragElClass = this.dropNotAllowed;
51832                 if (pt) {
51833                         var targetElClass;
51834                         if (pt == "above"){
51835                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
51836                                 targetElClass = "x-view-drag-insert-above";
51837                         } else {
51838                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
51839                                 targetElClass = "x-view-drag-insert-below";
51840                         }
51841                         if (this.lastInsertClass != targetElClass){
51842                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
51843                                 this.lastInsertClass = targetElClass;
51844                         }
51845                 }
51846                 return dragElClass;
51847         },
51848
51849     onNodeOut : function(n, dd, e, data){
51850                 this.removeDropIndicators(n);
51851     },
51852
51853     onNodeDrop : function(n, dd, e, data){
51854         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
51855                 return false;
51856         }
51857         var pt = this.getDropPoint(e, n, dd);
51858                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
51859                 if (pt == "below") { insertAt++; }
51860                 for (var i = 0; i < data.records.length; i++) {
51861                         var r = data.records[i];
51862                         var dup = this.store.getById(r.id);
51863                         if (dup && (dd != this.dragZone)) {
51864                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
51865                         } else {
51866                                 if (data.copy) {
51867                                         this.store.insert(insertAt++, r.copy());
51868                                 } else {
51869                                         data.source.isDirtyFlag = true;
51870                                         r.store.remove(r);
51871                                         this.store.insert(insertAt++, r);
51872                                 }
51873                                 this.isDirtyFlag = true;
51874                         }
51875                 }
51876                 this.dragZone.cachedTarget = null;
51877                 return true;
51878     },
51879
51880     removeDropIndicators : function(n){
51881                 if(n){
51882                         Roo.fly(n).removeClass([
51883                                 "x-view-drag-insert-above",
51884                                 "x-view-drag-insert-below"]);
51885                         this.lastInsertClass = "_noclass";
51886                 }
51887     },
51888
51889 /**
51890  *      Utility method. Add a delete option to the DDView's context menu.
51891  *      @param {String} imageUrl The URL of the "delete" icon image.
51892  */
51893         setDeletable: function(imageUrl) {
51894                 if (!this.singleSelect && !this.multiSelect) {
51895                         this.singleSelect = true;
51896                 }
51897                 var c = this.getContextMenu();
51898                 this.contextMenu.on("itemclick", function(item) {
51899                         switch (item.id) {
51900                                 case "delete":
51901                                         this.remove(this.getSelectedIndexes());
51902                                         break;
51903                         }
51904                 }, this);
51905                 this.contextMenu.add({
51906                         icon: imageUrl,
51907                         id: "delete",
51908                         text: 'Delete'
51909                 });
51910         },
51911         
51912 /**     Return the context menu for this DDView. */
51913         getContextMenu: function() {
51914                 if (!this.contextMenu) {
51915 //                      Create the View's context menu
51916                         this.contextMenu = new Roo.menu.Menu({
51917                                 id: this.id + "-contextmenu"
51918                         });
51919                         this.el.on("contextmenu", this.showContextMenu, this);
51920                 }
51921                 return this.contextMenu;
51922         },
51923         
51924         disableContextMenu: function() {
51925                 if (this.contextMenu) {
51926                         this.el.un("contextmenu", this.showContextMenu, this);
51927                 }
51928         },
51929
51930         showContextMenu: function(e, item) {
51931         item = this.findItemFromChild(e.getTarget());
51932                 if (item) {
51933                         e.stopEvent();
51934                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
51935                         this.contextMenu.showAt(e.getXY());
51936             }
51937     },
51938
51939 /**
51940  *      Remove {@link Roo.data.Record}s at the specified indices.
51941  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
51942  */
51943     remove: function(selectedIndices) {
51944                 selectedIndices = [].concat(selectedIndices);
51945                 for (var i = 0; i < selectedIndices.length; i++) {
51946                         var rec = this.store.getAt(selectedIndices[i]);
51947                         this.store.remove(rec);
51948                 }
51949     },
51950
51951 /**
51952  *      Double click fires the event, but also, if this is draggable, and there is only one other
51953  *      related DropZone, it transfers the selected node.
51954  */
51955     onDblClick : function(e){
51956         var item = this.findItemFromChild(e.getTarget());
51957         if(item){
51958             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
51959                 return false;
51960             }
51961             if (this.dragGroup) {
51962                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
51963                     while (targets.indexOf(this.dropZone) > -1) {
51964                             targets.remove(this.dropZone);
51965                                 }
51966                     if (targets.length == 1) {
51967                                         this.dragZone.cachedTarget = null;
51968                         var el = Roo.get(targets[0].getEl());
51969                         var box = el.getBox(true);
51970                         targets[0].onNodeDrop(el.dom, {
51971                                 target: el.dom,
51972                                 xy: [box.x, box.y + box.height - 1]
51973                         }, null, this.getDragData(e));
51974                     }
51975                 }
51976         }
51977     },
51978     
51979     handleSelection: function(e) {
51980                 this.dragZone.cachedTarget = null;
51981         var item = this.findItemFromChild(e.getTarget());
51982         if (!item) {
51983                 this.clearSelections(true);
51984                 return;
51985         }
51986                 if (item && (this.multiSelect || this.singleSelect)){
51987                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
51988                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
51989                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
51990                                 this.unselect(item);
51991                         } else {
51992                                 this.select(item, this.multiSelect && e.ctrlKey);
51993                                 this.lastSelection = item;
51994                         }
51995                 }
51996     },
51997
51998     onItemClick : function(item, index, e){
51999                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
52000                         return false;
52001                 }
52002                 return true;
52003     },
52004
52005     unselect : function(nodeInfo, suppressEvent){
52006                 var node = this.getNode(nodeInfo);
52007                 if(node && this.isSelected(node)){
52008                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
52009                                 Roo.fly(node).removeClass(this.selectedClass);
52010                                 this.selections.remove(node);
52011                                 if(!suppressEvent){
52012                                         this.fireEvent("selectionchange", this, this.selections);
52013                                 }
52014                         }
52015                 }
52016     }
52017 });
52018 /*
52019  * Based on:
52020  * Ext JS Library 1.1.1
52021  * Copyright(c) 2006-2007, Ext JS, LLC.
52022  *
52023  * Originally Released Under LGPL - original licence link has changed is not relivant.
52024  *
52025  * Fork - LGPL
52026  * <script type="text/javascript">
52027  */
52028  
52029 /**
52030  * @class Roo.LayoutManager
52031  * @extends Roo.util.Observable
52032  * Base class for layout managers.
52033  */
52034 Roo.LayoutManager = function(container, config){
52035     Roo.LayoutManager.superclass.constructor.call(this);
52036     this.el = Roo.get(container);
52037     // ie scrollbar fix
52038     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
52039         document.body.scroll = "no";
52040     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
52041         this.el.position('relative');
52042     }
52043     this.id = this.el.id;
52044     this.el.addClass("x-layout-container");
52045     /** false to disable window resize monitoring @type Boolean */
52046     this.monitorWindowResize = true;
52047     this.regions = {};
52048     this.addEvents({
52049         /**
52050          * @event layout
52051          * Fires when a layout is performed. 
52052          * @param {Roo.LayoutManager} this
52053          */
52054         "layout" : true,
52055         /**
52056          * @event regionresized
52057          * Fires when the user resizes a region. 
52058          * @param {Roo.LayoutRegion} region The resized region
52059          * @param {Number} newSize The new size (width for east/west, height for north/south)
52060          */
52061         "regionresized" : true,
52062         /**
52063          * @event regioncollapsed
52064          * Fires when a region is collapsed. 
52065          * @param {Roo.LayoutRegion} region The collapsed region
52066          */
52067         "regioncollapsed" : true,
52068         /**
52069          * @event regionexpanded
52070          * Fires when a region is expanded.  
52071          * @param {Roo.LayoutRegion} region The expanded region
52072          */
52073         "regionexpanded" : true
52074     });
52075     this.updating = false;
52076     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
52077 };
52078
52079 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
52080     /**
52081      * Returns true if this layout is currently being updated
52082      * @return {Boolean}
52083      */
52084     isUpdating : function(){
52085         return this.updating; 
52086     },
52087     
52088     /**
52089      * Suspend the LayoutManager from doing auto-layouts while
52090      * making multiple add or remove calls
52091      */
52092     beginUpdate : function(){
52093         this.updating = true;    
52094     },
52095     
52096     /**
52097      * Restore auto-layouts and optionally disable the manager from performing a layout
52098      * @param {Boolean} noLayout true to disable a layout update 
52099      */
52100     endUpdate : function(noLayout){
52101         this.updating = false;
52102         if(!noLayout){
52103             this.layout();
52104         }    
52105     },
52106     
52107     layout: function(){
52108         
52109     },
52110     
52111     onRegionResized : function(region, newSize){
52112         this.fireEvent("regionresized", region, newSize);
52113         this.layout();
52114     },
52115     
52116     onRegionCollapsed : function(region){
52117         this.fireEvent("regioncollapsed", region);
52118     },
52119     
52120     onRegionExpanded : function(region){
52121         this.fireEvent("regionexpanded", region);
52122     },
52123         
52124     /**
52125      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
52126      * performs box-model adjustments.
52127      * @return {Object} The size as an object {width: (the width), height: (the height)}
52128      */
52129     getViewSize : function(){
52130         var size;
52131         if(this.el.dom != document.body){
52132             size = this.el.getSize();
52133         }else{
52134             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
52135         }
52136         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
52137         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
52138         return size;
52139     },
52140     
52141     /**
52142      * Returns the Element this layout is bound to.
52143      * @return {Roo.Element}
52144      */
52145     getEl : function(){
52146         return this.el;
52147     },
52148     
52149     /**
52150      * Returns the specified region.
52151      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
52152      * @return {Roo.LayoutRegion}
52153      */
52154     getRegion : function(target){
52155         return this.regions[target.toLowerCase()];
52156     },
52157     
52158     onWindowResize : function(){
52159         if(this.monitorWindowResize){
52160             this.layout();
52161         }
52162     }
52163 });/*
52164  * Based on:
52165  * Ext JS Library 1.1.1
52166  * Copyright(c) 2006-2007, Ext JS, LLC.
52167  *
52168  * Originally Released Under LGPL - original licence link has changed is not relivant.
52169  *
52170  * Fork - LGPL
52171  * <script type="text/javascript">
52172  */
52173 /**
52174  * @class Roo.BorderLayout
52175  * @extends Roo.LayoutManager
52176  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
52177  * please see: <br><br>
52178  * <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>
52179  * <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>
52180  * Example:
52181  <pre><code>
52182  var layout = new Roo.BorderLayout(document.body, {
52183     north: {
52184         initialSize: 25,
52185         titlebar: false
52186     },
52187     west: {
52188         split:true,
52189         initialSize: 200,
52190         minSize: 175,
52191         maxSize: 400,
52192         titlebar: true,
52193         collapsible: true
52194     },
52195     east: {
52196         split:true,
52197         initialSize: 202,
52198         minSize: 175,
52199         maxSize: 400,
52200         titlebar: true,
52201         collapsible: true
52202     },
52203     south: {
52204         split:true,
52205         initialSize: 100,
52206         minSize: 100,
52207         maxSize: 200,
52208         titlebar: true,
52209         collapsible: true
52210     },
52211     center: {
52212         titlebar: true,
52213         autoScroll:true,
52214         resizeTabs: true,
52215         minTabWidth: 50,
52216         preferredTabWidth: 150
52217     }
52218 });
52219
52220 // shorthand
52221 var CP = Roo.ContentPanel;
52222
52223 layout.beginUpdate();
52224 layout.add("north", new CP("north", "North"));
52225 layout.add("south", new CP("south", {title: "South", closable: true}));
52226 layout.add("west", new CP("west", {title: "West"}));
52227 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
52228 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
52229 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
52230 layout.getRegion("center").showPanel("center1");
52231 layout.endUpdate();
52232 </code></pre>
52233
52234 <b>The container the layout is rendered into can be either the body element or any other element.
52235 If it is not the body element, the container needs to either be an absolute positioned element,
52236 or you will need to add "position:relative" to the css of the container.  You will also need to specify
52237 the container size if it is not the body element.</b>
52238
52239 * @constructor
52240 * Create a new BorderLayout
52241 * @param {String/HTMLElement/Element} container The container this layout is bound to
52242 * @param {Object} config Configuration options
52243  */
52244 Roo.BorderLayout = function(container, config){
52245     config = config || {};
52246     Roo.BorderLayout.superclass.constructor.call(this, container, config);
52247     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
52248     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
52249         var target = this.factory.validRegions[i];
52250         if(config[target]){
52251             this.addRegion(target, config[target]);
52252         }
52253     }
52254 };
52255
52256 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
52257     /**
52258      * Creates and adds a new region if it doesn't already exist.
52259      * @param {String} target The target region key (north, south, east, west or center).
52260      * @param {Object} config The regions config object
52261      * @return {BorderLayoutRegion} The new region
52262      */
52263     addRegion : function(target, config){
52264         if(!this.regions[target]){
52265             var r = this.factory.create(target, this, config);
52266             this.bindRegion(target, r);
52267         }
52268         return this.regions[target];
52269     },
52270
52271     // private (kinda)
52272     bindRegion : function(name, r){
52273         this.regions[name] = r;
52274         r.on("visibilitychange", this.layout, this);
52275         r.on("paneladded", this.layout, this);
52276         r.on("panelremoved", this.layout, this);
52277         r.on("invalidated", this.layout, this);
52278         r.on("resized", this.onRegionResized, this);
52279         r.on("collapsed", this.onRegionCollapsed, this);
52280         r.on("expanded", this.onRegionExpanded, this);
52281     },
52282
52283     /**
52284      * Performs a layout update.
52285      */
52286     layout : function(){
52287         if(this.updating) {
52288             return;
52289         }
52290         var size = this.getViewSize();
52291         var w = size.width;
52292         var h = size.height;
52293         var centerW = w;
52294         var centerH = h;
52295         var centerY = 0;
52296         var centerX = 0;
52297         //var x = 0, y = 0;
52298
52299         var rs = this.regions;
52300         var north = rs["north"];
52301         var south = rs["south"]; 
52302         var west = rs["west"];
52303         var east = rs["east"];
52304         var center = rs["center"];
52305         //if(this.hideOnLayout){ // not supported anymore
52306             //c.el.setStyle("display", "none");
52307         //}
52308         if(north && north.isVisible()){
52309             var b = north.getBox();
52310             var m = north.getMargins();
52311             b.width = w - (m.left+m.right);
52312             b.x = m.left;
52313             b.y = m.top;
52314             centerY = b.height + b.y + m.bottom;
52315             centerH -= centerY;
52316             north.updateBox(this.safeBox(b));
52317         }
52318         if(south && south.isVisible()){
52319             var b = south.getBox();
52320             var m = south.getMargins();
52321             b.width = w - (m.left+m.right);
52322             b.x = m.left;
52323             var totalHeight = (b.height + m.top + m.bottom);
52324             b.y = h - totalHeight + m.top;
52325             centerH -= totalHeight;
52326             south.updateBox(this.safeBox(b));
52327         }
52328         if(west && west.isVisible()){
52329             var b = west.getBox();
52330             var m = west.getMargins();
52331             b.height = centerH - (m.top+m.bottom);
52332             b.x = m.left;
52333             b.y = centerY + m.top;
52334             var totalWidth = (b.width + m.left + m.right);
52335             centerX += totalWidth;
52336             centerW -= totalWidth;
52337             west.updateBox(this.safeBox(b));
52338         }
52339         if(east && east.isVisible()){
52340             var b = east.getBox();
52341             var m = east.getMargins();
52342             b.height = centerH - (m.top+m.bottom);
52343             var totalWidth = (b.width + m.left + m.right);
52344             b.x = w - totalWidth + m.left;
52345             b.y = centerY + m.top;
52346             centerW -= totalWidth;
52347             east.updateBox(this.safeBox(b));
52348         }
52349         if(center){
52350             var m = center.getMargins();
52351             var centerBox = {
52352                 x: centerX + m.left,
52353                 y: centerY + m.top,
52354                 width: centerW - (m.left+m.right),
52355                 height: centerH - (m.top+m.bottom)
52356             };
52357             //if(this.hideOnLayout){
52358                 //center.el.setStyle("display", "block");
52359             //}
52360             center.updateBox(this.safeBox(centerBox));
52361         }
52362         this.el.repaint();
52363         this.fireEvent("layout", this);
52364     },
52365
52366     // private
52367     safeBox : function(box){
52368         box.width = Math.max(0, box.width);
52369         box.height = Math.max(0, box.height);
52370         return box;
52371     },
52372
52373     /**
52374      * Adds a ContentPanel (or subclass) to this layout.
52375      * @param {String} target The target region key (north, south, east, west or center).
52376      * @param {Roo.ContentPanel} panel The panel to add
52377      * @return {Roo.ContentPanel} The added panel
52378      */
52379     add : function(target, panel){
52380          
52381         target = target.toLowerCase();
52382         return this.regions[target].add(panel);
52383     },
52384
52385     /**
52386      * Remove a ContentPanel (or subclass) to this layout.
52387      * @param {String} target The target region key (north, south, east, west or center).
52388      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
52389      * @return {Roo.ContentPanel} The removed panel
52390      */
52391     remove : function(target, panel){
52392         target = target.toLowerCase();
52393         return this.regions[target].remove(panel);
52394     },
52395
52396     /**
52397      * Searches all regions for a panel with the specified id
52398      * @param {String} panelId
52399      * @return {Roo.ContentPanel} The panel or null if it wasn't found
52400      */
52401     findPanel : function(panelId){
52402         var rs = this.regions;
52403         for(var target in rs){
52404             if(typeof rs[target] != "function"){
52405                 var p = rs[target].getPanel(panelId);
52406                 if(p){
52407                     return p;
52408                 }
52409             }
52410         }
52411         return null;
52412     },
52413
52414     /**
52415      * Searches all regions for a panel with the specified id and activates (shows) it.
52416      * @param {String/ContentPanel} panelId The panels id or the panel itself
52417      * @return {Roo.ContentPanel} The shown panel or null
52418      */
52419     showPanel : function(panelId) {
52420       var rs = this.regions;
52421       for(var target in rs){
52422          var r = rs[target];
52423          if(typeof r != "function"){
52424             if(r.hasPanel(panelId)){
52425                return r.showPanel(panelId);
52426             }
52427          }
52428       }
52429       return null;
52430    },
52431
52432    /**
52433      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
52434      * @param {Roo.state.Provider} provider (optional) An alternate state provider
52435      */
52436     restoreState : function(provider){
52437         if(!provider){
52438             provider = Roo.state.Manager;
52439         }
52440         var sm = new Roo.LayoutStateManager();
52441         sm.init(this, provider);
52442     },
52443
52444     /**
52445      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
52446      * object should contain properties for each region to add ContentPanels to, and each property's value should be
52447      * a valid ContentPanel config object.  Example:
52448      * <pre><code>
52449 // Create the main layout
52450 var layout = new Roo.BorderLayout('main-ct', {
52451     west: {
52452         split:true,
52453         minSize: 175,
52454         titlebar: true
52455     },
52456     center: {
52457         title:'Components'
52458     }
52459 }, 'main-ct');
52460
52461 // Create and add multiple ContentPanels at once via configs
52462 layout.batchAdd({
52463    west: {
52464        id: 'source-files',
52465        autoCreate:true,
52466        title:'Ext Source Files',
52467        autoScroll:true,
52468        fitToFrame:true
52469    },
52470    center : {
52471        el: cview,
52472        autoScroll:true,
52473        fitToFrame:true,
52474        toolbar: tb,
52475        resizeEl:'cbody'
52476    }
52477 });
52478 </code></pre>
52479      * @param {Object} regions An object containing ContentPanel configs by region name
52480      */
52481     batchAdd : function(regions){
52482         this.beginUpdate();
52483         for(var rname in regions){
52484             var lr = this.regions[rname];
52485             if(lr){
52486                 this.addTypedPanels(lr, regions[rname]);
52487             }
52488         }
52489         this.endUpdate();
52490     },
52491
52492     // private
52493     addTypedPanels : function(lr, ps){
52494         if(typeof ps == 'string'){
52495             lr.add(new Roo.ContentPanel(ps));
52496         }
52497         else if(ps instanceof Array){
52498             for(var i =0, len = ps.length; i < len; i++){
52499                 this.addTypedPanels(lr, ps[i]);
52500             }
52501         }
52502         else if(!ps.events){ // raw config?
52503             var el = ps.el;
52504             delete ps.el; // prevent conflict
52505             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
52506         }
52507         else {  // panel object assumed!
52508             lr.add(ps);
52509         }
52510     },
52511     /**
52512      * Adds a xtype elements to the layout.
52513      * <pre><code>
52514
52515 layout.addxtype({
52516        xtype : 'ContentPanel',
52517        region: 'west',
52518        items: [ .... ]
52519    }
52520 );
52521
52522 layout.addxtype({
52523         xtype : 'NestedLayoutPanel',
52524         region: 'west',
52525         layout: {
52526            center: { },
52527            west: { }   
52528         },
52529         items : [ ... list of content panels or nested layout panels.. ]
52530    }
52531 );
52532 </code></pre>
52533      * @param {Object} cfg Xtype definition of item to add.
52534      */
52535     addxtype : function(cfg)
52536     {
52537         // basically accepts a pannel...
52538         // can accept a layout region..!?!?
52539         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
52540         
52541         if (!cfg.xtype.match(/Panel$/)) {
52542             return false;
52543         }
52544         var ret = false;
52545         
52546         if (typeof(cfg.region) == 'undefined') {
52547             Roo.log("Failed to add Panel, region was not set");
52548             Roo.log(cfg);
52549             return false;
52550         }
52551         var region = cfg.region;
52552         delete cfg.region;
52553         
52554           
52555         var xitems = [];
52556         if (cfg.items) {
52557             xitems = cfg.items;
52558             delete cfg.items;
52559         }
52560         var nb = false;
52561         
52562         switch(cfg.xtype) 
52563         {
52564             case 'ContentPanel':  // ContentPanel (el, cfg)
52565             case 'ScrollPanel':  // ContentPanel (el, cfg)
52566             case 'ViewPanel': 
52567                 if(cfg.autoCreate) {
52568                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52569                 } else {
52570                     var el = this.el.createChild();
52571                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
52572                 }
52573                 
52574                 this.add(region, ret);
52575                 break;
52576             
52577             
52578             case 'TreePanel': // our new panel!
52579                 cfg.el = this.el.createChild();
52580                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52581                 this.add(region, ret);
52582                 break;
52583             
52584             case 'NestedLayoutPanel': 
52585                 // create a new Layout (which is  a Border Layout...
52586                 var el = this.el.createChild();
52587                 var clayout = cfg.layout;
52588                 delete cfg.layout;
52589                 clayout.items   = clayout.items  || [];
52590                 // replace this exitems with the clayout ones..
52591                 xitems = clayout.items;
52592                  
52593                 
52594                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
52595                     cfg.background = false;
52596                 }
52597                 var layout = new Roo.BorderLayout(el, clayout);
52598                 
52599                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
52600                 //console.log('adding nested layout panel '  + cfg.toSource());
52601                 this.add(region, ret);
52602                 nb = {}; /// find first...
52603                 break;
52604                 
52605             case 'GridPanel': 
52606             
52607                 // needs grid and region
52608                 
52609                 //var el = this.getRegion(region).el.createChild();
52610                 var el = this.el.createChild();
52611                 // create the grid first...
52612                 
52613                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
52614                 delete cfg.grid;
52615                 if (region == 'center' && this.active ) {
52616                     cfg.background = false;
52617                 }
52618                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
52619                 
52620                 this.add(region, ret);
52621                 if (cfg.background) {
52622                     ret.on('activate', function(gp) {
52623                         if (!gp.grid.rendered) {
52624                             gp.grid.render();
52625                         }
52626                     });
52627                 } else {
52628                     grid.render();
52629                 }
52630                 break;
52631            
52632            
52633            
52634                 
52635                 
52636                 
52637             default:
52638                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
52639                     
52640                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
52641                     this.add(region, ret);
52642                 } else {
52643                 
52644                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
52645                     return null;
52646                 }
52647                 
52648              // GridPanel (grid, cfg)
52649             
52650         }
52651         this.beginUpdate();
52652         // add children..
52653         var region = '';
52654         var abn = {};
52655         Roo.each(xitems, function(i)  {
52656             region = nb && i.region ? i.region : false;
52657             
52658             var add = ret.addxtype(i);
52659            
52660             if (region) {
52661                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
52662                 if (!i.background) {
52663                     abn[region] = nb[region] ;
52664                 }
52665             }
52666             
52667         });
52668         this.endUpdate();
52669
52670         // make the last non-background panel active..
52671         //if (nb) { Roo.log(abn); }
52672         if (nb) {
52673             
52674             for(var r in abn) {
52675                 region = this.getRegion(r);
52676                 if (region) {
52677                     // tried using nb[r], but it does not work..
52678                      
52679                     region.showPanel(abn[r]);
52680                    
52681                 }
52682             }
52683         }
52684         return ret;
52685         
52686     }
52687 });
52688
52689 /**
52690  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
52691  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
52692  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
52693  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
52694  * <pre><code>
52695 // shorthand
52696 var CP = Roo.ContentPanel;
52697
52698 var layout = Roo.BorderLayout.create({
52699     north: {
52700         initialSize: 25,
52701         titlebar: false,
52702         panels: [new CP("north", "North")]
52703     },
52704     west: {
52705         split:true,
52706         initialSize: 200,
52707         minSize: 175,
52708         maxSize: 400,
52709         titlebar: true,
52710         collapsible: true,
52711         panels: [new CP("west", {title: "West"})]
52712     },
52713     east: {
52714         split:true,
52715         initialSize: 202,
52716         minSize: 175,
52717         maxSize: 400,
52718         titlebar: true,
52719         collapsible: true,
52720         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
52721     },
52722     south: {
52723         split:true,
52724         initialSize: 100,
52725         minSize: 100,
52726         maxSize: 200,
52727         titlebar: true,
52728         collapsible: true,
52729         panels: [new CP("south", {title: "South", closable: true})]
52730     },
52731     center: {
52732         titlebar: true,
52733         autoScroll:true,
52734         resizeTabs: true,
52735         minTabWidth: 50,
52736         preferredTabWidth: 150,
52737         panels: [
52738             new CP("center1", {title: "Close Me", closable: true}),
52739             new CP("center2", {title: "Center Panel", closable: false})
52740         ]
52741     }
52742 }, document.body);
52743
52744 layout.getRegion("center").showPanel("center1");
52745 </code></pre>
52746  * @param config
52747  * @param targetEl
52748  */
52749 Roo.BorderLayout.create = function(config, targetEl){
52750     var layout = new Roo.BorderLayout(targetEl || document.body, config);
52751     layout.beginUpdate();
52752     var regions = Roo.BorderLayout.RegionFactory.validRegions;
52753     for(var j = 0, jlen = regions.length; j < jlen; j++){
52754         var lr = regions[j];
52755         if(layout.regions[lr] && config[lr].panels){
52756             var r = layout.regions[lr];
52757             var ps = config[lr].panels;
52758             layout.addTypedPanels(r, ps);
52759         }
52760     }
52761     layout.endUpdate();
52762     return layout;
52763 };
52764
52765 // private
52766 Roo.BorderLayout.RegionFactory = {
52767     // private
52768     validRegions : ["north","south","east","west","center"],
52769
52770     // private
52771     create : function(target, mgr, config){
52772         target = target.toLowerCase();
52773         if(config.lightweight || config.basic){
52774             return new Roo.BasicLayoutRegion(mgr, config, target);
52775         }
52776         switch(target){
52777             case "north":
52778                 return new Roo.NorthLayoutRegion(mgr, config);
52779             case "south":
52780                 return new Roo.SouthLayoutRegion(mgr, config);
52781             case "east":
52782                 return new Roo.EastLayoutRegion(mgr, config);
52783             case "west":
52784                 return new Roo.WestLayoutRegion(mgr, config);
52785             case "center":
52786                 return new Roo.CenterLayoutRegion(mgr, config);
52787         }
52788         throw 'Layout region "'+target+'" not supported.';
52789     }
52790 };/*
52791  * Based on:
52792  * Ext JS Library 1.1.1
52793  * Copyright(c) 2006-2007, Ext JS, LLC.
52794  *
52795  * Originally Released Under LGPL - original licence link has changed is not relivant.
52796  *
52797  * Fork - LGPL
52798  * <script type="text/javascript">
52799  */
52800  
52801 /**
52802  * @class Roo.BasicLayoutRegion
52803  * @extends Roo.util.Observable
52804  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
52805  * and does not have a titlebar, tabs or any other features. All it does is size and position 
52806  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
52807  */
52808 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
52809     this.mgr = mgr;
52810     this.position  = pos;
52811     this.events = {
52812         /**
52813          * @scope Roo.BasicLayoutRegion
52814          */
52815         
52816         /**
52817          * @event beforeremove
52818          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
52819          * @param {Roo.LayoutRegion} this
52820          * @param {Roo.ContentPanel} panel The panel
52821          * @param {Object} e The cancel event object
52822          */
52823         "beforeremove" : true,
52824         /**
52825          * @event invalidated
52826          * Fires when the layout for this region is changed.
52827          * @param {Roo.LayoutRegion} this
52828          */
52829         "invalidated" : true,
52830         /**
52831          * @event visibilitychange
52832          * Fires when this region is shown or hidden 
52833          * @param {Roo.LayoutRegion} this
52834          * @param {Boolean} visibility true or false
52835          */
52836         "visibilitychange" : true,
52837         /**
52838          * @event paneladded
52839          * Fires when a panel is added. 
52840          * @param {Roo.LayoutRegion} this
52841          * @param {Roo.ContentPanel} panel The panel
52842          */
52843         "paneladded" : true,
52844         /**
52845          * @event panelremoved
52846          * Fires when a panel is removed. 
52847          * @param {Roo.LayoutRegion} this
52848          * @param {Roo.ContentPanel} panel The panel
52849          */
52850         "panelremoved" : true,
52851         /**
52852          * @event beforecollapse
52853          * Fires when this region before collapse.
52854          * @param {Roo.LayoutRegion} this
52855          */
52856         "beforecollapse" : true,
52857         /**
52858          * @event collapsed
52859          * Fires when this region is collapsed.
52860          * @param {Roo.LayoutRegion} this
52861          */
52862         "collapsed" : true,
52863         /**
52864          * @event expanded
52865          * Fires when this region is expanded.
52866          * @param {Roo.LayoutRegion} this
52867          */
52868         "expanded" : true,
52869         /**
52870          * @event slideshow
52871          * Fires when this region is slid into view.
52872          * @param {Roo.LayoutRegion} this
52873          */
52874         "slideshow" : true,
52875         /**
52876          * @event slidehide
52877          * Fires when this region slides out of view. 
52878          * @param {Roo.LayoutRegion} this
52879          */
52880         "slidehide" : true,
52881         /**
52882          * @event panelactivated
52883          * Fires when a panel is activated. 
52884          * @param {Roo.LayoutRegion} this
52885          * @param {Roo.ContentPanel} panel The activated panel
52886          */
52887         "panelactivated" : true,
52888         /**
52889          * @event resized
52890          * Fires when the user resizes this region. 
52891          * @param {Roo.LayoutRegion} this
52892          * @param {Number} newSize The new size (width for east/west, height for north/south)
52893          */
52894         "resized" : true
52895     };
52896     /** A collection of panels in this region. @type Roo.util.MixedCollection */
52897     this.panels = new Roo.util.MixedCollection();
52898     this.panels.getKey = this.getPanelId.createDelegate(this);
52899     this.box = null;
52900     this.activePanel = null;
52901     // ensure listeners are added...
52902     
52903     if (config.listeners || config.events) {
52904         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
52905             listeners : config.listeners || {},
52906             events : config.events || {}
52907         });
52908     }
52909     
52910     if(skipConfig !== true){
52911         this.applyConfig(config);
52912     }
52913 };
52914
52915 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
52916     getPanelId : function(p){
52917         return p.getId();
52918     },
52919     
52920     applyConfig : function(config){
52921         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
52922         this.config = config;
52923         
52924     },
52925     
52926     /**
52927      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
52928      * the width, for horizontal (north, south) the height.
52929      * @param {Number} newSize The new width or height
52930      */
52931     resizeTo : function(newSize){
52932         var el = this.el ? this.el :
52933                  (this.activePanel ? this.activePanel.getEl() : null);
52934         if(el){
52935             switch(this.position){
52936                 case "east":
52937                 case "west":
52938                     el.setWidth(newSize);
52939                     this.fireEvent("resized", this, newSize);
52940                 break;
52941                 case "north":
52942                 case "south":
52943                     el.setHeight(newSize);
52944                     this.fireEvent("resized", this, newSize);
52945                 break;                
52946             }
52947         }
52948     },
52949     
52950     getBox : function(){
52951         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
52952     },
52953     
52954     getMargins : function(){
52955         return this.margins;
52956     },
52957     
52958     updateBox : function(box){
52959         this.box = box;
52960         var el = this.activePanel.getEl();
52961         el.dom.style.left = box.x + "px";
52962         el.dom.style.top = box.y + "px";
52963         this.activePanel.setSize(box.width, box.height);
52964     },
52965     
52966     /**
52967      * Returns the container element for this region.
52968      * @return {Roo.Element}
52969      */
52970     getEl : function(){
52971         return this.activePanel;
52972     },
52973     
52974     /**
52975      * Returns true if this region is currently visible.
52976      * @return {Boolean}
52977      */
52978     isVisible : function(){
52979         return this.activePanel ? true : false;
52980     },
52981     
52982     setActivePanel : function(panel){
52983         panel = this.getPanel(panel);
52984         if(this.activePanel && this.activePanel != panel){
52985             this.activePanel.setActiveState(false);
52986             this.activePanel.getEl().setLeftTop(-10000,-10000);
52987         }
52988         this.activePanel = panel;
52989         panel.setActiveState(true);
52990         if(this.box){
52991             panel.setSize(this.box.width, this.box.height);
52992         }
52993         this.fireEvent("panelactivated", this, panel);
52994         this.fireEvent("invalidated");
52995     },
52996     
52997     /**
52998      * Show the specified panel.
52999      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
53000      * @return {Roo.ContentPanel} The shown panel or null
53001      */
53002     showPanel : function(panel){
53003         if(panel = this.getPanel(panel)){
53004             this.setActivePanel(panel);
53005         }
53006         return panel;
53007     },
53008     
53009     /**
53010      * Get the active panel for this region.
53011      * @return {Roo.ContentPanel} The active panel or null
53012      */
53013     getActivePanel : function(){
53014         return this.activePanel;
53015     },
53016     
53017     /**
53018      * Add the passed ContentPanel(s)
53019      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53020      * @return {Roo.ContentPanel} The panel added (if only one was added)
53021      */
53022     add : function(panel){
53023         if(arguments.length > 1){
53024             for(var i = 0, len = arguments.length; i < len; i++) {
53025                 this.add(arguments[i]);
53026             }
53027             return null;
53028         }
53029         if(this.hasPanel(panel)){
53030             this.showPanel(panel);
53031             return panel;
53032         }
53033         var el = panel.getEl();
53034         if(el.dom.parentNode != this.mgr.el.dom){
53035             this.mgr.el.dom.appendChild(el.dom);
53036         }
53037         if(panel.setRegion){
53038             panel.setRegion(this);
53039         }
53040         this.panels.add(panel);
53041         el.setStyle("position", "absolute");
53042         if(!panel.background){
53043             this.setActivePanel(panel);
53044             if(this.config.initialSize && this.panels.getCount()==1){
53045                 this.resizeTo(this.config.initialSize);
53046             }
53047         }
53048         this.fireEvent("paneladded", this, panel);
53049         return panel;
53050     },
53051     
53052     /**
53053      * Returns true if the panel is in this region.
53054      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53055      * @return {Boolean}
53056      */
53057     hasPanel : function(panel){
53058         if(typeof panel == "object"){ // must be panel obj
53059             panel = panel.getId();
53060         }
53061         return this.getPanel(panel) ? true : false;
53062     },
53063     
53064     /**
53065      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53066      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53067      * @param {Boolean} preservePanel Overrides the config preservePanel option
53068      * @return {Roo.ContentPanel} The panel that was removed
53069      */
53070     remove : function(panel, preservePanel){
53071         panel = this.getPanel(panel);
53072         if(!panel){
53073             return null;
53074         }
53075         var e = {};
53076         this.fireEvent("beforeremove", this, panel, e);
53077         if(e.cancel === true){
53078             return null;
53079         }
53080         var panelId = panel.getId();
53081         this.panels.removeKey(panelId);
53082         return panel;
53083     },
53084     
53085     /**
53086      * Returns the panel specified or null if it's not in this region.
53087      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
53088      * @return {Roo.ContentPanel}
53089      */
53090     getPanel : function(id){
53091         if(typeof id == "object"){ // must be panel obj
53092             return id;
53093         }
53094         return this.panels.get(id);
53095     },
53096     
53097     /**
53098      * Returns this regions position (north/south/east/west/center).
53099      * @return {String} 
53100      */
53101     getPosition: function(){
53102         return this.position;    
53103     }
53104 });/*
53105  * Based on:
53106  * Ext JS Library 1.1.1
53107  * Copyright(c) 2006-2007, Ext JS, LLC.
53108  *
53109  * Originally Released Under LGPL - original licence link has changed is not relivant.
53110  *
53111  * Fork - LGPL
53112  * <script type="text/javascript">
53113  */
53114  
53115 /**
53116  * @class Roo.LayoutRegion
53117  * @extends Roo.BasicLayoutRegion
53118  * This class represents a region in a layout manager.
53119  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
53120  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
53121  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
53122  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
53123  * @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})
53124  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
53125  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
53126  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
53127  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
53128  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
53129  * @cfg {String}    title           The title for the region (overrides panel titles)
53130  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
53131  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
53132  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
53133  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
53134  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
53135  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
53136  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
53137  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
53138  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
53139  * @cfg {Boolean}   showPin         True to show a pin button
53140  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
53141  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
53142  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
53143  * @cfg {Number}    width           For East/West panels
53144  * @cfg {Number}    height          For North/South panels
53145  * @cfg {Boolean}   split           To show the splitter
53146  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
53147  */
53148 Roo.LayoutRegion = function(mgr, config, pos){
53149     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
53150     var dh = Roo.DomHelper;
53151     /** This region's container element 
53152     * @type Roo.Element */
53153     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
53154     /** This region's title element 
53155     * @type Roo.Element */
53156
53157     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
53158         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
53159         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
53160     ]}, true);
53161     this.titleEl.enableDisplayMode();
53162     /** This region's title text element 
53163     * @type HTMLElement */
53164     this.titleTextEl = this.titleEl.dom.firstChild;
53165     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
53166     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
53167     this.closeBtn.enableDisplayMode();
53168     this.closeBtn.on("click", this.closeClicked, this);
53169     this.closeBtn.hide();
53170
53171     this.createBody(config);
53172     this.visible = true;
53173     this.collapsed = false;
53174
53175     if(config.hideWhenEmpty){
53176         this.hide();
53177         this.on("paneladded", this.validateVisibility, this);
53178         this.on("panelremoved", this.validateVisibility, this);
53179     }
53180     this.applyConfig(config);
53181 };
53182
53183 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
53184
53185     createBody : function(){
53186         /** This region's body element 
53187         * @type Roo.Element */
53188         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
53189     },
53190
53191     applyConfig : function(c){
53192         if(c.collapsible && this.position != "center" && !this.collapsedEl){
53193             var dh = Roo.DomHelper;
53194             if(c.titlebar !== false){
53195                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
53196                 this.collapseBtn.on("click", this.collapse, this);
53197                 this.collapseBtn.enableDisplayMode();
53198
53199                 if(c.showPin === true || this.showPin){
53200                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
53201                     this.stickBtn.enableDisplayMode();
53202                     this.stickBtn.on("click", this.expand, this);
53203                     this.stickBtn.hide();
53204                 }
53205             }
53206             /** This region's collapsed element
53207             * @type Roo.Element */
53208             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
53209                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
53210             ]}, true);
53211             if(c.floatable !== false){
53212                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
53213                this.collapsedEl.on("click", this.collapseClick, this);
53214             }
53215
53216             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
53217                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
53218                    id: "message", unselectable: "on", style:{"float":"left"}});
53219                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
53220              }
53221             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
53222             this.expandBtn.on("click", this.expand, this);
53223         }
53224         if(this.collapseBtn){
53225             this.collapseBtn.setVisible(c.collapsible == true);
53226         }
53227         this.cmargins = c.cmargins || this.cmargins ||
53228                          (this.position == "west" || this.position == "east" ?
53229                              {top: 0, left: 2, right:2, bottom: 0} :
53230                              {top: 2, left: 0, right:0, bottom: 2});
53231         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
53232         this.bottomTabs = c.tabPosition != "top";
53233         this.autoScroll = c.autoScroll || false;
53234         if(this.autoScroll){
53235             this.bodyEl.setStyle("overflow", "auto");
53236         }else{
53237             this.bodyEl.setStyle("overflow", "hidden");
53238         }
53239         //if(c.titlebar !== false){
53240             if((!c.titlebar && !c.title) || c.titlebar === false){
53241                 this.titleEl.hide();
53242             }else{
53243                 this.titleEl.show();
53244                 if(c.title){
53245                     this.titleTextEl.innerHTML = c.title;
53246                 }
53247             }
53248         //}
53249         this.duration = c.duration || .30;
53250         this.slideDuration = c.slideDuration || .45;
53251         this.config = c;
53252         if(c.collapsed){
53253             this.collapse(true);
53254         }
53255         if(c.hidden){
53256             this.hide();
53257         }
53258     },
53259     /**
53260      * Returns true if this region is currently visible.
53261      * @return {Boolean}
53262      */
53263     isVisible : function(){
53264         return this.visible;
53265     },
53266
53267     /**
53268      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
53269      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
53270      */
53271     setCollapsedTitle : function(title){
53272         title = title || "&#160;";
53273         if(this.collapsedTitleTextEl){
53274             this.collapsedTitleTextEl.innerHTML = title;
53275         }
53276     },
53277
53278     getBox : function(){
53279         var b;
53280         if(!this.collapsed){
53281             b = this.el.getBox(false, true);
53282         }else{
53283             b = this.collapsedEl.getBox(false, true);
53284         }
53285         return b;
53286     },
53287
53288     getMargins : function(){
53289         return this.collapsed ? this.cmargins : this.margins;
53290     },
53291
53292     highlight : function(){
53293         this.el.addClass("x-layout-panel-dragover");
53294     },
53295
53296     unhighlight : function(){
53297         this.el.removeClass("x-layout-panel-dragover");
53298     },
53299
53300     updateBox : function(box){
53301         this.box = box;
53302         if(!this.collapsed){
53303             this.el.dom.style.left = box.x + "px";
53304             this.el.dom.style.top = box.y + "px";
53305             this.updateBody(box.width, box.height);
53306         }else{
53307             this.collapsedEl.dom.style.left = box.x + "px";
53308             this.collapsedEl.dom.style.top = box.y + "px";
53309             this.collapsedEl.setSize(box.width, box.height);
53310         }
53311         if(this.tabs){
53312             this.tabs.autoSizeTabs();
53313         }
53314     },
53315
53316     updateBody : function(w, h){
53317         if(w !== null){
53318             this.el.setWidth(w);
53319             w -= this.el.getBorderWidth("rl");
53320             if(this.config.adjustments){
53321                 w += this.config.adjustments[0];
53322             }
53323         }
53324         if(h !== null){
53325             this.el.setHeight(h);
53326             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
53327             h -= this.el.getBorderWidth("tb");
53328             if(this.config.adjustments){
53329                 h += this.config.adjustments[1];
53330             }
53331             this.bodyEl.setHeight(h);
53332             if(this.tabs){
53333                 h = this.tabs.syncHeight(h);
53334             }
53335         }
53336         if(this.panelSize){
53337             w = w !== null ? w : this.panelSize.width;
53338             h = h !== null ? h : this.panelSize.height;
53339         }
53340         if(this.activePanel){
53341             var el = this.activePanel.getEl();
53342             w = w !== null ? w : el.getWidth();
53343             h = h !== null ? h : el.getHeight();
53344             this.panelSize = {width: w, height: h};
53345             this.activePanel.setSize(w, h);
53346         }
53347         if(Roo.isIE && this.tabs){
53348             this.tabs.el.repaint();
53349         }
53350     },
53351
53352     /**
53353      * Returns the container element for this region.
53354      * @return {Roo.Element}
53355      */
53356     getEl : function(){
53357         return this.el;
53358     },
53359
53360     /**
53361      * Hides this region.
53362      */
53363     hide : function(){
53364         if(!this.collapsed){
53365             this.el.dom.style.left = "-2000px";
53366             this.el.hide();
53367         }else{
53368             this.collapsedEl.dom.style.left = "-2000px";
53369             this.collapsedEl.hide();
53370         }
53371         this.visible = false;
53372         this.fireEvent("visibilitychange", this, false);
53373     },
53374
53375     /**
53376      * Shows this region if it was previously hidden.
53377      */
53378     show : function(){
53379         if(!this.collapsed){
53380             this.el.show();
53381         }else{
53382             this.collapsedEl.show();
53383         }
53384         this.visible = true;
53385         this.fireEvent("visibilitychange", this, true);
53386     },
53387
53388     closeClicked : function(){
53389         if(this.activePanel){
53390             this.remove(this.activePanel);
53391         }
53392     },
53393
53394     collapseClick : function(e){
53395         if(this.isSlid){
53396            e.stopPropagation();
53397            this.slideIn();
53398         }else{
53399            e.stopPropagation();
53400            this.slideOut();
53401         }
53402     },
53403
53404     /**
53405      * Collapses this region.
53406      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
53407      */
53408     collapse : function(skipAnim, skipCheck){
53409         if(this.collapsed) {
53410             return;
53411         }
53412         
53413         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
53414             
53415             this.collapsed = true;
53416             if(this.split){
53417                 this.split.el.hide();
53418             }
53419             if(this.config.animate && skipAnim !== true){
53420                 this.fireEvent("invalidated", this);
53421                 this.animateCollapse();
53422             }else{
53423                 this.el.setLocation(-20000,-20000);
53424                 this.el.hide();
53425                 this.collapsedEl.show();
53426                 this.fireEvent("collapsed", this);
53427                 this.fireEvent("invalidated", this);
53428             }
53429         }
53430         
53431     },
53432
53433     animateCollapse : function(){
53434         // overridden
53435     },
53436
53437     /**
53438      * Expands this region if it was previously collapsed.
53439      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
53440      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
53441      */
53442     expand : function(e, skipAnim){
53443         if(e) {
53444             e.stopPropagation();
53445         }
53446         if(!this.collapsed || this.el.hasActiveFx()) {
53447             return;
53448         }
53449         if(this.isSlid){
53450             this.afterSlideIn();
53451             skipAnim = true;
53452         }
53453         this.collapsed = false;
53454         if(this.config.animate && skipAnim !== true){
53455             this.animateExpand();
53456         }else{
53457             this.el.show();
53458             if(this.split){
53459                 this.split.el.show();
53460             }
53461             this.collapsedEl.setLocation(-2000,-2000);
53462             this.collapsedEl.hide();
53463             this.fireEvent("invalidated", this);
53464             this.fireEvent("expanded", this);
53465         }
53466     },
53467
53468     animateExpand : function(){
53469         // overridden
53470     },
53471
53472     initTabs : function()
53473     {
53474         this.bodyEl.setStyle("overflow", "hidden");
53475         var ts = new Roo.TabPanel(
53476                 this.bodyEl.dom,
53477                 {
53478                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
53479                     disableTooltips: this.config.disableTabTips,
53480                     toolbar : this.config.toolbar
53481                 }
53482         );
53483         if(this.config.hideTabs){
53484             ts.stripWrap.setDisplayed(false);
53485         }
53486         this.tabs = ts;
53487         ts.resizeTabs = this.config.resizeTabs === true;
53488         ts.minTabWidth = this.config.minTabWidth || 40;
53489         ts.maxTabWidth = this.config.maxTabWidth || 250;
53490         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
53491         ts.monitorResize = false;
53492         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53493         ts.bodyEl.addClass('x-layout-tabs-body');
53494         this.panels.each(this.initPanelAsTab, this);
53495     },
53496
53497     initPanelAsTab : function(panel){
53498         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
53499                     this.config.closeOnTab && panel.isClosable());
53500         if(panel.tabTip !== undefined){
53501             ti.setTooltip(panel.tabTip);
53502         }
53503         ti.on("activate", function(){
53504               this.setActivePanel(panel);
53505         }, this);
53506         if(this.config.closeOnTab){
53507             ti.on("beforeclose", function(t, e){
53508                 e.cancel = true;
53509                 this.remove(panel);
53510             }, this);
53511         }
53512         return ti;
53513     },
53514
53515     updatePanelTitle : function(panel, title){
53516         if(this.activePanel == panel){
53517             this.updateTitle(title);
53518         }
53519         if(this.tabs){
53520             var ti = this.tabs.getTab(panel.getEl().id);
53521             ti.setText(title);
53522             if(panel.tabTip !== undefined){
53523                 ti.setTooltip(panel.tabTip);
53524             }
53525         }
53526     },
53527
53528     updateTitle : function(title){
53529         if(this.titleTextEl && !this.config.title){
53530             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
53531         }
53532     },
53533
53534     setActivePanel : function(panel){
53535         panel = this.getPanel(panel);
53536         if(this.activePanel && this.activePanel != panel){
53537             this.activePanel.setActiveState(false);
53538         }
53539         this.activePanel = panel;
53540         panel.setActiveState(true);
53541         if(this.panelSize){
53542             panel.setSize(this.panelSize.width, this.panelSize.height);
53543         }
53544         if(this.closeBtn){
53545             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
53546         }
53547         this.updateTitle(panel.getTitle());
53548         if(this.tabs){
53549             this.fireEvent("invalidated", this);
53550         }
53551         this.fireEvent("panelactivated", this, panel);
53552     },
53553
53554     /**
53555      * Shows the specified panel.
53556      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
53557      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
53558      */
53559     showPanel : function(panel)
53560     {
53561         panel = this.getPanel(panel);
53562         if(panel){
53563             if(this.tabs){
53564                 var tab = this.tabs.getTab(panel.getEl().id);
53565                 if(tab.isHidden()){
53566                     this.tabs.unhideTab(tab.id);
53567                 }
53568                 tab.activate();
53569             }else{
53570                 this.setActivePanel(panel);
53571             }
53572         }
53573         return panel;
53574     },
53575
53576     /**
53577      * Get the active panel for this region.
53578      * @return {Roo.ContentPanel} The active panel or null
53579      */
53580     getActivePanel : function(){
53581         return this.activePanel;
53582     },
53583
53584     validateVisibility : function(){
53585         if(this.panels.getCount() < 1){
53586             this.updateTitle("&#160;");
53587             this.closeBtn.hide();
53588             this.hide();
53589         }else{
53590             if(!this.isVisible()){
53591                 this.show();
53592             }
53593         }
53594     },
53595
53596     /**
53597      * Adds the passed ContentPanel(s) to this region.
53598      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
53599      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
53600      */
53601     add : function(panel){
53602         if(arguments.length > 1){
53603             for(var i = 0, len = arguments.length; i < len; i++) {
53604                 this.add(arguments[i]);
53605             }
53606             return null;
53607         }
53608         if(this.hasPanel(panel)){
53609             this.showPanel(panel);
53610             return panel;
53611         }
53612         panel.setRegion(this);
53613         this.panels.add(panel);
53614         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
53615             this.bodyEl.dom.appendChild(panel.getEl().dom);
53616             if(panel.background !== true){
53617                 this.setActivePanel(panel);
53618             }
53619             this.fireEvent("paneladded", this, panel);
53620             return panel;
53621         }
53622         if(!this.tabs){
53623             this.initTabs();
53624         }else{
53625             this.initPanelAsTab(panel);
53626         }
53627         if(panel.background !== true){
53628             this.tabs.activate(panel.getEl().id);
53629         }
53630         this.fireEvent("paneladded", this, panel);
53631         return panel;
53632     },
53633
53634     /**
53635      * Hides the tab for the specified panel.
53636      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53637      */
53638     hidePanel : function(panel){
53639         if(this.tabs && (panel = this.getPanel(panel))){
53640             this.tabs.hideTab(panel.getEl().id);
53641         }
53642     },
53643
53644     /**
53645      * Unhides the tab for a previously hidden panel.
53646      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53647      */
53648     unhidePanel : function(panel){
53649         if(this.tabs && (panel = this.getPanel(panel))){
53650             this.tabs.unhideTab(panel.getEl().id);
53651         }
53652     },
53653
53654     clearPanels : function(){
53655         while(this.panels.getCount() > 0){
53656              this.remove(this.panels.first());
53657         }
53658     },
53659
53660     /**
53661      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
53662      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
53663      * @param {Boolean} preservePanel Overrides the config preservePanel option
53664      * @return {Roo.ContentPanel} The panel that was removed
53665      */
53666     remove : function(panel, preservePanel){
53667         panel = this.getPanel(panel);
53668         if(!panel){
53669             return null;
53670         }
53671         var e = {};
53672         this.fireEvent("beforeremove", this, panel, e);
53673         if(e.cancel === true){
53674             return null;
53675         }
53676         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
53677         var panelId = panel.getId();
53678         this.panels.removeKey(panelId);
53679         if(preservePanel){
53680             document.body.appendChild(panel.getEl().dom);
53681         }
53682         if(this.tabs){
53683             this.tabs.removeTab(panel.getEl().id);
53684         }else if (!preservePanel){
53685             this.bodyEl.dom.removeChild(panel.getEl().dom);
53686         }
53687         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
53688             var p = this.panels.first();
53689             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
53690             tempEl.appendChild(p.getEl().dom);
53691             this.bodyEl.update("");
53692             this.bodyEl.dom.appendChild(p.getEl().dom);
53693             tempEl = null;
53694             this.updateTitle(p.getTitle());
53695             this.tabs = null;
53696             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
53697             this.setActivePanel(p);
53698         }
53699         panel.setRegion(null);
53700         if(this.activePanel == panel){
53701             this.activePanel = null;
53702         }
53703         if(this.config.autoDestroy !== false && preservePanel !== true){
53704             try{panel.destroy();}catch(e){}
53705         }
53706         this.fireEvent("panelremoved", this, panel);
53707         return panel;
53708     },
53709
53710     /**
53711      * Returns the TabPanel component used by this region
53712      * @return {Roo.TabPanel}
53713      */
53714     getTabs : function(){
53715         return this.tabs;
53716     },
53717
53718     createTool : function(parentEl, className){
53719         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
53720             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
53721         btn.addClassOnOver("x-layout-tools-button-over");
53722         return btn;
53723     }
53724 });/*
53725  * Based on:
53726  * Ext JS Library 1.1.1
53727  * Copyright(c) 2006-2007, Ext JS, LLC.
53728  *
53729  * Originally Released Under LGPL - original licence link has changed is not relivant.
53730  *
53731  * Fork - LGPL
53732  * <script type="text/javascript">
53733  */
53734  
53735
53736
53737 /**
53738  * @class Roo.SplitLayoutRegion
53739  * @extends Roo.LayoutRegion
53740  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
53741  */
53742 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
53743     this.cursor = cursor;
53744     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
53745 };
53746
53747 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
53748     splitTip : "Drag to resize.",
53749     collapsibleSplitTip : "Drag to resize. Double click to hide.",
53750     useSplitTips : false,
53751
53752     applyConfig : function(config){
53753         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
53754         if(config.split){
53755             if(!this.split){
53756                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
53757                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
53758                 /** The SplitBar for this region 
53759                 * @type Roo.SplitBar */
53760                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
53761                 this.split.on("moved", this.onSplitMove, this);
53762                 this.split.useShim = config.useShim === true;
53763                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
53764                 if(this.useSplitTips){
53765                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
53766                 }
53767                 if(config.collapsible){
53768                     this.split.el.on("dblclick", this.collapse,  this);
53769                 }
53770             }
53771             if(typeof config.minSize != "undefined"){
53772                 this.split.minSize = config.minSize;
53773             }
53774             if(typeof config.maxSize != "undefined"){
53775                 this.split.maxSize = config.maxSize;
53776             }
53777             if(config.hideWhenEmpty || config.hidden || config.collapsed){
53778                 this.hideSplitter();
53779             }
53780         }
53781     },
53782
53783     getHMaxSize : function(){
53784          var cmax = this.config.maxSize || 10000;
53785          var center = this.mgr.getRegion("center");
53786          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
53787     },
53788
53789     getVMaxSize : function(){
53790          var cmax = this.config.maxSize || 10000;
53791          var center = this.mgr.getRegion("center");
53792          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
53793     },
53794
53795     onSplitMove : function(split, newSize){
53796         this.fireEvent("resized", this, newSize);
53797     },
53798     
53799     /** 
53800      * Returns the {@link Roo.SplitBar} for this region.
53801      * @return {Roo.SplitBar}
53802      */
53803     getSplitBar : function(){
53804         return this.split;
53805     },
53806     
53807     hide : function(){
53808         this.hideSplitter();
53809         Roo.SplitLayoutRegion.superclass.hide.call(this);
53810     },
53811
53812     hideSplitter : function(){
53813         if(this.split){
53814             this.split.el.setLocation(-2000,-2000);
53815             this.split.el.hide();
53816         }
53817     },
53818
53819     show : function(){
53820         if(this.split){
53821             this.split.el.show();
53822         }
53823         Roo.SplitLayoutRegion.superclass.show.call(this);
53824     },
53825     
53826     beforeSlide: function(){
53827         if(Roo.isGecko){// firefox overflow auto bug workaround
53828             this.bodyEl.clip();
53829             if(this.tabs) {
53830                 this.tabs.bodyEl.clip();
53831             }
53832             if(this.activePanel){
53833                 this.activePanel.getEl().clip();
53834                 
53835                 if(this.activePanel.beforeSlide){
53836                     this.activePanel.beforeSlide();
53837                 }
53838             }
53839         }
53840     },
53841     
53842     afterSlide : function(){
53843         if(Roo.isGecko){// firefox overflow auto bug workaround
53844             this.bodyEl.unclip();
53845             if(this.tabs) {
53846                 this.tabs.bodyEl.unclip();
53847             }
53848             if(this.activePanel){
53849                 this.activePanel.getEl().unclip();
53850                 if(this.activePanel.afterSlide){
53851                     this.activePanel.afterSlide();
53852                 }
53853             }
53854         }
53855     },
53856
53857     initAutoHide : function(){
53858         if(this.autoHide !== false){
53859             if(!this.autoHideHd){
53860                 var st = new Roo.util.DelayedTask(this.slideIn, this);
53861                 this.autoHideHd = {
53862                     "mouseout": function(e){
53863                         if(!e.within(this.el, true)){
53864                             st.delay(500);
53865                         }
53866                     },
53867                     "mouseover" : function(e){
53868                         st.cancel();
53869                     },
53870                     scope : this
53871                 };
53872             }
53873             this.el.on(this.autoHideHd);
53874         }
53875     },
53876
53877     clearAutoHide : function(){
53878         if(this.autoHide !== false){
53879             this.el.un("mouseout", this.autoHideHd.mouseout);
53880             this.el.un("mouseover", this.autoHideHd.mouseover);
53881         }
53882     },
53883
53884     clearMonitor : function(){
53885         Roo.get(document).un("click", this.slideInIf, this);
53886     },
53887
53888     // these names are backwards but not changed for compat
53889     slideOut : function(){
53890         if(this.isSlid || this.el.hasActiveFx()){
53891             return;
53892         }
53893         this.isSlid = true;
53894         if(this.collapseBtn){
53895             this.collapseBtn.hide();
53896         }
53897         this.closeBtnState = this.closeBtn.getStyle('display');
53898         this.closeBtn.hide();
53899         if(this.stickBtn){
53900             this.stickBtn.show();
53901         }
53902         this.el.show();
53903         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
53904         this.beforeSlide();
53905         this.el.setStyle("z-index", 10001);
53906         this.el.slideIn(this.getSlideAnchor(), {
53907             callback: function(){
53908                 this.afterSlide();
53909                 this.initAutoHide();
53910                 Roo.get(document).on("click", this.slideInIf, this);
53911                 this.fireEvent("slideshow", this);
53912             },
53913             scope: this,
53914             block: true
53915         });
53916     },
53917
53918     afterSlideIn : function(){
53919         this.clearAutoHide();
53920         this.isSlid = false;
53921         this.clearMonitor();
53922         this.el.setStyle("z-index", "");
53923         if(this.collapseBtn){
53924             this.collapseBtn.show();
53925         }
53926         this.closeBtn.setStyle('display', this.closeBtnState);
53927         if(this.stickBtn){
53928             this.stickBtn.hide();
53929         }
53930         this.fireEvent("slidehide", this);
53931     },
53932
53933     slideIn : function(cb){
53934         if(!this.isSlid || this.el.hasActiveFx()){
53935             Roo.callback(cb);
53936             return;
53937         }
53938         this.isSlid = false;
53939         this.beforeSlide();
53940         this.el.slideOut(this.getSlideAnchor(), {
53941             callback: function(){
53942                 this.el.setLeftTop(-10000, -10000);
53943                 this.afterSlide();
53944                 this.afterSlideIn();
53945                 Roo.callback(cb);
53946             },
53947             scope: this,
53948             block: true
53949         });
53950     },
53951     
53952     slideInIf : function(e){
53953         if(!e.within(this.el)){
53954             this.slideIn();
53955         }
53956     },
53957
53958     animateCollapse : function(){
53959         this.beforeSlide();
53960         this.el.setStyle("z-index", 20000);
53961         var anchor = this.getSlideAnchor();
53962         this.el.slideOut(anchor, {
53963             callback : function(){
53964                 this.el.setStyle("z-index", "");
53965                 this.collapsedEl.slideIn(anchor, {duration:.3});
53966                 this.afterSlide();
53967                 this.el.setLocation(-10000,-10000);
53968                 this.el.hide();
53969                 this.fireEvent("collapsed", this);
53970             },
53971             scope: this,
53972             block: true
53973         });
53974     },
53975
53976     animateExpand : function(){
53977         this.beforeSlide();
53978         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
53979         this.el.setStyle("z-index", 20000);
53980         this.collapsedEl.hide({
53981             duration:.1
53982         });
53983         this.el.slideIn(this.getSlideAnchor(), {
53984             callback : function(){
53985                 this.el.setStyle("z-index", "");
53986                 this.afterSlide();
53987                 if(this.split){
53988                     this.split.el.show();
53989                 }
53990                 this.fireEvent("invalidated", this);
53991                 this.fireEvent("expanded", this);
53992             },
53993             scope: this,
53994             block: true
53995         });
53996     },
53997
53998     anchors : {
53999         "west" : "left",
54000         "east" : "right",
54001         "north" : "top",
54002         "south" : "bottom"
54003     },
54004
54005     sanchors : {
54006         "west" : "l",
54007         "east" : "r",
54008         "north" : "t",
54009         "south" : "b"
54010     },
54011
54012     canchors : {
54013         "west" : "tl-tr",
54014         "east" : "tr-tl",
54015         "north" : "tl-bl",
54016         "south" : "bl-tl"
54017     },
54018
54019     getAnchor : function(){
54020         return this.anchors[this.position];
54021     },
54022
54023     getCollapseAnchor : function(){
54024         return this.canchors[this.position];
54025     },
54026
54027     getSlideAnchor : function(){
54028         return this.sanchors[this.position];
54029     },
54030
54031     getAlignAdj : function(){
54032         var cm = this.cmargins;
54033         switch(this.position){
54034             case "west":
54035                 return [0, 0];
54036             break;
54037             case "east":
54038                 return [0, 0];
54039             break;
54040             case "north":
54041                 return [0, 0];
54042             break;
54043             case "south":
54044                 return [0, 0];
54045             break;
54046         }
54047     },
54048
54049     getExpandAdj : function(){
54050         var c = this.collapsedEl, cm = this.cmargins;
54051         switch(this.position){
54052             case "west":
54053                 return [-(cm.right+c.getWidth()+cm.left), 0];
54054             break;
54055             case "east":
54056                 return [cm.right+c.getWidth()+cm.left, 0];
54057             break;
54058             case "north":
54059                 return [0, -(cm.top+cm.bottom+c.getHeight())];
54060             break;
54061             case "south":
54062                 return [0, cm.top+cm.bottom+c.getHeight()];
54063             break;
54064         }
54065     }
54066 });/*
54067  * Based on:
54068  * Ext JS Library 1.1.1
54069  * Copyright(c) 2006-2007, Ext JS, LLC.
54070  *
54071  * Originally Released Under LGPL - original licence link has changed is not relivant.
54072  *
54073  * Fork - LGPL
54074  * <script type="text/javascript">
54075  */
54076 /*
54077  * These classes are private internal classes
54078  */
54079 Roo.CenterLayoutRegion = function(mgr, config){
54080     Roo.LayoutRegion.call(this, mgr, config, "center");
54081     this.visible = true;
54082     this.minWidth = config.minWidth || 20;
54083     this.minHeight = config.minHeight || 20;
54084 };
54085
54086 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
54087     hide : function(){
54088         // center panel can't be hidden
54089     },
54090     
54091     show : function(){
54092         // center panel can't be hidden
54093     },
54094     
54095     getMinWidth: function(){
54096         return this.minWidth;
54097     },
54098     
54099     getMinHeight: function(){
54100         return this.minHeight;
54101     }
54102 });
54103
54104
54105 Roo.NorthLayoutRegion = function(mgr, config){
54106     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
54107     if(this.split){
54108         this.split.placement = Roo.SplitBar.TOP;
54109         this.split.orientation = Roo.SplitBar.VERTICAL;
54110         this.split.el.addClass("x-layout-split-v");
54111     }
54112     var size = config.initialSize || config.height;
54113     if(typeof size != "undefined"){
54114         this.el.setHeight(size);
54115     }
54116 };
54117 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
54118     orientation: Roo.SplitBar.VERTICAL,
54119     getBox : function(){
54120         if(this.collapsed){
54121             return this.collapsedEl.getBox();
54122         }
54123         var box = this.el.getBox();
54124         if(this.split){
54125             box.height += this.split.el.getHeight();
54126         }
54127         return box;
54128     },
54129     
54130     updateBox : function(box){
54131         if(this.split && !this.collapsed){
54132             box.height -= this.split.el.getHeight();
54133             this.split.el.setLeft(box.x);
54134             this.split.el.setTop(box.y+box.height);
54135             this.split.el.setWidth(box.width);
54136         }
54137         if(this.collapsed){
54138             this.updateBody(box.width, null);
54139         }
54140         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54141     }
54142 });
54143
54144 Roo.SouthLayoutRegion = function(mgr, config){
54145     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
54146     if(this.split){
54147         this.split.placement = Roo.SplitBar.BOTTOM;
54148         this.split.orientation = Roo.SplitBar.VERTICAL;
54149         this.split.el.addClass("x-layout-split-v");
54150     }
54151     var size = config.initialSize || config.height;
54152     if(typeof size != "undefined"){
54153         this.el.setHeight(size);
54154     }
54155 };
54156 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
54157     orientation: Roo.SplitBar.VERTICAL,
54158     getBox : function(){
54159         if(this.collapsed){
54160             return this.collapsedEl.getBox();
54161         }
54162         var box = this.el.getBox();
54163         if(this.split){
54164             var sh = this.split.el.getHeight();
54165             box.height += sh;
54166             box.y -= sh;
54167         }
54168         return box;
54169     },
54170     
54171     updateBox : function(box){
54172         if(this.split && !this.collapsed){
54173             var sh = this.split.el.getHeight();
54174             box.height -= sh;
54175             box.y += sh;
54176             this.split.el.setLeft(box.x);
54177             this.split.el.setTop(box.y-sh);
54178             this.split.el.setWidth(box.width);
54179         }
54180         if(this.collapsed){
54181             this.updateBody(box.width, null);
54182         }
54183         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54184     }
54185 });
54186
54187 Roo.EastLayoutRegion = function(mgr, config){
54188     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
54189     if(this.split){
54190         this.split.placement = Roo.SplitBar.RIGHT;
54191         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54192         this.split.el.addClass("x-layout-split-h");
54193     }
54194     var size = config.initialSize || config.width;
54195     if(typeof size != "undefined"){
54196         this.el.setWidth(size);
54197     }
54198 };
54199 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
54200     orientation: Roo.SplitBar.HORIZONTAL,
54201     getBox : function(){
54202         if(this.collapsed){
54203             return this.collapsedEl.getBox();
54204         }
54205         var box = this.el.getBox();
54206         if(this.split){
54207             var sw = this.split.el.getWidth();
54208             box.width += sw;
54209             box.x -= sw;
54210         }
54211         return box;
54212     },
54213
54214     updateBox : function(box){
54215         if(this.split && !this.collapsed){
54216             var sw = this.split.el.getWidth();
54217             box.width -= sw;
54218             this.split.el.setLeft(box.x);
54219             this.split.el.setTop(box.y);
54220             this.split.el.setHeight(box.height);
54221             box.x += sw;
54222         }
54223         if(this.collapsed){
54224             this.updateBody(null, box.height);
54225         }
54226         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54227     }
54228 });
54229
54230 Roo.WestLayoutRegion = function(mgr, config){
54231     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
54232     if(this.split){
54233         this.split.placement = Roo.SplitBar.LEFT;
54234         this.split.orientation = Roo.SplitBar.HORIZONTAL;
54235         this.split.el.addClass("x-layout-split-h");
54236     }
54237     var size = config.initialSize || config.width;
54238     if(typeof size != "undefined"){
54239         this.el.setWidth(size);
54240     }
54241 };
54242 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
54243     orientation: Roo.SplitBar.HORIZONTAL,
54244     getBox : function(){
54245         if(this.collapsed){
54246             return this.collapsedEl.getBox();
54247         }
54248         var box = this.el.getBox();
54249         if(this.split){
54250             box.width += this.split.el.getWidth();
54251         }
54252         return box;
54253     },
54254     
54255     updateBox : function(box){
54256         if(this.split && !this.collapsed){
54257             var sw = this.split.el.getWidth();
54258             box.width -= sw;
54259             this.split.el.setLeft(box.x+box.width);
54260             this.split.el.setTop(box.y);
54261             this.split.el.setHeight(box.height);
54262         }
54263         if(this.collapsed){
54264             this.updateBody(null, box.height);
54265         }
54266         Roo.LayoutRegion.prototype.updateBox.call(this, box);
54267     }
54268 });
54269 /*
54270  * Based on:
54271  * Ext JS Library 1.1.1
54272  * Copyright(c) 2006-2007, Ext JS, LLC.
54273  *
54274  * Originally Released Under LGPL - original licence link has changed is not relivant.
54275  *
54276  * Fork - LGPL
54277  * <script type="text/javascript">
54278  */
54279  
54280  
54281 /*
54282  * Private internal class for reading and applying state
54283  */
54284 Roo.LayoutStateManager = function(layout){
54285      // default empty state
54286      this.state = {
54287         north: {},
54288         south: {},
54289         east: {},
54290         west: {}       
54291     };
54292 };
54293
54294 Roo.LayoutStateManager.prototype = {
54295     init : function(layout, provider){
54296         this.provider = provider;
54297         var state = provider.get(layout.id+"-layout-state");
54298         if(state){
54299             var wasUpdating = layout.isUpdating();
54300             if(!wasUpdating){
54301                 layout.beginUpdate();
54302             }
54303             for(var key in state){
54304                 if(typeof state[key] != "function"){
54305                     var rstate = state[key];
54306                     var r = layout.getRegion(key);
54307                     if(r && rstate){
54308                         if(rstate.size){
54309                             r.resizeTo(rstate.size);
54310                         }
54311                         if(rstate.collapsed == true){
54312                             r.collapse(true);
54313                         }else{
54314                             r.expand(null, true);
54315                         }
54316                     }
54317                 }
54318             }
54319             if(!wasUpdating){
54320                 layout.endUpdate();
54321             }
54322             this.state = state; 
54323         }
54324         this.layout = layout;
54325         layout.on("regionresized", this.onRegionResized, this);
54326         layout.on("regioncollapsed", this.onRegionCollapsed, this);
54327         layout.on("regionexpanded", this.onRegionExpanded, this);
54328     },
54329     
54330     storeState : function(){
54331         this.provider.set(this.layout.id+"-layout-state", this.state);
54332     },
54333     
54334     onRegionResized : function(region, newSize){
54335         this.state[region.getPosition()].size = newSize;
54336         this.storeState();
54337     },
54338     
54339     onRegionCollapsed : function(region){
54340         this.state[region.getPosition()].collapsed = true;
54341         this.storeState();
54342     },
54343     
54344     onRegionExpanded : function(region){
54345         this.state[region.getPosition()].collapsed = false;
54346         this.storeState();
54347     }
54348 };/*
54349  * Based on:
54350  * Ext JS Library 1.1.1
54351  * Copyright(c) 2006-2007, Ext JS, LLC.
54352  *
54353  * Originally Released Under LGPL - original licence link has changed is not relivant.
54354  *
54355  * Fork - LGPL
54356  * <script type="text/javascript">
54357  */
54358 /**
54359  * @class Roo.ContentPanel
54360  * @extends Roo.util.Observable
54361  * A basic ContentPanel element.
54362  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
54363  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
54364  * @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
54365  * @cfg {Boolean}   closable      True if the panel can be closed/removed
54366  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
54367  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
54368  * @cfg {Toolbar}   toolbar       A toolbar for this panel
54369  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
54370  * @cfg {String} title          The title for this panel
54371  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
54372  * @cfg {String} url            Calls {@link #setUrl} with this value
54373  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
54374  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
54375  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
54376  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
54377  * @cfg {String}    style  Extra style to add to the content panel 
54378
54379  * @constructor
54380  * Create a new ContentPanel.
54381  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
54382  * @param {String/Object} config A string to set only the title or a config object
54383  * @param {String} content (optional) Set the HTML content for this panel
54384  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
54385  */
54386 Roo.ContentPanel = function(el, config, content){
54387     
54388      
54389     /*
54390     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
54391         config = el;
54392         el = Roo.id();
54393     }
54394     if (config && config.parentLayout) { 
54395         el = config.parentLayout.el.createChild(); 
54396     }
54397     */
54398     if(el.autoCreate){ // xtype is available if this is called from factory
54399         config = el;
54400         el = Roo.id();
54401     }
54402     this.el = Roo.get(el);
54403     if(!this.el && config && config.autoCreate){
54404         if(typeof config.autoCreate == "object"){
54405             if(!config.autoCreate.id){
54406                 config.autoCreate.id = config.id||el;
54407             }
54408             this.el = Roo.DomHelper.append(document.body,
54409                         config.autoCreate, true);
54410         }else{
54411             this.el = Roo.DomHelper.append(document.body,
54412                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
54413         }
54414     }
54415     
54416     
54417     this.closable = false;
54418     this.loaded = false;
54419     this.active = false;
54420     if(typeof config == "string"){
54421         this.title = config;
54422     }else{
54423         Roo.apply(this, config);
54424     }
54425     
54426     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
54427         this.wrapEl = this.el.wrap();
54428         this.toolbar.container = this.el.insertSibling(false, 'before');
54429         this.toolbar = new Roo.Toolbar(this.toolbar);
54430     }
54431     
54432     // xtype created footer. - not sure if will work as we normally have to render first..
54433     if (this.footer && !this.footer.el && this.footer.xtype) {
54434         if (!this.wrapEl) {
54435             this.wrapEl = this.el.wrap();
54436         }
54437     
54438         this.footer.container = this.wrapEl.createChild();
54439          
54440         this.footer = Roo.factory(this.footer, Roo);
54441         
54442     }
54443     
54444     if(this.resizeEl){
54445         this.resizeEl = Roo.get(this.resizeEl, true);
54446     }else{
54447         this.resizeEl = this.el;
54448     }
54449     // handle view.xtype
54450     
54451  
54452     
54453     
54454     this.addEvents({
54455         /**
54456          * @event activate
54457          * Fires when this panel is activated. 
54458          * @param {Roo.ContentPanel} this
54459          */
54460         "activate" : true,
54461         /**
54462          * @event deactivate
54463          * Fires when this panel is activated. 
54464          * @param {Roo.ContentPanel} this
54465          */
54466         "deactivate" : true,
54467
54468         /**
54469          * @event resize
54470          * Fires when this panel is resized if fitToFrame is true.
54471          * @param {Roo.ContentPanel} this
54472          * @param {Number} width The width after any component adjustments
54473          * @param {Number} height The height after any component adjustments
54474          */
54475         "resize" : true,
54476         
54477          /**
54478          * @event render
54479          * Fires when this tab is created
54480          * @param {Roo.ContentPanel} this
54481          */
54482         "render" : true
54483          
54484         
54485     });
54486     
54487
54488     
54489     
54490     if(this.autoScroll){
54491         this.resizeEl.setStyle("overflow", "auto");
54492     } else {
54493         // fix randome scrolling
54494         this.el.on('scroll', function() {
54495             Roo.log('fix random scolling');
54496             this.scrollTo('top',0); 
54497         });
54498     }
54499     content = content || this.content;
54500     if(content){
54501         this.setContent(content);
54502     }
54503     if(config && config.url){
54504         this.setUrl(this.url, this.params, this.loadOnce);
54505     }
54506     
54507     
54508     
54509     Roo.ContentPanel.superclass.constructor.call(this);
54510     
54511     if (this.view && typeof(this.view.xtype) != 'undefined') {
54512         this.view.el = this.el.appendChild(document.createElement("div"));
54513         this.view = Roo.factory(this.view); 
54514         this.view.render  &&  this.view.render(false, '');  
54515     }
54516     
54517     
54518     this.fireEvent('render', this);
54519 };
54520
54521 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
54522     tabTip:'',
54523     setRegion : function(region){
54524         this.region = region;
54525         if(region){
54526            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
54527         }else{
54528            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
54529         } 
54530     },
54531     
54532     /**
54533      * Returns the toolbar for this Panel if one was configured. 
54534      * @return {Roo.Toolbar} 
54535      */
54536     getToolbar : function(){
54537         return this.toolbar;
54538     },
54539     
54540     setActiveState : function(active){
54541         this.active = active;
54542         if(!active){
54543             this.fireEvent("deactivate", this);
54544         }else{
54545             this.fireEvent("activate", this);
54546         }
54547     },
54548     /**
54549      * Updates this panel's element
54550      * @param {String} content The new content
54551      * @param {Boolean} loadScripts (optional) true to look for and process scripts
54552     */
54553     setContent : function(content, loadScripts){
54554         this.el.update(content, loadScripts);
54555     },
54556
54557     ignoreResize : function(w, h){
54558         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
54559             return true;
54560         }else{
54561             this.lastSize = {width: w, height: h};
54562             return false;
54563         }
54564     },
54565     /**
54566      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
54567      * @return {Roo.UpdateManager} The UpdateManager
54568      */
54569     getUpdateManager : function(){
54570         return this.el.getUpdateManager();
54571     },
54572      /**
54573      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
54574      * @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:
54575 <pre><code>
54576 panel.load({
54577     url: "your-url.php",
54578     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
54579     callback: yourFunction,
54580     scope: yourObject, //(optional scope)
54581     discardUrl: false,
54582     nocache: false,
54583     text: "Loading...",
54584     timeout: 30,
54585     scripts: false
54586 });
54587 </code></pre>
54588      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
54589      * 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.
54590      * @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}
54591      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
54592      * @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.
54593      * @return {Roo.ContentPanel} this
54594      */
54595     load : function(){
54596         var um = this.el.getUpdateManager();
54597         um.update.apply(um, arguments);
54598         return this;
54599     },
54600
54601
54602     /**
54603      * 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.
54604      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
54605      * @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)
54606      * @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)
54607      * @return {Roo.UpdateManager} The UpdateManager
54608      */
54609     setUrl : function(url, params, loadOnce){
54610         if(this.refreshDelegate){
54611             this.removeListener("activate", this.refreshDelegate);
54612         }
54613         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
54614         this.on("activate", this.refreshDelegate);
54615         return this.el.getUpdateManager();
54616     },
54617     
54618     _handleRefresh : function(url, params, loadOnce){
54619         if(!loadOnce || !this.loaded){
54620             var updater = this.el.getUpdateManager();
54621             updater.update(url, params, this._setLoaded.createDelegate(this));
54622         }
54623     },
54624     
54625     _setLoaded : function(){
54626         this.loaded = true;
54627     }, 
54628     
54629     /**
54630      * Returns this panel's id
54631      * @return {String} 
54632      */
54633     getId : function(){
54634         return this.el.id;
54635     },
54636     
54637     /** 
54638      * Returns this panel's element - used by regiosn to add.
54639      * @return {Roo.Element} 
54640      */
54641     getEl : function(){
54642         return this.wrapEl || this.el;
54643     },
54644     
54645     adjustForComponents : function(width, height)
54646     {
54647         //Roo.log('adjustForComponents ');
54648         if(this.resizeEl != this.el){
54649             width -= this.el.getFrameWidth('lr');
54650             height -= this.el.getFrameWidth('tb');
54651         }
54652         if(this.toolbar){
54653             var te = this.toolbar.getEl();
54654             height -= te.getHeight();
54655             te.setWidth(width);
54656         }
54657         if(this.footer){
54658             var te = this.footer.getEl();
54659             //Roo.log("footer:" + te.getHeight());
54660             
54661             height -= te.getHeight();
54662             te.setWidth(width);
54663         }
54664         
54665         
54666         if(this.adjustments){
54667             width += this.adjustments[0];
54668             height += this.adjustments[1];
54669         }
54670         return {"width": width, "height": height};
54671     },
54672     
54673     setSize : function(width, height){
54674         if(this.fitToFrame && !this.ignoreResize(width, height)){
54675             if(this.fitContainer && this.resizeEl != this.el){
54676                 this.el.setSize(width, height);
54677             }
54678             var size = this.adjustForComponents(width, height);
54679             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
54680             this.fireEvent('resize', this, size.width, size.height);
54681         }
54682     },
54683     
54684     /**
54685      * Returns this panel's title
54686      * @return {String} 
54687      */
54688     getTitle : function(){
54689         return this.title;
54690     },
54691     
54692     /**
54693      * Set this panel's title
54694      * @param {String} title
54695      */
54696     setTitle : function(title){
54697         this.title = title;
54698         if(this.region){
54699             this.region.updatePanelTitle(this, title);
54700         }
54701     },
54702     
54703     /**
54704      * Returns true is this panel was configured to be closable
54705      * @return {Boolean} 
54706      */
54707     isClosable : function(){
54708         return this.closable;
54709     },
54710     
54711     beforeSlide : function(){
54712         this.el.clip();
54713         this.resizeEl.clip();
54714     },
54715     
54716     afterSlide : function(){
54717         this.el.unclip();
54718         this.resizeEl.unclip();
54719     },
54720     
54721     /**
54722      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
54723      *   Will fail silently if the {@link #setUrl} method has not been called.
54724      *   This does not activate the panel, just updates its content.
54725      */
54726     refresh : function(){
54727         if(this.refreshDelegate){
54728            this.loaded = false;
54729            this.refreshDelegate();
54730         }
54731     },
54732     
54733     /**
54734      * Destroys this panel
54735      */
54736     destroy : function(){
54737         this.el.removeAllListeners();
54738         var tempEl = document.createElement("span");
54739         tempEl.appendChild(this.el.dom);
54740         tempEl.innerHTML = "";
54741         this.el.remove();
54742         this.el = null;
54743     },
54744     
54745     /**
54746      * form - if the content panel contains a form - this is a reference to it.
54747      * @type {Roo.form.Form}
54748      */
54749     form : false,
54750     /**
54751      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
54752      *    This contains a reference to it.
54753      * @type {Roo.View}
54754      */
54755     view : false,
54756     
54757       /**
54758      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
54759      * <pre><code>
54760
54761 layout.addxtype({
54762        xtype : 'Form',
54763        items: [ .... ]
54764    }
54765 );
54766
54767 </code></pre>
54768      * @param {Object} cfg Xtype definition of item to add.
54769      */
54770     
54771     addxtype : function(cfg) {
54772         // add form..
54773         if (cfg.xtype.match(/^Form$/)) {
54774             
54775             var el;
54776             //if (this.footer) {
54777             //    el = this.footer.container.insertSibling(false, 'before');
54778             //} else {
54779                 el = this.el.createChild();
54780             //}
54781
54782             this.form = new  Roo.form.Form(cfg);
54783             
54784             
54785             if ( this.form.allItems.length) {
54786                 this.form.render(el.dom);
54787             }
54788             return this.form;
54789         }
54790         // should only have one of theses..
54791         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
54792             // views.. should not be just added - used named prop 'view''
54793             
54794             cfg.el = this.el.appendChild(document.createElement("div"));
54795             // factory?
54796             
54797             var ret = new Roo.factory(cfg);
54798              
54799              ret.render && ret.render(false, ''); // render blank..
54800             this.view = ret;
54801             return ret;
54802         }
54803         return false;
54804     }
54805 });
54806
54807 /**
54808  * @class Roo.GridPanel
54809  * @extends Roo.ContentPanel
54810  * @constructor
54811  * Create a new GridPanel.
54812  * @param {Roo.grid.Grid} grid The grid for this panel
54813  * @param {String/Object} config A string to set only the panel's title, or a config object
54814  */
54815 Roo.GridPanel = function(grid, config){
54816     
54817   
54818     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
54819         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
54820         
54821     this.wrapper.dom.appendChild(grid.getGridEl().dom);
54822     
54823     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
54824     
54825     if(this.toolbar){
54826         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
54827     }
54828     // xtype created footer. - not sure if will work as we normally have to render first..
54829     if (this.footer && !this.footer.el && this.footer.xtype) {
54830         
54831         this.footer.container = this.grid.getView().getFooterPanel(true);
54832         this.footer.dataSource = this.grid.dataSource;
54833         this.footer = Roo.factory(this.footer, Roo);
54834         
54835     }
54836     
54837     grid.monitorWindowResize = false; // turn off autosizing
54838     grid.autoHeight = false;
54839     grid.autoWidth = false;
54840     this.grid = grid;
54841     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
54842 };
54843
54844 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
54845     getId : function(){
54846         return this.grid.id;
54847     },
54848     
54849     /**
54850      * Returns the grid for this panel
54851      * @return {Roo.grid.Grid} 
54852      */
54853     getGrid : function(){
54854         return this.grid;    
54855     },
54856     
54857     setSize : function(width, height){
54858         if(!this.ignoreResize(width, height)){
54859             var grid = this.grid;
54860             var size = this.adjustForComponents(width, height);
54861             grid.getGridEl().setSize(size.width, size.height);
54862             grid.autoSize();
54863         }
54864     },
54865     
54866     beforeSlide : function(){
54867         this.grid.getView().scroller.clip();
54868     },
54869     
54870     afterSlide : function(){
54871         this.grid.getView().scroller.unclip();
54872     },
54873     
54874     destroy : function(){
54875         this.grid.destroy();
54876         delete this.grid;
54877         Roo.GridPanel.superclass.destroy.call(this); 
54878     }
54879 });
54880
54881
54882 /**
54883  * @class Roo.NestedLayoutPanel
54884  * @extends Roo.ContentPanel
54885  * @constructor
54886  * Create a new NestedLayoutPanel.
54887  * 
54888  * 
54889  * @param {Roo.BorderLayout} layout The layout for this panel
54890  * @param {String/Object} config A string to set only the title or a config object
54891  */
54892 Roo.NestedLayoutPanel = function(layout, config)
54893 {
54894     // construct with only one argument..
54895     /* FIXME - implement nicer consturctors
54896     if (layout.layout) {
54897         config = layout;
54898         layout = config.layout;
54899         delete config.layout;
54900     }
54901     if (layout.xtype && !layout.getEl) {
54902         // then layout needs constructing..
54903         layout = Roo.factory(layout, Roo);
54904     }
54905     */
54906     
54907     
54908     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
54909     
54910     layout.monitorWindowResize = false; // turn off autosizing
54911     this.layout = layout;
54912     this.layout.getEl().addClass("x-layout-nested-layout");
54913     
54914     
54915     
54916     
54917 };
54918
54919 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
54920
54921     setSize : function(width, height){
54922         if(!this.ignoreResize(width, height)){
54923             var size = this.adjustForComponents(width, height);
54924             var el = this.layout.getEl();
54925             el.setSize(size.width, size.height);
54926             var touch = el.dom.offsetWidth;
54927             this.layout.layout();
54928             // ie requires a double layout on the first pass
54929             if(Roo.isIE && !this.initialized){
54930                 this.initialized = true;
54931                 this.layout.layout();
54932             }
54933         }
54934     },
54935     
54936     // activate all subpanels if not currently active..
54937     
54938     setActiveState : function(active){
54939         this.active = active;
54940         if(!active){
54941             this.fireEvent("deactivate", this);
54942             return;
54943         }
54944         
54945         this.fireEvent("activate", this);
54946         // not sure if this should happen before or after..
54947         if (!this.layout) {
54948             return; // should not happen..
54949         }
54950         var reg = false;
54951         for (var r in this.layout.regions) {
54952             reg = this.layout.getRegion(r);
54953             if (reg.getActivePanel()) {
54954                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
54955                 reg.setActivePanel(reg.getActivePanel());
54956                 continue;
54957             }
54958             if (!reg.panels.length) {
54959                 continue;
54960             }
54961             reg.showPanel(reg.getPanel(0));
54962         }
54963         
54964         
54965         
54966         
54967     },
54968     
54969     /**
54970      * Returns the nested BorderLayout for this panel
54971      * @return {Roo.BorderLayout} 
54972      */
54973     getLayout : function(){
54974         return this.layout;
54975     },
54976     
54977      /**
54978      * Adds a xtype elements to the layout of the nested panel
54979      * <pre><code>
54980
54981 panel.addxtype({
54982        xtype : 'ContentPanel',
54983        region: 'west',
54984        items: [ .... ]
54985    }
54986 );
54987
54988 panel.addxtype({
54989         xtype : 'NestedLayoutPanel',
54990         region: 'west',
54991         layout: {
54992            center: { },
54993            west: { }   
54994         },
54995         items : [ ... list of content panels or nested layout panels.. ]
54996    }
54997 );
54998 </code></pre>
54999      * @param {Object} cfg Xtype definition of item to add.
55000      */
55001     addxtype : function(cfg) {
55002         return this.layout.addxtype(cfg);
55003     
55004     }
55005 });
55006
55007 Roo.ScrollPanel = function(el, config, content){
55008     config = config || {};
55009     config.fitToFrame = true;
55010     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
55011     
55012     this.el.dom.style.overflow = "hidden";
55013     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
55014     this.el.removeClass("x-layout-inactive-content");
55015     this.el.on("mousewheel", this.onWheel, this);
55016
55017     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
55018     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
55019     up.unselectable(); down.unselectable();
55020     up.on("click", this.scrollUp, this);
55021     down.on("click", this.scrollDown, this);
55022     up.addClassOnOver("x-scroller-btn-over");
55023     down.addClassOnOver("x-scroller-btn-over");
55024     up.addClassOnClick("x-scroller-btn-click");
55025     down.addClassOnClick("x-scroller-btn-click");
55026     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
55027
55028     this.resizeEl = this.el;
55029     this.el = wrap; this.up = up; this.down = down;
55030 };
55031
55032 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
55033     increment : 100,
55034     wheelIncrement : 5,
55035     scrollUp : function(){
55036         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
55037     },
55038
55039     scrollDown : function(){
55040         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
55041     },
55042
55043     afterScroll : function(){
55044         var el = this.resizeEl;
55045         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
55046         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55047         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
55048     },
55049
55050     setSize : function(){
55051         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
55052         this.afterScroll();
55053     },
55054
55055     onWheel : function(e){
55056         var d = e.getWheelDelta();
55057         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
55058         this.afterScroll();
55059         e.stopEvent();
55060     },
55061
55062     setContent : function(content, loadScripts){
55063         this.resizeEl.update(content, loadScripts);
55064     }
55065
55066 });
55067
55068
55069
55070
55071
55072
55073
55074
55075
55076 /**
55077  * @class Roo.TreePanel
55078  * @extends Roo.ContentPanel
55079  * @constructor
55080  * Create a new TreePanel. - defaults to fit/scoll contents.
55081  * @param {String/Object} config A string to set only the panel's title, or a config object
55082  * @cfg {Roo.tree.TreePanel} tree The tree TreePanel, with config etc.
55083  */
55084 Roo.TreePanel = function(config){
55085     var el = config.el;
55086     var tree = config.tree;
55087     delete config.tree; 
55088     delete config.el; // hopefull!
55089     
55090     // wrapper for IE7 strict & safari scroll issue
55091     
55092     var treeEl = el.createChild();
55093     config.resizeEl = treeEl;
55094     
55095     
55096     
55097     Roo.TreePanel.superclass.constructor.call(this, el, config);
55098  
55099  
55100     this.tree = new Roo.tree.TreePanel(treeEl , tree);
55101     //console.log(tree);
55102     this.on('activate', function()
55103     {
55104         if (this.tree.rendered) {
55105             return;
55106         }
55107         //console.log('render tree');
55108         this.tree.render();
55109     });
55110     // this should not be needed.. - it's actually the 'el' that resizes?
55111     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
55112     
55113     //this.on('resize',  function (cp, w, h) {
55114     //        this.tree.innerCt.setWidth(w);
55115     //        this.tree.innerCt.setHeight(h);
55116     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
55117     //});
55118
55119         
55120     
55121 };
55122
55123 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
55124     fitToFrame : true,
55125     autoScroll : true
55126 });
55127
55128
55129
55130
55131
55132
55133
55134
55135
55136
55137
55138 /*
55139  * Based on:
55140  * Ext JS Library 1.1.1
55141  * Copyright(c) 2006-2007, Ext JS, LLC.
55142  *
55143  * Originally Released Under LGPL - original licence link has changed is not relivant.
55144  *
55145  * Fork - LGPL
55146  * <script type="text/javascript">
55147  */
55148  
55149
55150 /**
55151  * @class Roo.ReaderLayout
55152  * @extends Roo.BorderLayout
55153  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
55154  * center region containing two nested regions (a top one for a list view and one for item preview below),
55155  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
55156  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
55157  * expedites the setup of the overall layout and regions for this common application style.
55158  * Example:
55159  <pre><code>
55160 var reader = new Roo.ReaderLayout();
55161 var CP = Roo.ContentPanel;  // shortcut for adding
55162
55163 reader.beginUpdate();
55164 reader.add("north", new CP("north", "North"));
55165 reader.add("west", new CP("west", {title: "West"}));
55166 reader.add("east", new CP("east", {title: "East"}));
55167
55168 reader.regions.listView.add(new CP("listView", "List"));
55169 reader.regions.preview.add(new CP("preview", "Preview"));
55170 reader.endUpdate();
55171 </code></pre>
55172 * @constructor
55173 * Create a new ReaderLayout
55174 * @param {Object} config Configuration options
55175 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
55176 * document.body if omitted)
55177 */
55178 Roo.ReaderLayout = function(config, renderTo){
55179     var c = config || {size:{}};
55180     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
55181         north: c.north !== false ? Roo.apply({
55182             split:false,
55183             initialSize: 32,
55184             titlebar: false
55185         }, c.north) : false,
55186         west: c.west !== false ? Roo.apply({
55187             split:true,
55188             initialSize: 200,
55189             minSize: 175,
55190             maxSize: 400,
55191             titlebar: true,
55192             collapsible: true,
55193             animate: true,
55194             margins:{left:5,right:0,bottom:5,top:5},
55195             cmargins:{left:5,right:5,bottom:5,top:5}
55196         }, c.west) : false,
55197         east: c.east !== false ? Roo.apply({
55198             split:true,
55199             initialSize: 200,
55200             minSize: 175,
55201             maxSize: 400,
55202             titlebar: true,
55203             collapsible: true,
55204             animate: true,
55205             margins:{left:0,right:5,bottom:5,top:5},
55206             cmargins:{left:5,right:5,bottom:5,top:5}
55207         }, c.east) : false,
55208         center: Roo.apply({
55209             tabPosition: 'top',
55210             autoScroll:false,
55211             closeOnTab: true,
55212             titlebar:false,
55213             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
55214         }, c.center)
55215     });
55216
55217     this.el.addClass('x-reader');
55218
55219     this.beginUpdate();
55220
55221     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
55222         south: c.preview !== false ? Roo.apply({
55223             split:true,
55224             initialSize: 200,
55225             minSize: 100,
55226             autoScroll:true,
55227             collapsible:true,
55228             titlebar: true,
55229             cmargins:{top:5,left:0, right:0, bottom:0}
55230         }, c.preview) : false,
55231         center: Roo.apply({
55232             autoScroll:false,
55233             titlebar:false,
55234             minHeight:200
55235         }, c.listView)
55236     });
55237     this.add('center', new Roo.NestedLayoutPanel(inner,
55238             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
55239
55240     this.endUpdate();
55241
55242     this.regions.preview = inner.getRegion('south');
55243     this.regions.listView = inner.getRegion('center');
55244 };
55245
55246 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
55247  * Based on:
55248  * Ext JS Library 1.1.1
55249  * Copyright(c) 2006-2007, Ext JS, LLC.
55250  *
55251  * Originally Released Under LGPL - original licence link has changed is not relivant.
55252  *
55253  * Fork - LGPL
55254  * <script type="text/javascript">
55255  */
55256  
55257 /**
55258  * @class Roo.grid.Grid
55259  * @extends Roo.util.Observable
55260  * This class represents the primary interface of a component based grid control.
55261  * <br><br>Usage:<pre><code>
55262  var grid = new Roo.grid.Grid("my-container-id", {
55263      ds: myDataStore,
55264      cm: myColModel,
55265      selModel: mySelectionModel,
55266      autoSizeColumns: true,
55267      monitorWindowResize: false,
55268      trackMouseOver: true
55269  });
55270  // set any options
55271  grid.render();
55272  * </code></pre>
55273  * <b>Common Problems:</b><br/>
55274  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
55275  * element will correct this<br/>
55276  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
55277  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
55278  * are unpredictable.<br/>
55279  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
55280  * grid to calculate dimensions/offsets.<br/>
55281   * @constructor
55282  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
55283  * The container MUST have some type of size defined for the grid to fill. The container will be
55284  * automatically set to position relative if it isn't already.
55285  * @param {Object} config A config object that sets properties on this grid.
55286  */
55287 Roo.grid.Grid = function(container, config){
55288         // initialize the container
55289         this.container = Roo.get(container);
55290         this.container.update("");
55291         this.container.setStyle("overflow", "hidden");
55292     this.container.addClass('x-grid-container');
55293
55294     this.id = this.container.id;
55295
55296     Roo.apply(this, config);
55297     // check and correct shorthanded configs
55298     if(this.ds){
55299         this.dataSource = this.ds;
55300         delete this.ds;
55301     }
55302     if(this.cm){
55303         this.colModel = this.cm;
55304         delete this.cm;
55305     }
55306     if(this.sm){
55307         this.selModel = this.sm;
55308         delete this.sm;
55309     }
55310
55311     if (this.selModel) {
55312         this.selModel = Roo.factory(this.selModel, Roo.grid);
55313         this.sm = this.selModel;
55314         this.sm.xmodule = this.xmodule || false;
55315     }
55316     if (typeof(this.colModel.config) == 'undefined') {
55317         this.colModel = new Roo.grid.ColumnModel(this.colModel);
55318         this.cm = this.colModel;
55319         this.cm.xmodule = this.xmodule || false;
55320     }
55321     if (this.dataSource) {
55322         this.dataSource= Roo.factory(this.dataSource, Roo.data);
55323         this.ds = this.dataSource;
55324         this.ds.xmodule = this.xmodule || false;
55325          
55326     }
55327     
55328     
55329     
55330     if(this.width){
55331         this.container.setWidth(this.width);
55332     }
55333
55334     if(this.height){
55335         this.container.setHeight(this.height);
55336     }
55337     /** @private */
55338         this.addEvents({
55339         // raw events
55340         /**
55341          * @event click
55342          * The raw click event for the entire grid.
55343          * @param {Roo.EventObject} e
55344          */
55345         "click" : true,
55346         /**
55347          * @event dblclick
55348          * The raw dblclick event for the entire grid.
55349          * @param {Roo.EventObject} e
55350          */
55351         "dblclick" : true,
55352         /**
55353          * @event contextmenu
55354          * The raw contextmenu event for the entire grid.
55355          * @param {Roo.EventObject} e
55356          */
55357         "contextmenu" : true,
55358         /**
55359          * @event mousedown
55360          * The raw mousedown event for the entire grid.
55361          * @param {Roo.EventObject} e
55362          */
55363         "mousedown" : true,
55364         /**
55365          * @event mouseup
55366          * The raw mouseup event for the entire grid.
55367          * @param {Roo.EventObject} e
55368          */
55369         "mouseup" : true,
55370         /**
55371          * @event mouseover
55372          * The raw mouseover event for the entire grid.
55373          * @param {Roo.EventObject} e
55374          */
55375         "mouseover" : true,
55376         /**
55377          * @event mouseout
55378          * The raw mouseout event for the entire grid.
55379          * @param {Roo.EventObject} e
55380          */
55381         "mouseout" : true,
55382         /**
55383          * @event keypress
55384          * The raw keypress event for the entire grid.
55385          * @param {Roo.EventObject} e
55386          */
55387         "keypress" : true,
55388         /**
55389          * @event keydown
55390          * The raw keydown event for the entire grid.
55391          * @param {Roo.EventObject} e
55392          */
55393         "keydown" : true,
55394
55395         // custom events
55396
55397         /**
55398          * @event cellclick
55399          * Fires when a cell is clicked
55400          * @param {Grid} this
55401          * @param {Number} rowIndex
55402          * @param {Number} columnIndex
55403          * @param {Roo.EventObject} e
55404          */
55405         "cellclick" : true,
55406         /**
55407          * @event celldblclick
55408          * Fires when a cell is double clicked
55409          * @param {Grid} this
55410          * @param {Number} rowIndex
55411          * @param {Number} columnIndex
55412          * @param {Roo.EventObject} e
55413          */
55414         "celldblclick" : true,
55415         /**
55416          * @event rowclick
55417          * Fires when a row is clicked
55418          * @param {Grid} this
55419          * @param {Number} rowIndex
55420          * @param {Roo.EventObject} e
55421          */
55422         "rowclick" : true,
55423         /**
55424          * @event rowdblclick
55425          * Fires when a row is double clicked
55426          * @param {Grid} this
55427          * @param {Number} rowIndex
55428          * @param {Roo.EventObject} e
55429          */
55430         "rowdblclick" : true,
55431         /**
55432          * @event headerclick
55433          * Fires when a header is clicked
55434          * @param {Grid} this
55435          * @param {Number} columnIndex
55436          * @param {Roo.EventObject} e
55437          */
55438         "headerclick" : true,
55439         /**
55440          * @event headerdblclick
55441          * Fires when a header cell is double clicked
55442          * @param {Grid} this
55443          * @param {Number} columnIndex
55444          * @param {Roo.EventObject} e
55445          */
55446         "headerdblclick" : true,
55447         /**
55448          * @event rowcontextmenu
55449          * Fires when a row is right clicked
55450          * @param {Grid} this
55451          * @param {Number} rowIndex
55452          * @param {Roo.EventObject} e
55453          */
55454         "rowcontextmenu" : true,
55455         /**
55456          * @event cellcontextmenu
55457          * Fires when a cell is right clicked
55458          * @param {Grid} this
55459          * @param {Number} rowIndex
55460          * @param {Number} cellIndex
55461          * @param {Roo.EventObject} e
55462          */
55463          "cellcontextmenu" : true,
55464         /**
55465          * @event headercontextmenu
55466          * Fires when a header is right clicked
55467          * @param {Grid} this
55468          * @param {Number} columnIndex
55469          * @param {Roo.EventObject} e
55470          */
55471         "headercontextmenu" : true,
55472         /**
55473          * @event bodyscroll
55474          * Fires when the body element is scrolled
55475          * @param {Number} scrollLeft
55476          * @param {Number} scrollTop
55477          */
55478         "bodyscroll" : true,
55479         /**
55480          * @event columnresize
55481          * Fires when the user resizes a column
55482          * @param {Number} columnIndex
55483          * @param {Number} newSize
55484          */
55485         "columnresize" : true,
55486         /**
55487          * @event columnmove
55488          * Fires when the user moves a column
55489          * @param {Number} oldIndex
55490          * @param {Number} newIndex
55491          */
55492         "columnmove" : true,
55493         /**
55494          * @event startdrag
55495          * Fires when row(s) start being dragged
55496          * @param {Grid} this
55497          * @param {Roo.GridDD} dd The drag drop object
55498          * @param {event} e The raw browser event
55499          */
55500         "startdrag" : true,
55501         /**
55502          * @event enddrag
55503          * Fires when a drag operation is complete
55504          * @param {Grid} this
55505          * @param {Roo.GridDD} dd The drag drop object
55506          * @param {event} e The raw browser event
55507          */
55508         "enddrag" : true,
55509         /**
55510          * @event dragdrop
55511          * Fires when dragged row(s) are dropped on a valid DD target
55512          * @param {Grid} this
55513          * @param {Roo.GridDD} dd The drag drop object
55514          * @param {String} targetId The target drag drop object
55515          * @param {event} e The raw browser event
55516          */
55517         "dragdrop" : true,
55518         /**
55519          * @event dragover
55520          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
55521          * @param {Grid} this
55522          * @param {Roo.GridDD} dd The drag drop object
55523          * @param {String} targetId The target drag drop object
55524          * @param {event} e The raw browser event
55525          */
55526         "dragover" : true,
55527         /**
55528          * @event dragenter
55529          *  Fires when the dragged row(s) first cross another DD target while being dragged
55530          * @param {Grid} this
55531          * @param {Roo.GridDD} dd The drag drop object
55532          * @param {String} targetId The target drag drop object
55533          * @param {event} e The raw browser event
55534          */
55535         "dragenter" : true,
55536         /**
55537          * @event dragout
55538          * Fires when the dragged row(s) leave another DD target while being dragged
55539          * @param {Grid} this
55540          * @param {Roo.GridDD} dd The drag drop object
55541          * @param {String} targetId The target drag drop object
55542          * @param {event} e The raw browser event
55543          */
55544         "dragout" : true,
55545         /**
55546          * @event rowclass
55547          * Fires when a row is rendered, so you can change add a style to it.
55548          * @param {GridView} gridview   The grid view
55549          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
55550          */
55551         'rowclass' : true,
55552
55553         /**
55554          * @event render
55555          * Fires when the grid is rendered
55556          * @param {Grid} grid
55557          */
55558         'render' : true
55559     });
55560
55561     Roo.grid.Grid.superclass.constructor.call(this);
55562 };
55563 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
55564     
55565     /**
55566      * @cfg {String} ddGroup - drag drop group.
55567      */
55568       /**
55569      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
55570      */
55571
55572     /**
55573      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
55574      */
55575     minColumnWidth : 25,
55576
55577     /**
55578      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
55579      * <b>on initial render.</b> It is more efficient to explicitly size the columns
55580      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
55581      */
55582     autoSizeColumns : false,
55583
55584     /**
55585      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
55586      */
55587     autoSizeHeaders : true,
55588
55589     /**
55590      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
55591      */
55592     monitorWindowResize : true,
55593
55594     /**
55595      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
55596      * rows measured to get a columns size. Default is 0 (all rows).
55597      */
55598     maxRowsToMeasure : 0,
55599
55600     /**
55601      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
55602      */
55603     trackMouseOver : true,
55604
55605     /**
55606     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
55607     */
55608       /**
55609     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
55610     */
55611     
55612     /**
55613     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
55614     */
55615     enableDragDrop : false,
55616     
55617     /**
55618     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
55619     */
55620     enableColumnMove : true,
55621     
55622     /**
55623     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
55624     */
55625     enableColumnHide : true,
55626     
55627     /**
55628     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
55629     */
55630     enableRowHeightSync : false,
55631     
55632     /**
55633     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
55634     */
55635     stripeRows : true,
55636     
55637     /**
55638     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
55639     */
55640     autoHeight : false,
55641
55642     /**
55643      * @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.
55644      */
55645     autoExpandColumn : false,
55646
55647     /**
55648     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
55649     * Default is 50.
55650     */
55651     autoExpandMin : 50,
55652
55653     /**
55654     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
55655     */
55656     autoExpandMax : 1000,
55657
55658     /**
55659     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
55660     */
55661     view : null,
55662
55663     /**
55664     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
55665     */
55666     loadMask : false,
55667     /**
55668     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
55669     */
55670     dropTarget: false,
55671     
55672    
55673     
55674     // private
55675     rendered : false,
55676
55677     /**
55678     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
55679     * of a fixed width. Default is false.
55680     */
55681     /**
55682     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
55683     */
55684     
55685     
55686     /**
55687     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
55688     * %0 is replaced with the number of selected rows.
55689     */
55690     ddText : "{0} selected row{1}",
55691     
55692     
55693     /**
55694      * Called once after all setup has been completed and the grid is ready to be rendered.
55695      * @return {Roo.grid.Grid} this
55696      */
55697     render : function()
55698     {
55699         var c = this.container;
55700         // try to detect autoHeight/width mode
55701         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
55702             this.autoHeight = true;
55703         }
55704         var view = this.getView();
55705         view.init(this);
55706
55707         c.on("click", this.onClick, this);
55708         c.on("dblclick", this.onDblClick, this);
55709         c.on("contextmenu", this.onContextMenu, this);
55710         c.on("keydown", this.onKeyDown, this);
55711         if (Roo.isTouch) {
55712             c.on("touchstart", this.onTouchStart, this);
55713         }
55714
55715         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
55716
55717         this.getSelectionModel().init(this);
55718
55719         view.render();
55720
55721         if(this.loadMask){
55722             this.loadMask = new Roo.LoadMask(this.container,
55723                     Roo.apply({store:this.dataSource}, this.loadMask));
55724         }
55725         
55726         
55727         if (this.toolbar && this.toolbar.xtype) {
55728             this.toolbar.container = this.getView().getHeaderPanel(true);
55729             this.toolbar = new Roo.Toolbar(this.toolbar);
55730         }
55731         if (this.footer && this.footer.xtype) {
55732             this.footer.dataSource = this.getDataSource();
55733             this.footer.container = this.getView().getFooterPanel(true);
55734             this.footer = Roo.factory(this.footer, Roo);
55735         }
55736         if (this.dropTarget && this.dropTarget.xtype) {
55737             delete this.dropTarget.xtype;
55738             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
55739         }
55740         
55741         
55742         this.rendered = true;
55743         this.fireEvent('render', this);
55744         return this;
55745     },
55746
55747     /**
55748      * Reconfigures the grid to use a different Store and Column Model.
55749      * The View will be bound to the new objects and refreshed.
55750      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
55751      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
55752      */
55753     reconfigure : function(dataSource, colModel){
55754         if(this.loadMask){
55755             this.loadMask.destroy();
55756             this.loadMask = new Roo.LoadMask(this.container,
55757                     Roo.apply({store:dataSource}, this.loadMask));
55758         }
55759         this.view.bind(dataSource, colModel);
55760         this.dataSource = dataSource;
55761         this.colModel = colModel;
55762         this.view.refresh(true);
55763     },
55764     /**
55765      * addColumns
55766      * Add's a column, default at the end..
55767      
55768      * @param {int} position to add (default end)
55769      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
55770      */
55771     addColumns : function(pos, ar)
55772     {
55773         
55774         for (var i =0;i< ar.length;i++) {
55775             var cfg = ar[i];
55776             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
55777             this.cm.lookup[cfg.id] = cfg;
55778         }
55779         
55780         
55781         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
55782             pos = this.cm.config.length; //this.cm.config.push(cfg);
55783         } 
55784         pos = Math.max(0,pos);
55785         ar.unshift(0);
55786         ar.unshift(pos);
55787         this.cm.config.splice.apply(this.cm.config, ar);
55788         
55789         
55790         
55791         this.view.generateRules(this.cm);
55792         this.view.refresh(true);
55793         
55794     },
55795     
55796     
55797     
55798     
55799     // private
55800     onKeyDown : function(e){
55801         this.fireEvent("keydown", e);
55802     },
55803
55804     /**
55805      * Destroy this grid.
55806      * @param {Boolean} removeEl True to remove the element
55807      */
55808     destroy : function(removeEl, keepListeners){
55809         if(this.loadMask){
55810             this.loadMask.destroy();
55811         }
55812         var c = this.container;
55813         c.removeAllListeners();
55814         this.view.destroy();
55815         this.colModel.purgeListeners();
55816         if(!keepListeners){
55817             this.purgeListeners();
55818         }
55819         c.update("");
55820         if(removeEl === true){
55821             c.remove();
55822         }
55823     },
55824
55825     // private
55826     processEvent : function(name, e){
55827         // does this fire select???
55828         //Roo.log('grid:processEvent '  + name);
55829         
55830         if (name != 'touchstart' ) {
55831             this.fireEvent(name, e);    
55832         }
55833         
55834         var t = e.getTarget();
55835         var v = this.view;
55836         var header = v.findHeaderIndex(t);
55837         if(header !== false){
55838             var ename = name == 'touchstart' ? 'click' : name;
55839              
55840             this.fireEvent("header" + ename, this, header, e);
55841         }else{
55842             var row = v.findRowIndex(t);
55843             var cell = v.findCellIndex(t);
55844             if (name == 'touchstart') {
55845                 // first touch is always a click.
55846                 // hopefull this happens after selection is updated.?
55847                 name = false;
55848                 
55849                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
55850                     var cs = this.selModel.getSelectedCell();
55851                     if (row == cs[0] && cell == cs[1]){
55852                         name = 'dblclick';
55853                     }
55854                 }
55855                 if (typeof(this.selModel.getSelections) != 'undefined') {
55856                     var cs = this.selModel.getSelections();
55857                     var ds = this.dataSource;
55858                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
55859                         name = 'dblclick';
55860                     }
55861                 }
55862                 if (!name) {
55863                     return;
55864                 }
55865             }
55866             
55867             
55868             if(row !== false){
55869                 this.fireEvent("row" + name, this, row, e);
55870                 if(cell !== false){
55871                     this.fireEvent("cell" + name, this, row, cell, e);
55872                 }
55873             }
55874         }
55875     },
55876
55877     // private
55878     onClick : function(e){
55879         this.processEvent("click", e);
55880     },
55881    // private
55882     onTouchStart : function(e){
55883         this.processEvent("touchstart", e);
55884     },
55885
55886     // private
55887     onContextMenu : function(e, t){
55888         this.processEvent("contextmenu", e);
55889     },
55890
55891     // private
55892     onDblClick : function(e){
55893         this.processEvent("dblclick", e);
55894     },
55895
55896     // private
55897     walkCells : function(row, col, step, fn, scope){
55898         var cm = this.colModel, clen = cm.getColumnCount();
55899         var ds = this.dataSource, rlen = ds.getCount(), first = true;
55900         if(step < 0){
55901             if(col < 0){
55902                 row--;
55903                 first = false;
55904             }
55905             while(row >= 0){
55906                 if(!first){
55907                     col = clen-1;
55908                 }
55909                 first = false;
55910                 while(col >= 0){
55911                     if(fn.call(scope || this, row, col, cm) === true){
55912                         return [row, col];
55913                     }
55914                     col--;
55915                 }
55916                 row--;
55917             }
55918         } else {
55919             if(col >= clen){
55920                 row++;
55921                 first = false;
55922             }
55923             while(row < rlen){
55924                 if(!first){
55925                     col = 0;
55926                 }
55927                 first = false;
55928                 while(col < clen){
55929                     if(fn.call(scope || this, row, col, cm) === true){
55930                         return [row, col];
55931                     }
55932                     col++;
55933                 }
55934                 row++;
55935             }
55936         }
55937         return null;
55938     },
55939
55940     // private
55941     getSelections : function(){
55942         return this.selModel.getSelections();
55943     },
55944
55945     /**
55946      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
55947      * but if manual update is required this method will initiate it.
55948      */
55949     autoSize : function(){
55950         if(this.rendered){
55951             this.view.layout();
55952             if(this.view.adjustForScroll){
55953                 this.view.adjustForScroll();
55954             }
55955         }
55956     },
55957
55958     /**
55959      * Returns the grid's underlying element.
55960      * @return {Element} The element
55961      */
55962     getGridEl : function(){
55963         return this.container;
55964     },
55965
55966     // private for compatibility, overridden by editor grid
55967     stopEditing : function(){},
55968
55969     /**
55970      * Returns the grid's SelectionModel.
55971      * @return {SelectionModel}
55972      */
55973     getSelectionModel : function(){
55974         if(!this.selModel){
55975             this.selModel = new Roo.grid.RowSelectionModel();
55976         }
55977         return this.selModel;
55978     },
55979
55980     /**
55981      * Returns the grid's DataSource.
55982      * @return {DataSource}
55983      */
55984     getDataSource : function(){
55985         return this.dataSource;
55986     },
55987
55988     /**
55989      * Returns the grid's ColumnModel.
55990      * @return {ColumnModel}
55991      */
55992     getColumnModel : function(){
55993         return this.colModel;
55994     },
55995
55996     /**
55997      * Returns the grid's GridView object.
55998      * @return {GridView}
55999      */
56000     getView : function(){
56001         if(!this.view){
56002             this.view = new Roo.grid.GridView(this.viewConfig);
56003             this.relayEvents(this.view, [
56004                 "beforerowremoved", "beforerowsinserted",
56005                 "beforerefresh", "rowremoved",
56006                 "rowsinserted", "rowupdated" ,"refresh"
56007             ]);
56008         }
56009         return this.view;
56010     },
56011     /**
56012      * Called to get grid's drag proxy text, by default returns this.ddText.
56013      * Override this to put something different in the dragged text.
56014      * @return {String}
56015      */
56016     getDragDropText : function(){
56017         var count = this.selModel.getCount();
56018         return String.format(this.ddText, count, count == 1 ? '' : 's');
56019     }
56020 });
56021 /*
56022  * Based on:
56023  * Ext JS Library 1.1.1
56024  * Copyright(c) 2006-2007, Ext JS, LLC.
56025  *
56026  * Originally Released Under LGPL - original licence link has changed is not relivant.
56027  *
56028  * Fork - LGPL
56029  * <script type="text/javascript">
56030  */
56031  
56032 Roo.grid.AbstractGridView = function(){
56033         this.grid = null;
56034         
56035         this.events = {
56036             "beforerowremoved" : true,
56037             "beforerowsinserted" : true,
56038             "beforerefresh" : true,
56039             "rowremoved" : true,
56040             "rowsinserted" : true,
56041             "rowupdated" : true,
56042             "refresh" : true
56043         };
56044     Roo.grid.AbstractGridView.superclass.constructor.call(this);
56045 };
56046
56047 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
56048     rowClass : "x-grid-row",
56049     cellClass : "x-grid-cell",
56050     tdClass : "x-grid-td",
56051     hdClass : "x-grid-hd",
56052     splitClass : "x-grid-hd-split",
56053     
56054     init: function(grid){
56055         this.grid = grid;
56056                 var cid = this.grid.getGridEl().id;
56057         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
56058         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
56059         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
56060         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
56061         },
56062         
56063     getColumnRenderers : function(){
56064         var renderers = [];
56065         var cm = this.grid.colModel;
56066         var colCount = cm.getColumnCount();
56067         for(var i = 0; i < colCount; i++){
56068             renderers[i] = cm.getRenderer(i);
56069         }
56070         return renderers;
56071     },
56072     
56073     getColumnIds : function(){
56074         var ids = [];
56075         var cm = this.grid.colModel;
56076         var colCount = cm.getColumnCount();
56077         for(var i = 0; i < colCount; i++){
56078             ids[i] = cm.getColumnId(i);
56079         }
56080         return ids;
56081     },
56082     
56083     getDataIndexes : function(){
56084         if(!this.indexMap){
56085             this.indexMap = this.buildIndexMap();
56086         }
56087         return this.indexMap.colToData;
56088     },
56089     
56090     getColumnIndexByDataIndex : function(dataIndex){
56091         if(!this.indexMap){
56092             this.indexMap = this.buildIndexMap();
56093         }
56094         return this.indexMap.dataToCol[dataIndex];
56095     },
56096     
56097     /**
56098      * Set a css style for a column dynamically. 
56099      * @param {Number} colIndex The index of the column
56100      * @param {String} name The css property name
56101      * @param {String} value The css value
56102      */
56103     setCSSStyle : function(colIndex, name, value){
56104         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
56105         Roo.util.CSS.updateRule(selector, name, value);
56106     },
56107     
56108     generateRules : function(cm){
56109         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
56110         Roo.util.CSS.removeStyleSheet(rulesId);
56111         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56112             var cid = cm.getColumnId(i);
56113             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
56114                          this.tdSelector, cid, " {\n}\n",
56115                          this.hdSelector, cid, " {\n}\n",
56116                          this.splitSelector, cid, " {\n}\n");
56117         }
56118         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56119     }
56120 });/*
56121  * Based on:
56122  * Ext JS Library 1.1.1
56123  * Copyright(c) 2006-2007, Ext JS, LLC.
56124  *
56125  * Originally Released Under LGPL - original licence link has changed is not relivant.
56126  *
56127  * Fork - LGPL
56128  * <script type="text/javascript">
56129  */
56130
56131 // private
56132 // This is a support class used internally by the Grid components
56133 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
56134     this.grid = grid;
56135     this.view = grid.getView();
56136     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56137     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
56138     if(hd2){
56139         this.setHandleElId(Roo.id(hd));
56140         this.setOuterHandleElId(Roo.id(hd2));
56141     }
56142     this.scroll = false;
56143 };
56144 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
56145     maxDragWidth: 120,
56146     getDragData : function(e){
56147         var t = Roo.lib.Event.getTarget(e);
56148         var h = this.view.findHeaderCell(t);
56149         if(h){
56150             return {ddel: h.firstChild, header:h};
56151         }
56152         return false;
56153     },
56154
56155     onInitDrag : function(e){
56156         this.view.headersDisabled = true;
56157         var clone = this.dragData.ddel.cloneNode(true);
56158         clone.id = Roo.id();
56159         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
56160         this.proxy.update(clone);
56161         return true;
56162     },
56163
56164     afterValidDrop : function(){
56165         var v = this.view;
56166         setTimeout(function(){
56167             v.headersDisabled = false;
56168         }, 50);
56169     },
56170
56171     afterInvalidDrop : function(){
56172         var v = this.view;
56173         setTimeout(function(){
56174             v.headersDisabled = false;
56175         }, 50);
56176     }
56177 });
56178 /*
56179  * Based on:
56180  * Ext JS Library 1.1.1
56181  * Copyright(c) 2006-2007, Ext JS, LLC.
56182  *
56183  * Originally Released Under LGPL - original licence link has changed is not relivant.
56184  *
56185  * Fork - LGPL
56186  * <script type="text/javascript">
56187  */
56188 // private
56189 // This is a support class used internally by the Grid components
56190 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
56191     this.grid = grid;
56192     this.view = grid.getView();
56193     // split the proxies so they don't interfere with mouse events
56194     this.proxyTop = Roo.DomHelper.append(document.body, {
56195         cls:"col-move-top", html:"&#160;"
56196     }, true);
56197     this.proxyBottom = Roo.DomHelper.append(document.body, {
56198         cls:"col-move-bottom", html:"&#160;"
56199     }, true);
56200     this.proxyTop.hide = this.proxyBottom.hide = function(){
56201         this.setLeftTop(-100,-100);
56202         this.setStyle("visibility", "hidden");
56203     };
56204     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
56205     // temporarily disabled
56206     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
56207     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
56208 };
56209 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
56210     proxyOffsets : [-4, -9],
56211     fly: Roo.Element.fly,
56212
56213     getTargetFromEvent : function(e){
56214         var t = Roo.lib.Event.getTarget(e);
56215         var cindex = this.view.findCellIndex(t);
56216         if(cindex !== false){
56217             return this.view.getHeaderCell(cindex);
56218         }
56219         return null;
56220     },
56221
56222     nextVisible : function(h){
56223         var v = this.view, cm = this.grid.colModel;
56224         h = h.nextSibling;
56225         while(h){
56226             if(!cm.isHidden(v.getCellIndex(h))){
56227                 return h;
56228             }
56229             h = h.nextSibling;
56230         }
56231         return null;
56232     },
56233
56234     prevVisible : function(h){
56235         var v = this.view, cm = this.grid.colModel;
56236         h = h.prevSibling;
56237         while(h){
56238             if(!cm.isHidden(v.getCellIndex(h))){
56239                 return h;
56240             }
56241             h = h.prevSibling;
56242         }
56243         return null;
56244     },
56245
56246     positionIndicator : function(h, n, e){
56247         var x = Roo.lib.Event.getPageX(e);
56248         var r = Roo.lib.Dom.getRegion(n.firstChild);
56249         var px, pt, py = r.top + this.proxyOffsets[1];
56250         if((r.right - x) <= (r.right-r.left)/2){
56251             px = r.right+this.view.borderWidth;
56252             pt = "after";
56253         }else{
56254             px = r.left;
56255             pt = "before";
56256         }
56257         var oldIndex = this.view.getCellIndex(h);
56258         var newIndex = this.view.getCellIndex(n);
56259
56260         if(this.grid.colModel.isFixed(newIndex)){
56261             return false;
56262         }
56263
56264         var locked = this.grid.colModel.isLocked(newIndex);
56265
56266         if(pt == "after"){
56267             newIndex++;
56268         }
56269         if(oldIndex < newIndex){
56270             newIndex--;
56271         }
56272         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
56273             return false;
56274         }
56275         px +=  this.proxyOffsets[0];
56276         this.proxyTop.setLeftTop(px, py);
56277         this.proxyTop.show();
56278         if(!this.bottomOffset){
56279             this.bottomOffset = this.view.mainHd.getHeight();
56280         }
56281         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
56282         this.proxyBottom.show();
56283         return pt;
56284     },
56285
56286     onNodeEnter : function(n, dd, e, data){
56287         if(data.header != n){
56288             this.positionIndicator(data.header, n, e);
56289         }
56290     },
56291
56292     onNodeOver : function(n, dd, e, data){
56293         var result = false;
56294         if(data.header != n){
56295             result = this.positionIndicator(data.header, n, e);
56296         }
56297         if(!result){
56298             this.proxyTop.hide();
56299             this.proxyBottom.hide();
56300         }
56301         return result ? this.dropAllowed : this.dropNotAllowed;
56302     },
56303
56304     onNodeOut : function(n, dd, e, data){
56305         this.proxyTop.hide();
56306         this.proxyBottom.hide();
56307     },
56308
56309     onNodeDrop : function(n, dd, e, data){
56310         var h = data.header;
56311         if(h != n){
56312             var cm = this.grid.colModel;
56313             var x = Roo.lib.Event.getPageX(e);
56314             var r = Roo.lib.Dom.getRegion(n.firstChild);
56315             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
56316             var oldIndex = this.view.getCellIndex(h);
56317             var newIndex = this.view.getCellIndex(n);
56318             var locked = cm.isLocked(newIndex);
56319             if(pt == "after"){
56320                 newIndex++;
56321             }
56322             if(oldIndex < newIndex){
56323                 newIndex--;
56324             }
56325             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
56326                 return false;
56327             }
56328             cm.setLocked(oldIndex, locked, true);
56329             cm.moveColumn(oldIndex, newIndex);
56330             this.grid.fireEvent("columnmove", oldIndex, newIndex);
56331             return true;
56332         }
56333         return false;
56334     }
56335 });
56336 /*
56337  * Based on:
56338  * Ext JS Library 1.1.1
56339  * Copyright(c) 2006-2007, Ext JS, LLC.
56340  *
56341  * Originally Released Under LGPL - original licence link has changed is not relivant.
56342  *
56343  * Fork - LGPL
56344  * <script type="text/javascript">
56345  */
56346   
56347 /**
56348  * @class Roo.grid.GridView
56349  * @extends Roo.util.Observable
56350  *
56351  * @constructor
56352  * @param {Object} config
56353  */
56354 Roo.grid.GridView = function(config){
56355     Roo.grid.GridView.superclass.constructor.call(this);
56356     this.el = null;
56357
56358     Roo.apply(this, config);
56359 };
56360
56361 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
56362
56363     unselectable :  'unselectable="on"',
56364     unselectableCls :  'x-unselectable',
56365     
56366     
56367     rowClass : "x-grid-row",
56368
56369     cellClass : "x-grid-col",
56370
56371     tdClass : "x-grid-td",
56372
56373     hdClass : "x-grid-hd",
56374
56375     splitClass : "x-grid-split",
56376
56377     sortClasses : ["sort-asc", "sort-desc"],
56378
56379     enableMoveAnim : false,
56380
56381     hlColor: "C3DAF9",
56382
56383     dh : Roo.DomHelper,
56384
56385     fly : Roo.Element.fly,
56386
56387     css : Roo.util.CSS,
56388
56389     borderWidth: 1,
56390
56391     splitOffset: 3,
56392
56393     scrollIncrement : 22,
56394
56395     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
56396
56397     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
56398
56399     bind : function(ds, cm){
56400         if(this.ds){
56401             this.ds.un("load", this.onLoad, this);
56402             this.ds.un("datachanged", this.onDataChange, this);
56403             this.ds.un("add", this.onAdd, this);
56404             this.ds.un("remove", this.onRemove, this);
56405             this.ds.un("update", this.onUpdate, this);
56406             this.ds.un("clear", this.onClear, this);
56407         }
56408         if(ds){
56409             ds.on("load", this.onLoad, this);
56410             ds.on("datachanged", this.onDataChange, this);
56411             ds.on("add", this.onAdd, this);
56412             ds.on("remove", this.onRemove, this);
56413             ds.on("update", this.onUpdate, this);
56414             ds.on("clear", this.onClear, this);
56415         }
56416         this.ds = ds;
56417
56418         if(this.cm){
56419             this.cm.un("widthchange", this.onColWidthChange, this);
56420             this.cm.un("headerchange", this.onHeaderChange, this);
56421             this.cm.un("hiddenchange", this.onHiddenChange, this);
56422             this.cm.un("columnmoved", this.onColumnMove, this);
56423             this.cm.un("columnlockchange", this.onColumnLock, this);
56424         }
56425         if(cm){
56426             this.generateRules(cm);
56427             cm.on("widthchange", this.onColWidthChange, this);
56428             cm.on("headerchange", this.onHeaderChange, this);
56429             cm.on("hiddenchange", this.onHiddenChange, this);
56430             cm.on("columnmoved", this.onColumnMove, this);
56431             cm.on("columnlockchange", this.onColumnLock, this);
56432         }
56433         this.cm = cm;
56434     },
56435
56436     init: function(grid){
56437         Roo.grid.GridView.superclass.init.call(this, grid);
56438
56439         this.bind(grid.dataSource, grid.colModel);
56440
56441         grid.on("headerclick", this.handleHeaderClick, this);
56442
56443         if(grid.trackMouseOver){
56444             grid.on("mouseover", this.onRowOver, this);
56445             grid.on("mouseout", this.onRowOut, this);
56446         }
56447         grid.cancelTextSelection = function(){};
56448         this.gridId = grid.id;
56449
56450         var tpls = this.templates || {};
56451
56452         if(!tpls.master){
56453             tpls.master = new Roo.Template(
56454                '<div class="x-grid" hidefocus="true">',
56455                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
56456                   '<div class="x-grid-topbar"></div>',
56457                   '<div class="x-grid-scroller"><div></div></div>',
56458                   '<div class="x-grid-locked">',
56459                       '<div class="x-grid-header">{lockedHeader}</div>',
56460                       '<div class="x-grid-body">{lockedBody}</div>',
56461                   "</div>",
56462                   '<div class="x-grid-viewport">',
56463                       '<div class="x-grid-header">{header}</div>',
56464                       '<div class="x-grid-body">{body}</div>',
56465                   "</div>",
56466                   '<div class="x-grid-bottombar"></div>',
56467                  
56468                   '<div class="x-grid-resize-proxy">&#160;</div>',
56469                "</div>"
56470             );
56471             tpls.master.disableformats = true;
56472         }
56473
56474         if(!tpls.header){
56475             tpls.header = new Roo.Template(
56476                '<table border="0" cellspacing="0" cellpadding="0">',
56477                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
56478                "</table>{splits}"
56479             );
56480             tpls.header.disableformats = true;
56481         }
56482         tpls.header.compile();
56483
56484         if(!tpls.hcell){
56485             tpls.hcell = new Roo.Template(
56486                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
56487                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
56488                 "</div></td>"
56489              );
56490              tpls.hcell.disableFormats = true;
56491         }
56492         tpls.hcell.compile();
56493
56494         if(!tpls.hsplit){
56495             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
56496                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
56497             tpls.hsplit.disableFormats = true;
56498         }
56499         tpls.hsplit.compile();
56500
56501         if(!tpls.body){
56502             tpls.body = new Roo.Template(
56503                '<table border="0" cellspacing="0" cellpadding="0">',
56504                "<tbody>{rows}</tbody>",
56505                "</table>"
56506             );
56507             tpls.body.disableFormats = true;
56508         }
56509         tpls.body.compile();
56510
56511         if(!tpls.row){
56512             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
56513             tpls.row.disableFormats = true;
56514         }
56515         tpls.row.compile();
56516
56517         if(!tpls.cell){
56518             tpls.cell = new Roo.Template(
56519                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
56520                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
56521                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
56522                 "</td>"
56523             );
56524             tpls.cell.disableFormats = true;
56525         }
56526         tpls.cell.compile();
56527
56528         this.templates = tpls;
56529     },
56530
56531     // remap these for backwards compat
56532     onColWidthChange : function(){
56533         this.updateColumns.apply(this, arguments);
56534     },
56535     onHeaderChange : function(){
56536         this.updateHeaders.apply(this, arguments);
56537     }, 
56538     onHiddenChange : function(){
56539         this.handleHiddenChange.apply(this, arguments);
56540     },
56541     onColumnMove : function(){
56542         this.handleColumnMove.apply(this, arguments);
56543     },
56544     onColumnLock : function(){
56545         this.handleLockChange.apply(this, arguments);
56546     },
56547
56548     onDataChange : function(){
56549         this.refresh();
56550         this.updateHeaderSortState();
56551     },
56552
56553     onClear : function(){
56554         this.refresh();
56555     },
56556
56557     onUpdate : function(ds, record){
56558         this.refreshRow(record);
56559     },
56560
56561     refreshRow : function(record){
56562         var ds = this.ds, index;
56563         if(typeof record == 'number'){
56564             index = record;
56565             record = ds.getAt(index);
56566         }else{
56567             index = ds.indexOf(record);
56568         }
56569         this.insertRows(ds, index, index, true);
56570         this.onRemove(ds, record, index+1, true);
56571         this.syncRowHeights(index, index);
56572         this.layout();
56573         this.fireEvent("rowupdated", this, index, record);
56574     },
56575
56576     onAdd : function(ds, records, index){
56577         this.insertRows(ds, index, index + (records.length-1));
56578     },
56579
56580     onRemove : function(ds, record, index, isUpdate){
56581         if(isUpdate !== true){
56582             this.fireEvent("beforerowremoved", this, index, record);
56583         }
56584         var bt = this.getBodyTable(), lt = this.getLockedTable();
56585         if(bt.rows[index]){
56586             bt.firstChild.removeChild(bt.rows[index]);
56587         }
56588         if(lt.rows[index]){
56589             lt.firstChild.removeChild(lt.rows[index]);
56590         }
56591         if(isUpdate !== true){
56592             this.stripeRows(index);
56593             this.syncRowHeights(index, index);
56594             this.layout();
56595             this.fireEvent("rowremoved", this, index, record);
56596         }
56597     },
56598
56599     onLoad : function(){
56600         this.scrollToTop();
56601     },
56602
56603     /**
56604      * Scrolls the grid to the top
56605      */
56606     scrollToTop : function(){
56607         if(this.scroller){
56608             this.scroller.dom.scrollTop = 0;
56609             this.syncScroll();
56610         }
56611     },
56612
56613     /**
56614      * Gets a panel in the header of the grid that can be used for toolbars etc.
56615      * After modifying the contents of this panel a call to grid.autoSize() may be
56616      * required to register any changes in size.
56617      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
56618      * @return Roo.Element
56619      */
56620     getHeaderPanel : function(doShow){
56621         if(doShow){
56622             this.headerPanel.show();
56623         }
56624         return this.headerPanel;
56625     },
56626
56627     /**
56628      * Gets a panel in the footer of the grid that can be used for toolbars etc.
56629      * After modifying the contents of this panel a call to grid.autoSize() may be
56630      * required to register any changes in size.
56631      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
56632      * @return Roo.Element
56633      */
56634     getFooterPanel : function(doShow){
56635         if(doShow){
56636             this.footerPanel.show();
56637         }
56638         return this.footerPanel;
56639     },
56640
56641     initElements : function(){
56642         var E = Roo.Element;
56643         var el = this.grid.getGridEl().dom.firstChild;
56644         var cs = el.childNodes;
56645
56646         this.el = new E(el);
56647         
56648          this.focusEl = new E(el.firstChild);
56649         this.focusEl.swallowEvent("click", true);
56650         
56651         this.headerPanel = new E(cs[1]);
56652         this.headerPanel.enableDisplayMode("block");
56653
56654         this.scroller = new E(cs[2]);
56655         this.scrollSizer = new E(this.scroller.dom.firstChild);
56656
56657         this.lockedWrap = new E(cs[3]);
56658         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
56659         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
56660
56661         this.mainWrap = new E(cs[4]);
56662         this.mainHd = new E(this.mainWrap.dom.firstChild);
56663         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
56664
56665         this.footerPanel = new E(cs[5]);
56666         this.footerPanel.enableDisplayMode("block");
56667
56668         this.resizeProxy = new E(cs[6]);
56669
56670         this.headerSelector = String.format(
56671            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
56672            this.lockedHd.id, this.mainHd.id
56673         );
56674
56675         this.splitterSelector = String.format(
56676            '#{0} div.x-grid-split, #{1} div.x-grid-split',
56677            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
56678         );
56679     },
56680     idToCssName : function(s)
56681     {
56682         return s.replace(/[^a-z0-9]+/ig, '-');
56683     },
56684
56685     getHeaderCell : function(index){
56686         return Roo.DomQuery.select(this.headerSelector)[index];
56687     },
56688
56689     getHeaderCellMeasure : function(index){
56690         return this.getHeaderCell(index).firstChild;
56691     },
56692
56693     getHeaderCellText : function(index){
56694         return this.getHeaderCell(index).firstChild.firstChild;
56695     },
56696
56697     getLockedTable : function(){
56698         return this.lockedBody.dom.firstChild;
56699     },
56700
56701     getBodyTable : function(){
56702         return this.mainBody.dom.firstChild;
56703     },
56704
56705     getLockedRow : function(index){
56706         return this.getLockedTable().rows[index];
56707     },
56708
56709     getRow : function(index){
56710         return this.getBodyTable().rows[index];
56711     },
56712
56713     getRowComposite : function(index){
56714         if(!this.rowEl){
56715             this.rowEl = new Roo.CompositeElementLite();
56716         }
56717         var els = [], lrow, mrow;
56718         if(lrow = this.getLockedRow(index)){
56719             els.push(lrow);
56720         }
56721         if(mrow = this.getRow(index)){
56722             els.push(mrow);
56723         }
56724         this.rowEl.elements = els;
56725         return this.rowEl;
56726     },
56727     /**
56728      * Gets the 'td' of the cell
56729      * 
56730      * @param {Integer} rowIndex row to select
56731      * @param {Integer} colIndex column to select
56732      * 
56733      * @return {Object} 
56734      */
56735     getCell : function(rowIndex, colIndex){
56736         var locked = this.cm.getLockedCount();
56737         var source;
56738         if(colIndex < locked){
56739             source = this.lockedBody.dom.firstChild;
56740         }else{
56741             source = this.mainBody.dom.firstChild;
56742             colIndex -= locked;
56743         }
56744         return source.rows[rowIndex].childNodes[colIndex];
56745     },
56746
56747     getCellText : function(rowIndex, colIndex){
56748         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
56749     },
56750
56751     getCellBox : function(cell){
56752         var b = this.fly(cell).getBox();
56753         if(Roo.isOpera){ // opera fails to report the Y
56754             b.y = cell.offsetTop + this.mainBody.getY();
56755         }
56756         return b;
56757     },
56758
56759     getCellIndex : function(cell){
56760         var id = String(cell.className).match(this.cellRE);
56761         if(id){
56762             return parseInt(id[1], 10);
56763         }
56764         return 0;
56765     },
56766
56767     findHeaderIndex : function(n){
56768         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56769         return r ? this.getCellIndex(r) : false;
56770     },
56771
56772     findHeaderCell : function(n){
56773         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
56774         return r ? r : false;
56775     },
56776
56777     findRowIndex : function(n){
56778         if(!n){
56779             return false;
56780         }
56781         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
56782         return r ? r.rowIndex : false;
56783     },
56784
56785     findCellIndex : function(node){
56786         var stop = this.el.dom;
56787         while(node && node != stop){
56788             if(this.findRE.test(node.className)){
56789                 return this.getCellIndex(node);
56790             }
56791             node = node.parentNode;
56792         }
56793         return false;
56794     },
56795
56796     getColumnId : function(index){
56797         return this.cm.getColumnId(index);
56798     },
56799
56800     getSplitters : function()
56801     {
56802         if(this.splitterSelector){
56803            return Roo.DomQuery.select(this.splitterSelector);
56804         }else{
56805             return null;
56806       }
56807     },
56808
56809     getSplitter : function(index){
56810         return this.getSplitters()[index];
56811     },
56812
56813     onRowOver : function(e, t){
56814         var row;
56815         if((row = this.findRowIndex(t)) !== false){
56816             this.getRowComposite(row).addClass("x-grid-row-over");
56817         }
56818     },
56819
56820     onRowOut : function(e, t){
56821         var row;
56822         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
56823             this.getRowComposite(row).removeClass("x-grid-row-over");
56824         }
56825     },
56826
56827     renderHeaders : function(){
56828         var cm = this.cm;
56829         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
56830         var cb = [], lb = [], sb = [], lsb = [], p = {};
56831         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56832             p.cellId = "x-grid-hd-0-" + i;
56833             p.splitId = "x-grid-csplit-0-" + i;
56834             p.id = cm.getColumnId(i);
56835             p.value = cm.getColumnHeader(i) || "";
56836             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
56837             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
56838             if(!cm.isLocked(i)){
56839                 cb[cb.length] = ct.apply(p);
56840                 sb[sb.length] = st.apply(p);
56841             }else{
56842                 lb[lb.length] = ct.apply(p);
56843                 lsb[lsb.length] = st.apply(p);
56844             }
56845         }
56846         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
56847                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
56848     },
56849
56850     updateHeaders : function(){
56851         var html = this.renderHeaders();
56852         this.lockedHd.update(html[0]);
56853         this.mainHd.update(html[1]);
56854     },
56855
56856     /**
56857      * Focuses the specified row.
56858      * @param {Number} row The row index
56859      */
56860     focusRow : function(row)
56861     {
56862         //Roo.log('GridView.focusRow');
56863         var x = this.scroller.dom.scrollLeft;
56864         this.focusCell(row, 0, false);
56865         this.scroller.dom.scrollLeft = x;
56866     },
56867
56868     /**
56869      * Focuses the specified cell.
56870      * @param {Number} row The row index
56871      * @param {Number} col The column index
56872      * @param {Boolean} hscroll false to disable horizontal scrolling
56873      */
56874     focusCell : function(row, col, hscroll)
56875     {
56876         //Roo.log('GridView.focusCell');
56877         var el = this.ensureVisible(row, col, hscroll);
56878         this.focusEl.alignTo(el, "tl-tl");
56879         if(Roo.isGecko){
56880             this.focusEl.focus();
56881         }else{
56882             this.focusEl.focus.defer(1, this.focusEl);
56883         }
56884     },
56885
56886     /**
56887      * Scrolls the specified cell into view
56888      * @param {Number} row The row index
56889      * @param {Number} col The column index
56890      * @param {Boolean} hscroll false to disable horizontal scrolling
56891      */
56892     ensureVisible : function(row, col, hscroll)
56893     {
56894         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
56895         //return null; //disable for testing.
56896         if(typeof row != "number"){
56897             row = row.rowIndex;
56898         }
56899         if(row < 0 && row >= this.ds.getCount()){
56900             return  null;
56901         }
56902         col = (col !== undefined ? col : 0);
56903         var cm = this.grid.colModel;
56904         while(cm.isHidden(col)){
56905             col++;
56906         }
56907
56908         var el = this.getCell(row, col);
56909         if(!el){
56910             return null;
56911         }
56912         var c = this.scroller.dom;
56913
56914         var ctop = parseInt(el.offsetTop, 10);
56915         var cleft = parseInt(el.offsetLeft, 10);
56916         var cbot = ctop + el.offsetHeight;
56917         var cright = cleft + el.offsetWidth;
56918         
56919         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
56920         var stop = parseInt(c.scrollTop, 10);
56921         var sleft = parseInt(c.scrollLeft, 10);
56922         var sbot = stop + ch;
56923         var sright = sleft + c.clientWidth;
56924         /*
56925         Roo.log('GridView.ensureVisible:' +
56926                 ' ctop:' + ctop +
56927                 ' c.clientHeight:' + c.clientHeight +
56928                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
56929                 ' stop:' + stop +
56930                 ' cbot:' + cbot +
56931                 ' sbot:' + sbot +
56932                 ' ch:' + ch  
56933                 );
56934         */
56935         if(ctop < stop){
56936             c.scrollTop = ctop;
56937             //Roo.log("set scrolltop to ctop DISABLE?");
56938         }else if(cbot > sbot){
56939             //Roo.log("set scrolltop to cbot-ch");
56940             c.scrollTop = cbot-ch;
56941         }
56942         
56943         if(hscroll !== false){
56944             if(cleft < sleft){
56945                 c.scrollLeft = cleft;
56946             }else if(cright > sright){
56947                 c.scrollLeft = cright-c.clientWidth;
56948             }
56949         }
56950          
56951         return el;
56952     },
56953
56954     updateColumns : function(){
56955         this.grid.stopEditing();
56956         var cm = this.grid.colModel, colIds = this.getColumnIds();
56957         //var totalWidth = cm.getTotalWidth();
56958         var pos = 0;
56959         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56960             //if(cm.isHidden(i)) continue;
56961             var w = cm.getColumnWidth(i);
56962             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56963             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
56964         }
56965         this.updateSplitters();
56966     },
56967
56968     generateRules : function(cm){
56969         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
56970         Roo.util.CSS.removeStyleSheet(rulesId);
56971         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56972             var cid = cm.getColumnId(i);
56973             var align = '';
56974             if(cm.config[i].align){
56975                 align = 'text-align:'+cm.config[i].align+';';
56976             }
56977             var hidden = '';
56978             if(cm.isHidden(i)){
56979                 hidden = 'display:none;';
56980             }
56981             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
56982             ruleBuf.push(
56983                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
56984                     this.hdSelector, cid, " {\n", align, width, "}\n",
56985                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
56986                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
56987         }
56988         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
56989     },
56990
56991     updateSplitters : function(){
56992         var cm = this.cm, s = this.getSplitters();
56993         if(s){ // splitters not created yet
56994             var pos = 0, locked = true;
56995             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
56996                 if(cm.isHidden(i)) {
56997                     continue;
56998                 }
56999                 var w = cm.getColumnWidth(i); // make sure it's a number
57000                 if(!cm.isLocked(i) && locked){
57001                     pos = 0;
57002                     locked = false;
57003                 }
57004                 pos += w;
57005                 s[i].style.left = (pos-this.splitOffset) + "px";
57006             }
57007         }
57008     },
57009
57010     handleHiddenChange : function(colModel, colIndex, hidden){
57011         if(hidden){
57012             this.hideColumn(colIndex);
57013         }else{
57014             this.unhideColumn(colIndex);
57015         }
57016     },
57017
57018     hideColumn : function(colIndex){
57019         var cid = this.getColumnId(colIndex);
57020         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
57021         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
57022         if(Roo.isSafari){
57023             this.updateHeaders();
57024         }
57025         this.updateSplitters();
57026         this.layout();
57027     },
57028
57029     unhideColumn : function(colIndex){
57030         var cid = this.getColumnId(colIndex);
57031         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
57032         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
57033
57034         if(Roo.isSafari){
57035             this.updateHeaders();
57036         }
57037         this.updateSplitters();
57038         this.layout();
57039     },
57040
57041     insertRows : function(dm, firstRow, lastRow, isUpdate){
57042         if(firstRow == 0 && lastRow == dm.getCount()-1){
57043             this.refresh();
57044         }else{
57045             if(!isUpdate){
57046                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
57047             }
57048             var s = this.getScrollState();
57049             var markup = this.renderRows(firstRow, lastRow);
57050             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
57051             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
57052             this.restoreScroll(s);
57053             if(!isUpdate){
57054                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
57055                 this.syncRowHeights(firstRow, lastRow);
57056                 this.stripeRows(firstRow);
57057                 this.layout();
57058             }
57059         }
57060     },
57061
57062     bufferRows : function(markup, target, index){
57063         var before = null, trows = target.rows, tbody = target.tBodies[0];
57064         if(index < trows.length){
57065             before = trows[index];
57066         }
57067         var b = document.createElement("div");
57068         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
57069         var rows = b.firstChild.rows;
57070         for(var i = 0, len = rows.length; i < len; i++){
57071             if(before){
57072                 tbody.insertBefore(rows[0], before);
57073             }else{
57074                 tbody.appendChild(rows[0]);
57075             }
57076         }
57077         b.innerHTML = "";
57078         b = null;
57079     },
57080
57081     deleteRows : function(dm, firstRow, lastRow){
57082         if(dm.getRowCount()<1){
57083             this.fireEvent("beforerefresh", this);
57084             this.mainBody.update("");
57085             this.lockedBody.update("");
57086             this.fireEvent("refresh", this);
57087         }else{
57088             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
57089             var bt = this.getBodyTable();
57090             var tbody = bt.firstChild;
57091             var rows = bt.rows;
57092             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
57093                 tbody.removeChild(rows[firstRow]);
57094             }
57095             this.stripeRows(firstRow);
57096             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
57097         }
57098     },
57099
57100     updateRows : function(dataSource, firstRow, lastRow){
57101         var s = this.getScrollState();
57102         this.refresh();
57103         this.restoreScroll(s);
57104     },
57105
57106     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
57107         if(!noRefresh){
57108            this.refresh();
57109         }
57110         this.updateHeaderSortState();
57111     },
57112
57113     getScrollState : function(){
57114         
57115         var sb = this.scroller.dom;
57116         return {left: sb.scrollLeft, top: sb.scrollTop};
57117     },
57118
57119     stripeRows : function(startRow){
57120         if(!this.grid.stripeRows || this.ds.getCount() < 1){
57121             return;
57122         }
57123         startRow = startRow || 0;
57124         var rows = this.getBodyTable().rows;
57125         var lrows = this.getLockedTable().rows;
57126         var cls = ' x-grid-row-alt ';
57127         for(var i = startRow, len = rows.length; i < len; i++){
57128             var row = rows[i], lrow = lrows[i];
57129             var isAlt = ((i+1) % 2 == 0);
57130             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
57131             if(isAlt == hasAlt){
57132                 continue;
57133             }
57134             if(isAlt){
57135                 row.className += " x-grid-row-alt";
57136             }else{
57137                 row.className = row.className.replace("x-grid-row-alt", "");
57138             }
57139             if(lrow){
57140                 lrow.className = row.className;
57141             }
57142         }
57143     },
57144
57145     restoreScroll : function(state){
57146         //Roo.log('GridView.restoreScroll');
57147         var sb = this.scroller.dom;
57148         sb.scrollLeft = state.left;
57149         sb.scrollTop = state.top;
57150         this.syncScroll();
57151     },
57152
57153     syncScroll : function(){
57154         //Roo.log('GridView.syncScroll');
57155         var sb = this.scroller.dom;
57156         var sh = this.mainHd.dom;
57157         var bs = this.mainBody.dom;
57158         var lv = this.lockedBody.dom;
57159         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
57160         lv.scrollTop = bs.scrollTop = sb.scrollTop;
57161     },
57162
57163     handleScroll : function(e){
57164         this.syncScroll();
57165         var sb = this.scroller.dom;
57166         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
57167         e.stopEvent();
57168     },
57169
57170     handleWheel : function(e){
57171         var d = e.getWheelDelta();
57172         this.scroller.dom.scrollTop -= d*22;
57173         // set this here to prevent jumpy scrolling on large tables
57174         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
57175         e.stopEvent();
57176     },
57177
57178     renderRows : function(startRow, endRow){
57179         // pull in all the crap needed to render rows
57180         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
57181         var colCount = cm.getColumnCount();
57182
57183         if(ds.getCount() < 1){
57184             return ["", ""];
57185         }
57186
57187         // build a map for all the columns
57188         var cs = [];
57189         for(var i = 0; i < colCount; i++){
57190             var name = cm.getDataIndex(i);
57191             cs[i] = {
57192                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
57193                 renderer : cm.getRenderer(i),
57194                 id : cm.getColumnId(i),
57195                 locked : cm.isLocked(i),
57196                 has_editor : cm.isCellEditable(i)
57197             };
57198         }
57199
57200         startRow = startRow || 0;
57201         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
57202
57203         // records to render
57204         var rs = ds.getRange(startRow, endRow);
57205
57206         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
57207     },
57208
57209     // As much as I hate to duplicate code, this was branched because FireFox really hates
57210     // [].join("") on strings. The performance difference was substantial enough to
57211     // branch this function
57212     doRender : Roo.isGecko ?
57213             function(cs, rs, ds, startRow, colCount, stripe){
57214                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57215                 // buffers
57216                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57217                 
57218                 var hasListener = this.grid.hasListener('rowclass');
57219                 var rowcfg = {};
57220                 for(var j = 0, len = rs.length; j < len; j++){
57221                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
57222                     for(var i = 0; i < colCount; i++){
57223                         c = cs[i];
57224                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57225                         p.id = c.id;
57226                         p.css = p.attr = "";
57227                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57228                         if(p.value == undefined || p.value === "") {
57229                             p.value = "&#160;";
57230                         }
57231                         if(c.has_editor){
57232                             p.css += ' x-grid-editable-cell';
57233                         }
57234                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
57235                             p.css +=  ' x-grid-dirty-cell';
57236                         }
57237                         var markup = ct.apply(p);
57238                         if(!c.locked){
57239                             cb+= markup;
57240                         }else{
57241                             lcb+= markup;
57242                         }
57243                     }
57244                     var alt = [];
57245                     if(stripe && ((rowIndex+1) % 2 == 0)){
57246                         alt.push("x-grid-row-alt")
57247                     }
57248                     if(r.dirty){
57249                         alt.push(  " x-grid-dirty-row");
57250                     }
57251                     rp.cells = lcb;
57252                     if(this.getRowClass){
57253                         alt.push(this.getRowClass(r, rowIndex));
57254                     }
57255                     if (hasListener) {
57256                         rowcfg = {
57257                              
57258                             record: r,
57259                             rowIndex : rowIndex,
57260                             rowClass : ''
57261                         };
57262                         this.grid.fireEvent('rowclass', this, rowcfg);
57263                         alt.push(rowcfg.rowClass);
57264                     }
57265                     rp.alt = alt.join(" ");
57266                     lbuf+= rt.apply(rp);
57267                     rp.cells = cb;
57268                     buf+=  rt.apply(rp);
57269                 }
57270                 return [lbuf, buf];
57271             } :
57272             function(cs, rs, ds, startRow, colCount, stripe){
57273                 var ts = this.templates, ct = ts.cell, rt = ts.row;
57274                 // buffers
57275                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
57276                 var hasListener = this.grid.hasListener('rowclass');
57277  
57278                 var rowcfg = {};
57279                 for(var j = 0, len = rs.length; j < len; j++){
57280                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
57281                     for(var i = 0; i < colCount; i++){
57282                         c = cs[i];
57283                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
57284                         p.id = c.id;
57285                         p.css = p.attr = "";
57286                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
57287                         if(p.value == undefined || p.value === "") {
57288                             p.value = "&#160;";
57289                         }
57290                         //Roo.log(c);
57291                          if(c.has_editor){
57292                             p.css += ' x-grid-editable-cell';
57293                         }
57294                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
57295                             p.css += ' x-grid-dirty-cell' 
57296                         }
57297                         
57298                         var markup = ct.apply(p);
57299                         if(!c.locked){
57300                             cb[cb.length] = markup;
57301                         }else{
57302                             lcb[lcb.length] = markup;
57303                         }
57304                     }
57305                     var alt = [];
57306                     if(stripe && ((rowIndex+1) % 2 == 0)){
57307                         alt.push( "x-grid-row-alt");
57308                     }
57309                     if(r.dirty){
57310                         alt.push(" x-grid-dirty-row");
57311                     }
57312                     rp.cells = lcb;
57313                     if(this.getRowClass){
57314                         alt.push( this.getRowClass(r, rowIndex));
57315                     }
57316                     if (hasListener) {
57317                         rowcfg = {
57318                              
57319                             record: r,
57320                             rowIndex : rowIndex,
57321                             rowClass : ''
57322                         };
57323                         this.grid.fireEvent('rowclass', this, rowcfg);
57324                         alt.push(rowcfg.rowClass);
57325                     }
57326                     
57327                     rp.alt = alt.join(" ");
57328                     rp.cells = lcb.join("");
57329                     lbuf[lbuf.length] = rt.apply(rp);
57330                     rp.cells = cb.join("");
57331                     buf[buf.length] =  rt.apply(rp);
57332                 }
57333                 return [lbuf.join(""), buf.join("")];
57334             },
57335
57336     renderBody : function(){
57337         var markup = this.renderRows();
57338         var bt = this.templates.body;
57339         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
57340     },
57341
57342     /**
57343      * Refreshes the grid
57344      * @param {Boolean} headersToo
57345      */
57346     refresh : function(headersToo){
57347         this.fireEvent("beforerefresh", this);
57348         this.grid.stopEditing();
57349         var result = this.renderBody();
57350         this.lockedBody.update(result[0]);
57351         this.mainBody.update(result[1]);
57352         if(headersToo === true){
57353             this.updateHeaders();
57354             this.updateColumns();
57355             this.updateSplitters();
57356             this.updateHeaderSortState();
57357         }
57358         this.syncRowHeights();
57359         this.layout();
57360         this.fireEvent("refresh", this);
57361     },
57362
57363     handleColumnMove : function(cm, oldIndex, newIndex){
57364         this.indexMap = null;
57365         var s = this.getScrollState();
57366         this.refresh(true);
57367         this.restoreScroll(s);
57368         this.afterMove(newIndex);
57369     },
57370
57371     afterMove : function(colIndex){
57372         if(this.enableMoveAnim && Roo.enableFx){
57373             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
57374         }
57375         // if multisort - fix sortOrder, and reload..
57376         if (this.grid.dataSource.multiSort) {
57377             // the we can call sort again..
57378             var dm = this.grid.dataSource;
57379             var cm = this.grid.colModel;
57380             var so = [];
57381             for(var i = 0; i < cm.config.length; i++ ) {
57382                 
57383                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
57384                     continue; // dont' bother, it's not in sort list or being set.
57385                 }
57386                 
57387                 so.push(cm.config[i].dataIndex);
57388             };
57389             dm.sortOrder = so;
57390             dm.load(dm.lastOptions);
57391             
57392             
57393         }
57394         
57395     },
57396
57397     updateCell : function(dm, rowIndex, dataIndex){
57398         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
57399         if(typeof colIndex == "undefined"){ // not present in grid
57400             return;
57401         }
57402         var cm = this.grid.colModel;
57403         var cell = this.getCell(rowIndex, colIndex);
57404         var cellText = this.getCellText(rowIndex, colIndex);
57405
57406         var p = {
57407             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
57408             id : cm.getColumnId(colIndex),
57409             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
57410         };
57411         var renderer = cm.getRenderer(colIndex);
57412         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
57413         if(typeof val == "undefined" || val === "") {
57414             val = "&#160;";
57415         }
57416         cellText.innerHTML = val;
57417         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
57418         this.syncRowHeights(rowIndex, rowIndex);
57419     },
57420
57421     calcColumnWidth : function(colIndex, maxRowsToMeasure){
57422         var maxWidth = 0;
57423         if(this.grid.autoSizeHeaders){
57424             var h = this.getHeaderCellMeasure(colIndex);
57425             maxWidth = Math.max(maxWidth, h.scrollWidth);
57426         }
57427         var tb, index;
57428         if(this.cm.isLocked(colIndex)){
57429             tb = this.getLockedTable();
57430             index = colIndex;
57431         }else{
57432             tb = this.getBodyTable();
57433             index = colIndex - this.cm.getLockedCount();
57434         }
57435         if(tb && tb.rows){
57436             var rows = tb.rows;
57437             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
57438             for(var i = 0; i < stopIndex; i++){
57439                 var cell = rows[i].childNodes[index].firstChild;
57440                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
57441             }
57442         }
57443         return maxWidth + /*margin for error in IE*/ 5;
57444     },
57445     /**
57446      * Autofit a column to its content.
57447      * @param {Number} colIndex
57448      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
57449      */
57450      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
57451          if(this.cm.isHidden(colIndex)){
57452              return; // can't calc a hidden column
57453          }
57454         if(forceMinSize){
57455             var cid = this.cm.getColumnId(colIndex);
57456             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
57457            if(this.grid.autoSizeHeaders){
57458                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
57459            }
57460         }
57461         var newWidth = this.calcColumnWidth(colIndex);
57462         this.cm.setColumnWidth(colIndex,
57463             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
57464         if(!suppressEvent){
57465             this.grid.fireEvent("columnresize", colIndex, newWidth);
57466         }
57467     },
57468
57469     /**
57470      * Autofits all columns to their content and then expands to fit any extra space in the grid
57471      */
57472      autoSizeColumns : function(){
57473         var cm = this.grid.colModel;
57474         var colCount = cm.getColumnCount();
57475         for(var i = 0; i < colCount; i++){
57476             this.autoSizeColumn(i, true, true);
57477         }
57478         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
57479             this.fitColumns();
57480         }else{
57481             this.updateColumns();
57482             this.layout();
57483         }
57484     },
57485
57486     /**
57487      * Autofits all columns to the grid's width proportionate with their current size
57488      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
57489      */
57490     fitColumns : function(reserveScrollSpace){
57491         var cm = this.grid.colModel;
57492         var colCount = cm.getColumnCount();
57493         var cols = [];
57494         var width = 0;
57495         var i, w;
57496         for (i = 0; i < colCount; i++){
57497             if(!cm.isHidden(i) && !cm.isFixed(i)){
57498                 w = cm.getColumnWidth(i);
57499                 cols.push(i);
57500                 cols.push(w);
57501                 width += w;
57502             }
57503         }
57504         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
57505         if(reserveScrollSpace){
57506             avail -= 17;
57507         }
57508         var frac = (avail - cm.getTotalWidth())/width;
57509         while (cols.length){
57510             w = cols.pop();
57511             i = cols.pop();
57512             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
57513         }
57514         this.updateColumns();
57515         this.layout();
57516     },
57517
57518     onRowSelect : function(rowIndex){
57519         var row = this.getRowComposite(rowIndex);
57520         row.addClass("x-grid-row-selected");
57521     },
57522
57523     onRowDeselect : function(rowIndex){
57524         var row = this.getRowComposite(rowIndex);
57525         row.removeClass("x-grid-row-selected");
57526     },
57527
57528     onCellSelect : function(row, col){
57529         var cell = this.getCell(row, col);
57530         if(cell){
57531             Roo.fly(cell).addClass("x-grid-cell-selected");
57532         }
57533     },
57534
57535     onCellDeselect : function(row, col){
57536         var cell = this.getCell(row, col);
57537         if(cell){
57538             Roo.fly(cell).removeClass("x-grid-cell-selected");
57539         }
57540     },
57541
57542     updateHeaderSortState : function(){
57543         
57544         // sort state can be single { field: xxx, direction : yyy}
57545         // or   { xxx=>ASC , yyy : DESC ..... }
57546         
57547         var mstate = {};
57548         if (!this.ds.multiSort) { 
57549             var state = this.ds.getSortState();
57550             if(!state){
57551                 return;
57552             }
57553             mstate[state.field] = state.direction;
57554             // FIXME... - this is not used here.. but might be elsewhere..
57555             this.sortState = state;
57556             
57557         } else {
57558             mstate = this.ds.sortToggle;
57559         }
57560         //remove existing sort classes..
57561         
57562         var sc = this.sortClasses;
57563         var hds = this.el.select(this.headerSelector).removeClass(sc);
57564         
57565         for(var f in mstate) {
57566         
57567             var sortColumn = this.cm.findColumnIndex(f);
57568             
57569             if(sortColumn != -1){
57570                 var sortDir = mstate[f];        
57571                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
57572             }
57573         }
57574         
57575          
57576         
57577     },
57578
57579
57580     handleHeaderClick : function(g, index,e){
57581         
57582         Roo.log("header click");
57583         
57584         if (Roo.isTouch) {
57585             // touch events on header are handled by context
57586             this.handleHdCtx(g,index,e);
57587             return;
57588         }
57589         
57590         
57591         if(this.headersDisabled){
57592             return;
57593         }
57594         var dm = g.dataSource, cm = g.colModel;
57595         if(!cm.isSortable(index)){
57596             return;
57597         }
57598         g.stopEditing();
57599         
57600         if (dm.multiSort) {
57601             // update the sortOrder
57602             var so = [];
57603             for(var i = 0; i < cm.config.length; i++ ) {
57604                 
57605                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
57606                     continue; // dont' bother, it's not in sort list or being set.
57607                 }
57608                 
57609                 so.push(cm.config[i].dataIndex);
57610             };
57611             dm.sortOrder = so;
57612         }
57613         
57614         
57615         dm.sort(cm.getDataIndex(index));
57616     },
57617
57618
57619     destroy : function(){
57620         if(this.colMenu){
57621             this.colMenu.removeAll();
57622             Roo.menu.MenuMgr.unregister(this.colMenu);
57623             this.colMenu.getEl().remove();
57624             delete this.colMenu;
57625         }
57626         if(this.hmenu){
57627             this.hmenu.removeAll();
57628             Roo.menu.MenuMgr.unregister(this.hmenu);
57629             this.hmenu.getEl().remove();
57630             delete this.hmenu;
57631         }
57632         if(this.grid.enableColumnMove){
57633             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57634             if(dds){
57635                 for(var dd in dds){
57636                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
57637                         var elid = dds[dd].dragElId;
57638                         dds[dd].unreg();
57639                         Roo.get(elid).remove();
57640                     } else if(dds[dd].config.isTarget){
57641                         dds[dd].proxyTop.remove();
57642                         dds[dd].proxyBottom.remove();
57643                         dds[dd].unreg();
57644                     }
57645                     if(Roo.dd.DDM.locationCache[dd]){
57646                         delete Roo.dd.DDM.locationCache[dd];
57647                     }
57648                 }
57649                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
57650             }
57651         }
57652         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
57653         this.bind(null, null);
57654         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
57655     },
57656
57657     handleLockChange : function(){
57658         this.refresh(true);
57659     },
57660
57661     onDenyColumnLock : function(){
57662
57663     },
57664
57665     onDenyColumnHide : function(){
57666
57667     },
57668
57669     handleHdMenuClick : function(item){
57670         var index = this.hdCtxIndex;
57671         var cm = this.cm, ds = this.ds;
57672         switch(item.id){
57673             case "asc":
57674                 ds.sort(cm.getDataIndex(index), "ASC");
57675                 break;
57676             case "desc":
57677                 ds.sort(cm.getDataIndex(index), "DESC");
57678                 break;
57679             case "lock":
57680                 var lc = cm.getLockedCount();
57681                 if(cm.getColumnCount(true) <= lc+1){
57682                     this.onDenyColumnLock();
57683                     return;
57684                 }
57685                 if(lc != index){
57686                     cm.setLocked(index, true, true);
57687                     cm.moveColumn(index, lc);
57688                     this.grid.fireEvent("columnmove", index, lc);
57689                 }else{
57690                     cm.setLocked(index, true);
57691                 }
57692             break;
57693             case "unlock":
57694                 var lc = cm.getLockedCount();
57695                 if((lc-1) != index){
57696                     cm.setLocked(index, false, true);
57697                     cm.moveColumn(index, lc-1);
57698                     this.grid.fireEvent("columnmove", index, lc-1);
57699                 }else{
57700                     cm.setLocked(index, false);
57701                 }
57702             break;
57703             case 'wider': // used to expand cols on touch..
57704             case 'narrow':
57705                 var cw = cm.getColumnWidth(index);
57706                 cw += (item.id == 'wider' ? 1 : -1) * 50;
57707                 cw = Math.max(0, cw);
57708                 cw = Math.min(cw,4000);
57709                 cm.setColumnWidth(index, cw);
57710                 break;
57711                 
57712             default:
57713                 index = cm.getIndexById(item.id.substr(4));
57714                 if(index != -1){
57715                     if(item.checked && cm.getColumnCount(true) <= 1){
57716                         this.onDenyColumnHide();
57717                         return false;
57718                     }
57719                     cm.setHidden(index, item.checked);
57720                 }
57721         }
57722         return true;
57723     },
57724
57725     beforeColMenuShow : function(){
57726         var cm = this.cm,  colCount = cm.getColumnCount();
57727         this.colMenu.removeAll();
57728         for(var i = 0; i < colCount; i++){
57729             this.colMenu.add(new Roo.menu.CheckItem({
57730                 id: "col-"+cm.getColumnId(i),
57731                 text: cm.getColumnHeader(i),
57732                 checked: !cm.isHidden(i),
57733                 hideOnClick:false
57734             }));
57735         }
57736     },
57737
57738     handleHdCtx : function(g, index, e){
57739         e.stopEvent();
57740         var hd = this.getHeaderCell(index);
57741         this.hdCtxIndex = index;
57742         var ms = this.hmenu.items, cm = this.cm;
57743         ms.get("asc").setDisabled(!cm.isSortable(index));
57744         ms.get("desc").setDisabled(!cm.isSortable(index));
57745         if(this.grid.enableColLock !== false){
57746             ms.get("lock").setDisabled(cm.isLocked(index));
57747             ms.get("unlock").setDisabled(!cm.isLocked(index));
57748         }
57749         this.hmenu.show(hd, "tl-bl");
57750     },
57751
57752     handleHdOver : function(e){
57753         var hd = this.findHeaderCell(e.getTarget());
57754         if(hd && !this.headersDisabled){
57755             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
57756                this.fly(hd).addClass("x-grid-hd-over");
57757             }
57758         }
57759     },
57760
57761     handleHdOut : function(e){
57762         var hd = this.findHeaderCell(e.getTarget());
57763         if(hd){
57764             this.fly(hd).removeClass("x-grid-hd-over");
57765         }
57766     },
57767
57768     handleSplitDblClick : function(e, t){
57769         var i = this.getCellIndex(t);
57770         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
57771             this.autoSizeColumn(i, true);
57772             this.layout();
57773         }
57774     },
57775
57776     render : function(){
57777
57778         var cm = this.cm;
57779         var colCount = cm.getColumnCount();
57780
57781         if(this.grid.monitorWindowResize === true){
57782             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
57783         }
57784         var header = this.renderHeaders();
57785         var body = this.templates.body.apply({rows:""});
57786         var html = this.templates.master.apply({
57787             lockedBody: body,
57788             body: body,
57789             lockedHeader: header[0],
57790             header: header[1]
57791         });
57792
57793         //this.updateColumns();
57794
57795         this.grid.getGridEl().dom.innerHTML = html;
57796
57797         this.initElements();
57798         
57799         // a kludge to fix the random scolling effect in webkit
57800         this.el.on("scroll", function() {
57801             this.el.dom.scrollTop=0; // hopefully not recursive..
57802         },this);
57803
57804         this.scroller.on("scroll", this.handleScroll, this);
57805         this.lockedBody.on("mousewheel", this.handleWheel, this);
57806         this.mainBody.on("mousewheel", this.handleWheel, this);
57807
57808         this.mainHd.on("mouseover", this.handleHdOver, this);
57809         this.mainHd.on("mouseout", this.handleHdOut, this);
57810         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
57811                 {delegate: "."+this.splitClass});
57812
57813         this.lockedHd.on("mouseover", this.handleHdOver, this);
57814         this.lockedHd.on("mouseout", this.handleHdOut, this);
57815         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
57816                 {delegate: "."+this.splitClass});
57817
57818         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
57819             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57820         }
57821
57822         this.updateSplitters();
57823
57824         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
57825             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57826             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
57827         }
57828
57829         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
57830             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
57831             this.hmenu.add(
57832                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
57833                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
57834             );
57835             if(this.grid.enableColLock !== false){
57836                 this.hmenu.add('-',
57837                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
57838                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
57839                 );
57840             }
57841             if (Roo.isTouch) {
57842                  this.hmenu.add('-',
57843                     {id:"wider", text: this.columnsWiderText},
57844                     {id:"narrow", text: this.columnsNarrowText }
57845                 );
57846                 
57847                  
57848             }
57849             
57850             if(this.grid.enableColumnHide !== false){
57851
57852                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
57853                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
57854                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
57855
57856                 this.hmenu.add('-',
57857                     {id:"columns", text: this.columnsText, menu: this.colMenu}
57858                 );
57859             }
57860             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
57861
57862             this.grid.on("headercontextmenu", this.handleHdCtx, this);
57863         }
57864
57865         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
57866             this.dd = new Roo.grid.GridDragZone(this.grid, {
57867                 ddGroup : this.grid.ddGroup || 'GridDD'
57868             });
57869             
57870         }
57871
57872         /*
57873         for(var i = 0; i < colCount; i++){
57874             if(cm.isHidden(i)){
57875                 this.hideColumn(i);
57876             }
57877             if(cm.config[i].align){
57878                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
57879                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
57880             }
57881         }*/
57882         
57883         this.updateHeaderSortState();
57884
57885         this.beforeInitialResize();
57886         this.layout(true);
57887
57888         // two part rendering gives faster view to the user
57889         this.renderPhase2.defer(1, this);
57890     },
57891
57892     renderPhase2 : function(){
57893         // render the rows now
57894         this.refresh();
57895         if(this.grid.autoSizeColumns){
57896             this.autoSizeColumns();
57897         }
57898     },
57899
57900     beforeInitialResize : function(){
57901
57902     },
57903
57904     onColumnSplitterMoved : function(i, w){
57905         this.userResized = true;
57906         var cm = this.grid.colModel;
57907         cm.setColumnWidth(i, w, true);
57908         var cid = cm.getColumnId(i);
57909         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57910         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
57911         this.updateSplitters();
57912         this.layout();
57913         this.grid.fireEvent("columnresize", i, w);
57914     },
57915
57916     syncRowHeights : function(startIndex, endIndex){
57917         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
57918             startIndex = startIndex || 0;
57919             var mrows = this.getBodyTable().rows;
57920             var lrows = this.getLockedTable().rows;
57921             var len = mrows.length-1;
57922             endIndex = Math.min(endIndex || len, len);
57923             for(var i = startIndex; i <= endIndex; i++){
57924                 var m = mrows[i], l = lrows[i];
57925                 var h = Math.max(m.offsetHeight, l.offsetHeight);
57926                 m.style.height = l.style.height = h + "px";
57927             }
57928         }
57929     },
57930
57931     layout : function(initialRender, is2ndPass)
57932     {
57933         var g = this.grid;
57934         var auto = g.autoHeight;
57935         var scrollOffset = 16;
57936         var c = g.getGridEl(), cm = this.cm,
57937                 expandCol = g.autoExpandColumn,
57938                 gv = this;
57939         //c.beginMeasure();
57940
57941         if(!c.dom.offsetWidth){ // display:none?
57942             if(initialRender){
57943                 this.lockedWrap.show();
57944                 this.mainWrap.show();
57945             }
57946             return;
57947         }
57948
57949         var hasLock = this.cm.isLocked(0);
57950
57951         var tbh = this.headerPanel.getHeight();
57952         var bbh = this.footerPanel.getHeight();
57953
57954         if(auto){
57955             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
57956             var newHeight = ch + c.getBorderWidth("tb");
57957             if(g.maxHeight){
57958                 newHeight = Math.min(g.maxHeight, newHeight);
57959             }
57960             c.setHeight(newHeight);
57961         }
57962
57963         if(g.autoWidth){
57964             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
57965         }
57966
57967         var s = this.scroller;
57968
57969         var csize = c.getSize(true);
57970
57971         this.el.setSize(csize.width, csize.height);
57972
57973         this.headerPanel.setWidth(csize.width);
57974         this.footerPanel.setWidth(csize.width);
57975
57976         var hdHeight = this.mainHd.getHeight();
57977         var vw = csize.width;
57978         var vh = csize.height - (tbh + bbh);
57979
57980         s.setSize(vw, vh);
57981
57982         var bt = this.getBodyTable();
57983         
57984         if(cm.getLockedCount() == cm.config.length){
57985             bt = this.getLockedTable();
57986         }
57987         
57988         var ltWidth = hasLock ?
57989                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
57990
57991         var scrollHeight = bt.offsetHeight;
57992         var scrollWidth = ltWidth + bt.offsetWidth;
57993         var vscroll = false, hscroll = false;
57994
57995         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
57996
57997         var lw = this.lockedWrap, mw = this.mainWrap;
57998         var lb = this.lockedBody, mb = this.mainBody;
57999
58000         setTimeout(function(){
58001             var t = s.dom.offsetTop;
58002             var w = s.dom.clientWidth,
58003                 h = s.dom.clientHeight;
58004
58005             lw.setTop(t);
58006             lw.setSize(ltWidth, h);
58007
58008             mw.setLeftTop(ltWidth, t);
58009             mw.setSize(w-ltWidth, h);
58010
58011             lb.setHeight(h-hdHeight);
58012             mb.setHeight(h-hdHeight);
58013
58014             if(is2ndPass !== true && !gv.userResized && expandCol){
58015                 // high speed resize without full column calculation
58016                 
58017                 var ci = cm.getIndexById(expandCol);
58018                 if (ci < 0) {
58019                     ci = cm.findColumnIndex(expandCol);
58020                 }
58021                 ci = Math.max(0, ci); // make sure it's got at least the first col.
58022                 var expandId = cm.getColumnId(ci);
58023                 var  tw = cm.getTotalWidth(false);
58024                 var currentWidth = cm.getColumnWidth(ci);
58025                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
58026                 if(currentWidth != cw){
58027                     cm.setColumnWidth(ci, cw, true);
58028                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58029                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
58030                     gv.updateSplitters();
58031                     gv.layout(false, true);
58032                 }
58033             }
58034
58035             if(initialRender){
58036                 lw.show();
58037                 mw.show();
58038             }
58039             //c.endMeasure();
58040         }, 10);
58041     },
58042
58043     onWindowResize : function(){
58044         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
58045             return;
58046         }
58047         this.layout();
58048     },
58049
58050     appendFooter : function(parentEl){
58051         return null;
58052     },
58053
58054     sortAscText : "Sort Ascending",
58055     sortDescText : "Sort Descending",
58056     lockText : "Lock Column",
58057     unlockText : "Unlock Column",
58058     columnsText : "Columns",
58059  
58060     columnsWiderText : "Wider",
58061     columnsNarrowText : "Thinner"
58062 });
58063
58064
58065 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
58066     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
58067     this.proxy.el.addClass('x-grid3-col-dd');
58068 };
58069
58070 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
58071     handleMouseDown : function(e){
58072
58073     },
58074
58075     callHandleMouseDown : function(e){
58076         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
58077     }
58078 });
58079 /*
58080  * Based on:
58081  * Ext JS Library 1.1.1
58082  * Copyright(c) 2006-2007, Ext JS, LLC.
58083  *
58084  * Originally Released Under LGPL - original licence link has changed is not relivant.
58085  *
58086  * Fork - LGPL
58087  * <script type="text/javascript">
58088  */
58089  /**
58090  * @extends Roo.dd.DDProxy
58091  * @class Roo.grid.SplitDragZone
58092  * Support for Column Header resizing
58093  * @constructor
58094  * @param {Object} config
58095  */
58096 // private
58097 // This is a support class used internally by the Grid components
58098 Roo.grid.SplitDragZone = function(grid, hd, hd2){
58099     this.grid = grid;
58100     this.view = grid.getView();
58101     this.proxy = this.view.resizeProxy;
58102     Roo.grid.SplitDragZone.superclass.constructor.call(
58103         this,
58104         hd, // ID
58105         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
58106         {  // CONFIG
58107             dragElId : Roo.id(this.proxy.dom),
58108             resizeFrame:false
58109         }
58110     );
58111     
58112     this.setHandleElId(Roo.id(hd));
58113     if (hd2 !== false) {
58114         this.setOuterHandleElId(Roo.id(hd2));
58115     }
58116     
58117     this.scroll = false;
58118 };
58119 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
58120     fly: Roo.Element.fly,
58121
58122     b4StartDrag : function(x, y){
58123         this.view.headersDisabled = true;
58124         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
58125                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
58126         );
58127         this.proxy.setHeight(h);
58128         
58129         // for old system colWidth really stored the actual width?
58130         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
58131         // which in reality did not work.. - it worked only for fixed sizes
58132         // for resizable we need to use actual sizes.
58133         var w = this.cm.getColumnWidth(this.cellIndex);
58134         if (!this.view.mainWrap) {
58135             // bootstrap.
58136             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
58137         }
58138         
58139         
58140         
58141         // this was w-this.grid.minColumnWidth;
58142         // doesnt really make sense? - w = thie curren width or the rendered one?
58143         var minw = Math.max(w-this.grid.minColumnWidth, 0);
58144         this.resetConstraints();
58145         this.setXConstraint(minw, 1000);
58146         this.setYConstraint(0, 0);
58147         this.minX = x - minw;
58148         this.maxX = x + 1000;
58149         this.startPos = x;
58150         if (!this.view.mainWrap) { // this is Bootstrap code..
58151             this.getDragEl().style.display='block';
58152         }
58153         
58154         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
58155     },
58156
58157
58158     handleMouseDown : function(e){
58159         ev = Roo.EventObject.setEvent(e);
58160         var t = this.fly(ev.getTarget());
58161         if(t.hasClass("x-grid-split")){
58162             this.cellIndex = this.view.getCellIndex(t.dom);
58163             this.split = t.dom;
58164             this.cm = this.grid.colModel;
58165             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
58166                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
58167             }
58168         }
58169     },
58170
58171     endDrag : function(e){
58172         this.view.headersDisabled = false;
58173         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
58174         var diff = endX - this.startPos;
58175         // 
58176         var w = this.cm.getColumnWidth(this.cellIndex);
58177         if (!this.view.mainWrap) {
58178             w = 0;
58179         }
58180         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
58181     },
58182
58183     autoOffset : function(){
58184         this.setDelta(0,0);
58185     }
58186 });/*
58187  * Based on:
58188  * Ext JS Library 1.1.1
58189  * Copyright(c) 2006-2007, Ext JS, LLC.
58190  *
58191  * Originally Released Under LGPL - original licence link has changed is not relivant.
58192  *
58193  * Fork - LGPL
58194  * <script type="text/javascript">
58195  */
58196  
58197 // private
58198 // This is a support class used internally by the Grid components
58199 Roo.grid.GridDragZone = function(grid, config){
58200     this.view = grid.getView();
58201     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
58202     if(this.view.lockedBody){
58203         this.setHandleElId(Roo.id(this.view.mainBody.dom));
58204         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
58205     }
58206     this.scroll = false;
58207     this.grid = grid;
58208     this.ddel = document.createElement('div');
58209     this.ddel.className = 'x-grid-dd-wrap';
58210 };
58211
58212 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
58213     ddGroup : "GridDD",
58214
58215     getDragData : function(e){
58216         var t = Roo.lib.Event.getTarget(e);
58217         var rowIndex = this.view.findRowIndex(t);
58218         var sm = this.grid.selModel;
58219             
58220         //Roo.log(rowIndex);
58221         
58222         if (sm.getSelectedCell) {
58223             // cell selection..
58224             if (!sm.getSelectedCell()) {
58225                 return false;
58226             }
58227             if (rowIndex != sm.getSelectedCell()[0]) {
58228                 return false;
58229             }
58230         
58231         }
58232         if (sm.getSelections && sm.getSelections().length < 1) {
58233             return false;
58234         }
58235         
58236         
58237         // before it used to all dragging of unseleted... - now we dont do that.
58238         if(rowIndex !== false){
58239             
58240             // if editorgrid.. 
58241             
58242             
58243             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
58244                
58245             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
58246               //  
58247             //}
58248             if (e.hasModifier()){
58249                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
58250             }
58251             
58252             Roo.log("getDragData");
58253             
58254             return {
58255                 grid: this.grid,
58256                 ddel: this.ddel,
58257                 rowIndex: rowIndex,
58258                 selections: sm.getSelections ? sm.getSelections() : (
58259                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
58260             };
58261         }
58262         return false;
58263     },
58264     
58265     
58266     onInitDrag : function(e){
58267         var data = this.dragData;
58268         this.ddel.innerHTML = this.grid.getDragDropText();
58269         this.proxy.update(this.ddel);
58270         // fire start drag?
58271     },
58272
58273     afterRepair : function(){
58274         this.dragging = false;
58275     },
58276
58277     getRepairXY : function(e, data){
58278         return false;
58279     },
58280
58281     onEndDrag : function(data, e){
58282         // fire end drag?
58283     },
58284
58285     onValidDrop : function(dd, e, id){
58286         // fire drag drop?
58287         this.hideProxy();
58288     },
58289
58290     beforeInvalidDrop : function(e, id){
58291
58292     }
58293 });/*
58294  * Based on:
58295  * Ext JS Library 1.1.1
58296  * Copyright(c) 2006-2007, Ext JS, LLC.
58297  *
58298  * Originally Released Under LGPL - original licence link has changed is not relivant.
58299  *
58300  * Fork - LGPL
58301  * <script type="text/javascript">
58302  */
58303  
58304
58305 /**
58306  * @class Roo.grid.ColumnModel
58307  * @extends Roo.util.Observable
58308  * This is the default implementation of a ColumnModel used by the Grid. It defines
58309  * the columns in the grid.
58310  * <br>Usage:<br>
58311  <pre><code>
58312  var colModel = new Roo.grid.ColumnModel([
58313         {header: "Ticker", width: 60, sortable: true, locked: true},
58314         {header: "Company Name", width: 150, sortable: true},
58315         {header: "Market Cap.", width: 100, sortable: true},
58316         {header: "$ Sales", width: 100, sortable: true, renderer: money},
58317         {header: "Employees", width: 100, sortable: true, resizable: false}
58318  ]);
58319  </code></pre>
58320  * <p>
58321  
58322  * The config options listed for this class are options which may appear in each
58323  * individual column definition.
58324  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
58325  * @constructor
58326  * @param {Object} config An Array of column config objects. See this class's
58327  * config objects for details.
58328 */
58329 Roo.grid.ColumnModel = function(config){
58330         /**
58331      * The config passed into the constructor
58332      */
58333     this.config = []; //config;
58334     this.lookup = {};
58335
58336     // if no id, create one
58337     // if the column does not have a dataIndex mapping,
58338     // map it to the order it is in the config
58339     for(var i = 0, len = config.length; i < len; i++){
58340         this.addColumn(config[i]);
58341         
58342     }
58343
58344     /**
58345      * The width of columns which have no width specified (defaults to 100)
58346      * @type Number
58347      */
58348     this.defaultWidth = 100;
58349
58350     /**
58351      * Default sortable of columns which have no sortable specified (defaults to false)
58352      * @type Boolean
58353      */
58354     this.defaultSortable = false;
58355
58356     this.addEvents({
58357         /**
58358              * @event widthchange
58359              * Fires when the width of a column changes.
58360              * @param {ColumnModel} this
58361              * @param {Number} columnIndex The column index
58362              * @param {Number} newWidth The new width
58363              */
58364             "widthchange": true,
58365         /**
58366              * @event headerchange
58367              * Fires when the text of a header changes.
58368              * @param {ColumnModel} this
58369              * @param {Number} columnIndex The column index
58370              * @param {Number} newText The new header text
58371              */
58372             "headerchange": true,
58373         /**
58374              * @event hiddenchange
58375              * Fires when a column is hidden or "unhidden".
58376              * @param {ColumnModel} this
58377              * @param {Number} columnIndex The column index
58378              * @param {Boolean} hidden true if hidden, false otherwise
58379              */
58380             "hiddenchange": true,
58381             /**
58382          * @event columnmoved
58383          * Fires when a column is moved.
58384          * @param {ColumnModel} this
58385          * @param {Number} oldIndex
58386          * @param {Number} newIndex
58387          */
58388         "columnmoved" : true,
58389         /**
58390          * @event columlockchange
58391          * Fires when a column's locked state is changed
58392          * @param {ColumnModel} this
58393          * @param {Number} colIndex
58394          * @param {Boolean} locked true if locked
58395          */
58396         "columnlockchange" : true
58397     });
58398     Roo.grid.ColumnModel.superclass.constructor.call(this);
58399 };
58400 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
58401     /**
58402      * @cfg {String} header The header text to display in the Grid view.
58403      */
58404         /**
58405      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
58406      */
58407         /**
58408      * @cfg {String} smHeader Header at Bootsrap Small width
58409      */
58410         /**
58411      * @cfg {String} mdHeader Header at Bootsrap Medium width
58412      */
58413         /**
58414      * @cfg {String} lgHeader Header at Bootsrap Large width
58415      */
58416         /**
58417      * @cfg {String} xlHeader Header at Bootsrap extra Large width
58418      */
58419     /**
58420      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
58421      * {@link Roo.data.Record} definition from which to draw the column's value. If not
58422      * specified, the column's index is used as an index into the Record's data Array.
58423      */
58424     /**
58425      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
58426      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
58427      */
58428     /**
58429      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
58430      * Defaults to the value of the {@link #defaultSortable} property.
58431      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
58432      */
58433     /**
58434      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
58435      */
58436     /**
58437      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
58438      */
58439     /**
58440      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
58441      */
58442     /**
58443      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
58444      */
58445     /**
58446      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
58447      * given the cell's data value. See {@link #setRenderer}. If not specified, the
58448      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
58449      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
58450      */
58451        /**
58452      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
58453      */
58454     /**
58455      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
58456      */
58457     /**
58458      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
58459      */
58460     /**
58461      * @cfg {String} cursor (Optional)
58462      */
58463     /**
58464      * @cfg {String} tooltip (Optional)
58465      */
58466     /**
58467      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
58468      */
58469     /**
58470      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
58471      */
58472     /**
58473      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
58474      */
58475     /**
58476      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
58477      */
58478         /**
58479      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
58480      */
58481     /**
58482      * Returns the id of the column at the specified index.
58483      * @param {Number} index The column index
58484      * @return {String} the id
58485      */
58486     getColumnId : function(index){
58487         return this.config[index].id;
58488     },
58489
58490     /**
58491      * Returns the column for a specified id.
58492      * @param {String} id The column id
58493      * @return {Object} the column
58494      */
58495     getColumnById : function(id){
58496         return this.lookup[id];
58497     },
58498
58499     
58500     /**
58501      * Returns the column Object for a specified dataIndex.
58502      * @param {String} dataIndex The column dataIndex
58503      * @return {Object|Boolean} the column or false if not found
58504      */
58505     getColumnByDataIndex: function(dataIndex){
58506         var index = this.findColumnIndex(dataIndex);
58507         return index > -1 ? this.config[index] : false;
58508     },
58509     
58510     /**
58511      * Returns the index for a specified column id.
58512      * @param {String} id The column id
58513      * @return {Number} the index, or -1 if not found
58514      */
58515     getIndexById : function(id){
58516         for(var i = 0, len = this.config.length; i < len; i++){
58517             if(this.config[i].id == id){
58518                 return i;
58519             }
58520         }
58521         return -1;
58522     },
58523     
58524     /**
58525      * Returns the index for a specified column dataIndex.
58526      * @param {String} dataIndex The column dataIndex
58527      * @return {Number} the index, or -1 if not found
58528      */
58529     
58530     findColumnIndex : function(dataIndex){
58531         for(var i = 0, len = this.config.length; i < len; i++){
58532             if(this.config[i].dataIndex == dataIndex){
58533                 return i;
58534             }
58535         }
58536         return -1;
58537     },
58538     
58539     
58540     moveColumn : function(oldIndex, newIndex){
58541         var c = this.config[oldIndex];
58542         this.config.splice(oldIndex, 1);
58543         this.config.splice(newIndex, 0, c);
58544         this.dataMap = null;
58545         this.fireEvent("columnmoved", this, oldIndex, newIndex);
58546     },
58547
58548     isLocked : function(colIndex){
58549         return this.config[colIndex].locked === true;
58550     },
58551
58552     setLocked : function(colIndex, value, suppressEvent){
58553         if(this.isLocked(colIndex) == value){
58554             return;
58555         }
58556         this.config[colIndex].locked = value;
58557         if(!suppressEvent){
58558             this.fireEvent("columnlockchange", this, colIndex, value);
58559         }
58560     },
58561
58562     getTotalLockedWidth : function(){
58563         var totalWidth = 0;
58564         for(var i = 0; i < this.config.length; i++){
58565             if(this.isLocked(i) && !this.isHidden(i)){
58566                 this.totalWidth += this.getColumnWidth(i);
58567             }
58568         }
58569         return totalWidth;
58570     },
58571
58572     getLockedCount : function(){
58573         for(var i = 0, len = this.config.length; i < len; i++){
58574             if(!this.isLocked(i)){
58575                 return i;
58576             }
58577         }
58578         
58579         return this.config.length;
58580     },
58581
58582     /**
58583      * Returns the number of columns.
58584      * @return {Number}
58585      */
58586     getColumnCount : function(visibleOnly){
58587         if(visibleOnly === true){
58588             var c = 0;
58589             for(var i = 0, len = this.config.length; i < len; i++){
58590                 if(!this.isHidden(i)){
58591                     c++;
58592                 }
58593             }
58594             return c;
58595         }
58596         return this.config.length;
58597     },
58598
58599     /**
58600      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
58601      * @param {Function} fn
58602      * @param {Object} scope (optional)
58603      * @return {Array} result
58604      */
58605     getColumnsBy : function(fn, scope){
58606         var r = [];
58607         for(var i = 0, len = this.config.length; i < len; i++){
58608             var c = this.config[i];
58609             if(fn.call(scope||this, c, i) === true){
58610                 r[r.length] = c;
58611             }
58612         }
58613         return r;
58614     },
58615
58616     /**
58617      * Returns true if the specified column is sortable.
58618      * @param {Number} col The column index
58619      * @return {Boolean}
58620      */
58621     isSortable : function(col){
58622         if(typeof this.config[col].sortable == "undefined"){
58623             return this.defaultSortable;
58624         }
58625         return this.config[col].sortable;
58626     },
58627
58628     /**
58629      * Returns the rendering (formatting) function defined for the column.
58630      * @param {Number} col The column index.
58631      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
58632      */
58633     getRenderer : function(col){
58634         if(!this.config[col].renderer){
58635             return Roo.grid.ColumnModel.defaultRenderer;
58636         }
58637         return this.config[col].renderer;
58638     },
58639
58640     /**
58641      * Sets the rendering (formatting) function for a column.
58642      * @param {Number} col The column index
58643      * @param {Function} fn The function to use to process the cell's raw data
58644      * to return HTML markup for the grid view. The render function is called with
58645      * the following parameters:<ul>
58646      * <li>Data value.</li>
58647      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
58648      * <li>css A CSS style string to apply to the table cell.</li>
58649      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
58650      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
58651      * <li>Row index</li>
58652      * <li>Column index</li>
58653      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
58654      */
58655     setRenderer : function(col, fn){
58656         this.config[col].renderer = fn;
58657     },
58658
58659     /**
58660      * Returns the width for the specified column.
58661      * @param {Number} col The column index
58662      * @param (optional) {String} gridSize bootstrap width size.
58663      * @return {Number}
58664      */
58665     getColumnWidth : function(col, gridSize)
58666         {
58667                 var cfg = this.config[col];
58668                 
58669                 if (typeof(gridSize) == 'undefined') {
58670                         return cfg.width * 1 || this.defaultWidth;
58671                 }
58672                 if (gridSize === false) { // if we set it..
58673                         return cfg.width || false;
58674                 }
58675                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
58676                 
58677                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
58678                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
58679                                 continue;
58680                         }
58681                         return cfg[ sizes[i] ];
58682                 }
58683                 return 1;
58684                 
58685     },
58686
58687     /**
58688      * Sets the width for a column.
58689      * @param {Number} col The column index
58690      * @param {Number} width The new width
58691      */
58692     setColumnWidth : function(col, width, suppressEvent){
58693         this.config[col].width = width;
58694         this.totalWidth = null;
58695         if(!suppressEvent){
58696              this.fireEvent("widthchange", this, col, width);
58697         }
58698     },
58699
58700     /**
58701      * Returns the total width of all columns.
58702      * @param {Boolean} includeHidden True to include hidden column widths
58703      * @return {Number}
58704      */
58705     getTotalWidth : function(includeHidden){
58706         if(!this.totalWidth){
58707             this.totalWidth = 0;
58708             for(var i = 0, len = this.config.length; i < len; i++){
58709                 if(includeHidden || !this.isHidden(i)){
58710                     this.totalWidth += this.getColumnWidth(i);
58711                 }
58712             }
58713         }
58714         return this.totalWidth;
58715     },
58716
58717     /**
58718      * Returns the header for the specified column.
58719      * @param {Number} col The column index
58720      * @return {String}
58721      */
58722     getColumnHeader : function(col){
58723         return this.config[col].header;
58724     },
58725
58726     /**
58727      * Sets the header for a column.
58728      * @param {Number} col The column index
58729      * @param {String} header The new header
58730      */
58731     setColumnHeader : function(col, header){
58732         this.config[col].header = header;
58733         this.fireEvent("headerchange", this, col, header);
58734     },
58735
58736     /**
58737      * Returns the tooltip for the specified column.
58738      * @param {Number} col The column index
58739      * @return {String}
58740      */
58741     getColumnTooltip : function(col){
58742             return this.config[col].tooltip;
58743     },
58744     /**
58745      * Sets the tooltip for a column.
58746      * @param {Number} col The column index
58747      * @param {String} tooltip The new tooltip
58748      */
58749     setColumnTooltip : function(col, tooltip){
58750             this.config[col].tooltip = tooltip;
58751     },
58752
58753     /**
58754      * Returns the dataIndex for the specified column.
58755      * @param {Number} col The column index
58756      * @return {Number}
58757      */
58758     getDataIndex : function(col){
58759         return this.config[col].dataIndex;
58760     },
58761
58762     /**
58763      * Sets the dataIndex for a column.
58764      * @param {Number} col The column index
58765      * @param {Number} dataIndex The new dataIndex
58766      */
58767     setDataIndex : function(col, dataIndex){
58768         this.config[col].dataIndex = dataIndex;
58769     },
58770
58771     
58772     
58773     /**
58774      * Returns true if the cell is editable.
58775      * @param {Number} colIndex The column index
58776      * @param {Number} rowIndex The row index - this is nto actually used..?
58777      * @return {Boolean}
58778      */
58779     isCellEditable : function(colIndex, rowIndex){
58780         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
58781     },
58782
58783     /**
58784      * Returns the editor defined for the cell/column.
58785      * return false or null to disable editing.
58786      * @param {Number} colIndex The column index
58787      * @param {Number} rowIndex The row index
58788      * @return {Object}
58789      */
58790     getCellEditor : function(colIndex, rowIndex){
58791         return this.config[colIndex].editor;
58792     },
58793
58794     /**
58795      * Sets if a column is editable.
58796      * @param {Number} col The column index
58797      * @param {Boolean} editable True if the column is editable
58798      */
58799     setEditable : function(col, editable){
58800         this.config[col].editable = editable;
58801     },
58802
58803
58804     /**
58805      * Returns true if the column is hidden.
58806      * @param {Number} colIndex The column index
58807      * @return {Boolean}
58808      */
58809     isHidden : function(colIndex){
58810         return this.config[colIndex].hidden;
58811     },
58812
58813
58814     /**
58815      * Returns true if the column width cannot be changed
58816      */
58817     isFixed : function(colIndex){
58818         return this.config[colIndex].fixed;
58819     },
58820
58821     /**
58822      * Returns true if the column can be resized
58823      * @return {Boolean}
58824      */
58825     isResizable : function(colIndex){
58826         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
58827     },
58828     /**
58829      * Sets if a column is hidden.
58830      * @param {Number} colIndex The column index
58831      * @param {Boolean} hidden True if the column is hidden
58832      */
58833     setHidden : function(colIndex, hidden){
58834         this.config[colIndex].hidden = hidden;
58835         this.totalWidth = null;
58836         this.fireEvent("hiddenchange", this, colIndex, hidden);
58837     },
58838
58839     /**
58840      * Sets the editor for a column.
58841      * @param {Number} col The column index
58842      * @param {Object} editor The editor object
58843      */
58844     setEditor : function(col, editor){
58845         this.config[col].editor = editor;
58846     },
58847     /**
58848      * Add a column (experimental...) - defaults to adding to the end..
58849      * @param {Object} config 
58850     */
58851     addColumn : function(c)
58852     {
58853     
58854         var i = this.config.length;
58855         this.config[i] = c;
58856         
58857         if(typeof c.dataIndex == "undefined"){
58858             c.dataIndex = i;
58859         }
58860         if(typeof c.renderer == "string"){
58861             c.renderer = Roo.util.Format[c.renderer];
58862         }
58863         if(typeof c.id == "undefined"){
58864             c.id = Roo.id();
58865         }
58866         if(c.editor && c.editor.xtype){
58867             c.editor  = Roo.factory(c.editor, Roo.grid);
58868         }
58869         if(c.editor && c.editor.isFormField){
58870             c.editor = new Roo.grid.GridEditor(c.editor);
58871         }
58872         this.lookup[c.id] = c;
58873     }
58874     
58875 });
58876
58877 Roo.grid.ColumnModel.defaultRenderer = function(value)
58878 {
58879     if(typeof value == "object") {
58880         return value;
58881     }
58882         if(typeof value == "string" && value.length < 1){
58883             return "&#160;";
58884         }
58885     
58886         return String.format("{0}", value);
58887 };
58888
58889 // Alias for backwards compatibility
58890 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
58891 /*
58892  * Based on:
58893  * Ext JS Library 1.1.1
58894  * Copyright(c) 2006-2007, Ext JS, LLC.
58895  *
58896  * Originally Released Under LGPL - original licence link has changed is not relivant.
58897  *
58898  * Fork - LGPL
58899  * <script type="text/javascript">
58900  */
58901
58902 /**
58903  * @class Roo.grid.AbstractSelectionModel
58904  * @extends Roo.util.Observable
58905  * Abstract base class for grid SelectionModels.  It provides the interface that should be
58906  * implemented by descendant classes.  This class should not be directly instantiated.
58907  * @constructor
58908  */
58909 Roo.grid.AbstractSelectionModel = function(){
58910     this.locked = false;
58911     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
58912 };
58913
58914 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
58915     /** @ignore Called by the grid automatically. Do not call directly. */
58916     init : function(grid){
58917         this.grid = grid;
58918         this.initEvents();
58919     },
58920
58921     /**
58922      * Locks the selections.
58923      */
58924     lock : function(){
58925         this.locked = true;
58926     },
58927
58928     /**
58929      * Unlocks the selections.
58930      */
58931     unlock : function(){
58932         this.locked = false;
58933     },
58934
58935     /**
58936      * Returns true if the selections are locked.
58937      * @return {Boolean}
58938      */
58939     isLocked : function(){
58940         return this.locked;
58941     }
58942 });/*
58943  * Based on:
58944  * Ext JS Library 1.1.1
58945  * Copyright(c) 2006-2007, Ext JS, LLC.
58946  *
58947  * Originally Released Under LGPL - original licence link has changed is not relivant.
58948  *
58949  * Fork - LGPL
58950  * <script type="text/javascript">
58951  */
58952 /**
58953  * @extends Roo.grid.AbstractSelectionModel
58954  * @class Roo.grid.RowSelectionModel
58955  * The default SelectionModel used by {@link Roo.grid.Grid}.
58956  * It supports multiple selections and keyboard selection/navigation. 
58957  * @constructor
58958  * @param {Object} config
58959  */
58960 Roo.grid.RowSelectionModel = function(config){
58961     Roo.apply(this, config);
58962     this.selections = new Roo.util.MixedCollection(false, function(o){
58963         return o.id;
58964     });
58965
58966     this.last = false;
58967     this.lastActive = false;
58968
58969     this.addEvents({
58970         /**
58971         * @event selectionchange
58972         * Fires when the selection changes
58973         * @param {SelectionModel} this
58974         */
58975        "selectionchange" : true,
58976        /**
58977         * @event afterselectionchange
58978         * Fires after the selection changes (eg. by key press or clicking)
58979         * @param {SelectionModel} this
58980         */
58981        "afterselectionchange" : true,
58982        /**
58983         * @event beforerowselect
58984         * Fires when a row is selected being selected, return false to cancel.
58985         * @param {SelectionModel} this
58986         * @param {Number} rowIndex The selected index
58987         * @param {Boolean} keepExisting False if other selections will be cleared
58988         */
58989        "beforerowselect" : true,
58990        /**
58991         * @event rowselect
58992         * Fires when a row is selected.
58993         * @param {SelectionModel} this
58994         * @param {Number} rowIndex The selected index
58995         * @param {Roo.data.Record} r The record
58996         */
58997        "rowselect" : true,
58998        /**
58999         * @event rowdeselect
59000         * Fires when a row is deselected.
59001         * @param {SelectionModel} this
59002         * @param {Number} rowIndex The selected index
59003         */
59004         "rowdeselect" : true
59005     });
59006     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
59007     this.locked = false;
59008 };
59009
59010 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
59011     /**
59012      * @cfg {Boolean} singleSelect
59013      * True to allow selection of only one row at a time (defaults to false)
59014      */
59015     singleSelect : false,
59016
59017     // private
59018     initEvents : function(){
59019
59020         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
59021             this.grid.on("mousedown", this.handleMouseDown, this);
59022         }else{ // allow click to work like normal
59023             this.grid.on("rowclick", this.handleDragableRowClick, this);
59024         }
59025         // bootstrap does not have a view..
59026         var view = this.grid.view ? this.grid.view : this.grid;
59027         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
59028             "up" : function(e){
59029                 if(!e.shiftKey){
59030                     this.selectPrevious(e.shiftKey);
59031                 }else if(this.last !== false && this.lastActive !== false){
59032                     var last = this.last;
59033                     this.selectRange(this.last,  this.lastActive-1);
59034                     view.focusRow(this.lastActive);
59035                     if(last !== false){
59036                         this.last = last;
59037                     }
59038                 }else{
59039                     this.selectFirstRow();
59040                 }
59041                 this.fireEvent("afterselectionchange", this);
59042             },
59043             "down" : function(e){
59044                 if(!e.shiftKey){
59045                     this.selectNext(e.shiftKey);
59046                 }else if(this.last !== false && this.lastActive !== false){
59047                     var last = this.last;
59048                     this.selectRange(this.last,  this.lastActive+1);
59049                     view.focusRow(this.lastActive);
59050                     if(last !== false){
59051                         this.last = last;
59052                     }
59053                 }else{
59054                     this.selectFirstRow();
59055                 }
59056                 this.fireEvent("afterselectionchange", this);
59057             },
59058             scope: this
59059         });
59060
59061          
59062         view.on("refresh", this.onRefresh, this);
59063         view.on("rowupdated", this.onRowUpdated, this);
59064         view.on("rowremoved", this.onRemove, this);
59065     },
59066
59067     // private
59068     onRefresh : function(){
59069         var ds = this.grid.ds, i, v = this.grid.view;
59070         var s = this.selections;
59071         s.each(function(r){
59072             if((i = ds.indexOfId(r.id)) != -1){
59073                 v.onRowSelect(i);
59074                 s.add(ds.getAt(i)); // updating the selection relate data
59075             }else{
59076                 s.remove(r);
59077             }
59078         });
59079     },
59080
59081     // private
59082     onRemove : function(v, index, r){
59083         this.selections.remove(r);
59084     },
59085
59086     // private
59087     onRowUpdated : function(v, index, r){
59088         if(this.isSelected(r)){
59089             v.onRowSelect(index);
59090         }
59091     },
59092
59093     /**
59094      * Select records.
59095      * @param {Array} records The records to select
59096      * @param {Boolean} keepExisting (optional) True to keep existing selections
59097      */
59098     selectRecords : function(records, keepExisting){
59099         if(!keepExisting){
59100             this.clearSelections();
59101         }
59102         var ds = this.grid.ds;
59103         for(var i = 0, len = records.length; i < len; i++){
59104             this.selectRow(ds.indexOf(records[i]), true);
59105         }
59106     },
59107
59108     /**
59109      * Gets the number of selected rows.
59110      * @return {Number}
59111      */
59112     getCount : function(){
59113         return this.selections.length;
59114     },
59115
59116     /**
59117      * Selects the first row in the grid.
59118      */
59119     selectFirstRow : function(){
59120         this.selectRow(0);
59121     },
59122
59123     /**
59124      * Select the last row.
59125      * @param {Boolean} keepExisting (optional) True to keep existing selections
59126      */
59127     selectLastRow : function(keepExisting){
59128         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
59129     },
59130
59131     /**
59132      * Selects the row immediately following the last selected row.
59133      * @param {Boolean} keepExisting (optional) True to keep existing selections
59134      */
59135     selectNext : function(keepExisting){
59136         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
59137             this.selectRow(this.last+1, keepExisting);
59138             var view = this.grid.view ? this.grid.view : this.grid;
59139             view.focusRow(this.last);
59140         }
59141     },
59142
59143     /**
59144      * Selects the row that precedes the last selected row.
59145      * @param {Boolean} keepExisting (optional) True to keep existing selections
59146      */
59147     selectPrevious : function(keepExisting){
59148         if(this.last){
59149             this.selectRow(this.last-1, keepExisting);
59150             var view = this.grid.view ? this.grid.view : this.grid;
59151             view.focusRow(this.last);
59152         }
59153     },
59154
59155     /**
59156      * Returns the selected records
59157      * @return {Array} Array of selected records
59158      */
59159     getSelections : function(){
59160         return [].concat(this.selections.items);
59161     },
59162
59163     /**
59164      * Returns the first selected record.
59165      * @return {Record}
59166      */
59167     getSelected : function(){
59168         return this.selections.itemAt(0);
59169     },
59170
59171
59172     /**
59173      * Clears all selections.
59174      */
59175     clearSelections : function(fast){
59176         if(this.locked) {
59177             return;
59178         }
59179         if(fast !== true){
59180             var ds = this.grid.ds;
59181             var s = this.selections;
59182             s.each(function(r){
59183                 this.deselectRow(ds.indexOfId(r.id));
59184             }, this);
59185             s.clear();
59186         }else{
59187             this.selections.clear();
59188         }
59189         this.last = false;
59190     },
59191
59192
59193     /**
59194      * Selects all rows.
59195      */
59196     selectAll : function(){
59197         if(this.locked) {
59198             return;
59199         }
59200         this.selections.clear();
59201         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
59202             this.selectRow(i, true);
59203         }
59204     },
59205
59206     /**
59207      * Returns True if there is a selection.
59208      * @return {Boolean}
59209      */
59210     hasSelection : function(){
59211         return this.selections.length > 0;
59212     },
59213
59214     /**
59215      * Returns True if the specified row is selected.
59216      * @param {Number/Record} record The record or index of the record to check
59217      * @return {Boolean}
59218      */
59219     isSelected : function(index){
59220         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
59221         return (r && this.selections.key(r.id) ? true : false);
59222     },
59223
59224     /**
59225      * Returns True if the specified record id is selected.
59226      * @param {String} id The id of record to check
59227      * @return {Boolean}
59228      */
59229     isIdSelected : function(id){
59230         return (this.selections.key(id) ? true : false);
59231     },
59232
59233     // private
59234     handleMouseDown : function(e, t)
59235     {
59236         var view = this.grid.view ? this.grid.view : this.grid;
59237         var rowIndex;
59238         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
59239             return;
59240         };
59241         if(e.shiftKey && this.last !== false){
59242             var last = this.last;
59243             this.selectRange(last, rowIndex, e.ctrlKey);
59244             this.last = last; // reset the last
59245             view.focusRow(rowIndex);
59246         }else{
59247             var isSelected = this.isSelected(rowIndex);
59248             if(e.button !== 0 && isSelected){
59249                 view.focusRow(rowIndex);
59250             }else if(e.ctrlKey && isSelected){
59251                 this.deselectRow(rowIndex);
59252             }else if(!isSelected){
59253                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
59254                 view.focusRow(rowIndex);
59255             }
59256         }
59257         this.fireEvent("afterselectionchange", this);
59258     },
59259     // private
59260     handleDragableRowClick :  function(grid, rowIndex, e) 
59261     {
59262         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
59263             this.selectRow(rowIndex, false);
59264             var view = this.grid.view ? this.grid.view : this.grid;
59265             view.focusRow(rowIndex);
59266              this.fireEvent("afterselectionchange", this);
59267         }
59268     },
59269     
59270     /**
59271      * Selects multiple rows.
59272      * @param {Array} rows Array of the indexes of the row to select
59273      * @param {Boolean} keepExisting (optional) True to keep existing selections
59274      */
59275     selectRows : function(rows, keepExisting){
59276         if(!keepExisting){
59277             this.clearSelections();
59278         }
59279         for(var i = 0, len = rows.length; i < len; i++){
59280             this.selectRow(rows[i], true);
59281         }
59282     },
59283
59284     /**
59285      * Selects a range of rows. All rows in between startRow and endRow are also selected.
59286      * @param {Number} startRow The index of the first row in the range
59287      * @param {Number} endRow The index of the last row in the range
59288      * @param {Boolean} keepExisting (optional) True to retain existing selections
59289      */
59290     selectRange : function(startRow, endRow, keepExisting){
59291         if(this.locked) {
59292             return;
59293         }
59294         if(!keepExisting){
59295             this.clearSelections();
59296         }
59297         if(startRow <= endRow){
59298             for(var i = startRow; i <= endRow; i++){
59299                 this.selectRow(i, true);
59300             }
59301         }else{
59302             for(var i = startRow; i >= endRow; i--){
59303                 this.selectRow(i, true);
59304             }
59305         }
59306     },
59307
59308     /**
59309      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
59310      * @param {Number} startRow The index of the first row in the range
59311      * @param {Number} endRow The index of the last row in the range
59312      */
59313     deselectRange : function(startRow, endRow, preventViewNotify){
59314         if(this.locked) {
59315             return;
59316         }
59317         for(var i = startRow; i <= endRow; i++){
59318             this.deselectRow(i, preventViewNotify);
59319         }
59320     },
59321
59322     /**
59323      * Selects a row.
59324      * @param {Number} row The index of the row to select
59325      * @param {Boolean} keepExisting (optional) True to keep existing selections
59326      */
59327     selectRow : function(index, keepExisting, preventViewNotify){
59328         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
59329             return;
59330         }
59331         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
59332             if(!keepExisting || this.singleSelect){
59333                 this.clearSelections();
59334             }
59335             var r = this.grid.ds.getAt(index);
59336             this.selections.add(r);
59337             this.last = this.lastActive = index;
59338             if(!preventViewNotify){
59339                 var view = this.grid.view ? this.grid.view : this.grid;
59340                 view.onRowSelect(index);
59341             }
59342             this.fireEvent("rowselect", this, index, r);
59343             this.fireEvent("selectionchange", this);
59344         }
59345     },
59346
59347     /**
59348      * Deselects a row.
59349      * @param {Number} row The index of the row to deselect
59350      */
59351     deselectRow : function(index, preventViewNotify){
59352         if(this.locked) {
59353             return;
59354         }
59355         if(this.last == index){
59356             this.last = false;
59357         }
59358         if(this.lastActive == index){
59359             this.lastActive = false;
59360         }
59361         var r = this.grid.ds.getAt(index);
59362         this.selections.remove(r);
59363         if(!preventViewNotify){
59364             var view = this.grid.view ? this.grid.view : this.grid;
59365             view.onRowDeselect(index);
59366         }
59367         this.fireEvent("rowdeselect", this, index);
59368         this.fireEvent("selectionchange", this);
59369     },
59370
59371     // private
59372     restoreLast : function(){
59373         if(this._last){
59374             this.last = this._last;
59375         }
59376     },
59377
59378     // private
59379     acceptsNav : function(row, col, cm){
59380         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59381     },
59382
59383     // private
59384     onEditorKey : function(field, e){
59385         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
59386         if(k == e.TAB){
59387             e.stopEvent();
59388             ed.completeEdit();
59389             if(e.shiftKey){
59390                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59391             }else{
59392                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59393             }
59394         }else if(k == e.ENTER && !e.ctrlKey){
59395             e.stopEvent();
59396             ed.completeEdit();
59397             if(e.shiftKey){
59398                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
59399             }else{
59400                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
59401             }
59402         }else if(k == e.ESC){
59403             ed.cancelEdit();
59404         }
59405         if(newCell){
59406             g.startEditing(newCell[0], newCell[1]);
59407         }
59408     }
59409 });/*
59410  * Based on:
59411  * Ext JS Library 1.1.1
59412  * Copyright(c) 2006-2007, Ext JS, LLC.
59413  *
59414  * Originally Released Under LGPL - original licence link has changed is not relivant.
59415  *
59416  * Fork - LGPL
59417  * <script type="text/javascript">
59418  */
59419 /**
59420  * @class Roo.grid.CellSelectionModel
59421  * @extends Roo.grid.AbstractSelectionModel
59422  * This class provides the basic implementation for cell selection in a grid.
59423  * @constructor
59424  * @param {Object} config The object containing the configuration of this model.
59425  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
59426  */
59427 Roo.grid.CellSelectionModel = function(config){
59428     Roo.apply(this, config);
59429
59430     this.selection = null;
59431
59432     this.addEvents({
59433         /**
59434              * @event beforerowselect
59435              * Fires before a cell is selected.
59436              * @param {SelectionModel} this
59437              * @param {Number} rowIndex The selected row index
59438              * @param {Number} colIndex The selected cell index
59439              */
59440             "beforecellselect" : true,
59441         /**
59442              * @event cellselect
59443              * Fires when a cell is selected.
59444              * @param {SelectionModel} this
59445              * @param {Number} rowIndex The selected row index
59446              * @param {Number} colIndex The selected cell index
59447              */
59448             "cellselect" : true,
59449         /**
59450              * @event selectionchange
59451              * Fires when the active selection changes.
59452              * @param {SelectionModel} this
59453              * @param {Object} selection null for no selection or an object (o) with two properties
59454                 <ul>
59455                 <li>o.record: the record object for the row the selection is in</li>
59456                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
59457                 </ul>
59458              */
59459             "selectionchange" : true,
59460         /**
59461              * @event tabend
59462              * Fires when the tab (or enter) was pressed on the last editable cell
59463              * You can use this to trigger add new row.
59464              * @param {SelectionModel} this
59465              */
59466             "tabend" : true,
59467          /**
59468              * @event beforeeditnext
59469              * Fires before the next editable sell is made active
59470              * You can use this to skip to another cell or fire the tabend
59471              *    if you set cell to false
59472              * @param {Object} eventdata object : { cell : [ row, col ] } 
59473              */
59474             "beforeeditnext" : true
59475     });
59476     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
59477 };
59478
59479 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
59480     
59481     enter_is_tab: false,
59482
59483     /** @ignore */
59484     initEvents : function(){
59485         this.grid.on("mousedown", this.handleMouseDown, this);
59486         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
59487         var view = this.grid.view;
59488         view.on("refresh", this.onViewChange, this);
59489         view.on("rowupdated", this.onRowUpdated, this);
59490         view.on("beforerowremoved", this.clearSelections, this);
59491         view.on("beforerowsinserted", this.clearSelections, this);
59492         if(this.grid.isEditor){
59493             this.grid.on("beforeedit", this.beforeEdit,  this);
59494         }
59495     },
59496
59497         //private
59498     beforeEdit : function(e){
59499         this.select(e.row, e.column, false, true, e.record);
59500     },
59501
59502         //private
59503     onRowUpdated : function(v, index, r){
59504         if(this.selection && this.selection.record == r){
59505             v.onCellSelect(index, this.selection.cell[1]);
59506         }
59507     },
59508
59509         //private
59510     onViewChange : function(){
59511         this.clearSelections(true);
59512     },
59513
59514         /**
59515          * Returns the currently selected cell,.
59516          * @return {Array} The selected cell (row, column) or null if none selected.
59517          */
59518     getSelectedCell : function(){
59519         return this.selection ? this.selection.cell : null;
59520     },
59521
59522     /**
59523      * Clears all selections.
59524      * @param {Boolean} true to prevent the gridview from being notified about the change.
59525      */
59526     clearSelections : function(preventNotify){
59527         var s = this.selection;
59528         if(s){
59529             if(preventNotify !== true){
59530                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
59531             }
59532             this.selection = null;
59533             this.fireEvent("selectionchange", this, null);
59534         }
59535     },
59536
59537     /**
59538      * Returns true if there is a selection.
59539      * @return {Boolean}
59540      */
59541     hasSelection : function(){
59542         return this.selection ? true : false;
59543     },
59544
59545     /** @ignore */
59546     handleMouseDown : function(e, t){
59547         var v = this.grid.getView();
59548         if(this.isLocked()){
59549             return;
59550         };
59551         var row = v.findRowIndex(t);
59552         var cell = v.findCellIndex(t);
59553         if(row !== false && cell !== false){
59554             this.select(row, cell);
59555         }
59556     },
59557
59558     /**
59559      * Selects a cell.
59560      * @param {Number} rowIndex
59561      * @param {Number} collIndex
59562      */
59563     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
59564         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
59565             this.clearSelections();
59566             r = r || this.grid.dataSource.getAt(rowIndex);
59567             this.selection = {
59568                 record : r,
59569                 cell : [rowIndex, colIndex]
59570             };
59571             if(!preventViewNotify){
59572                 var v = this.grid.getView();
59573                 v.onCellSelect(rowIndex, colIndex);
59574                 if(preventFocus !== true){
59575                     v.focusCell(rowIndex, colIndex);
59576                 }
59577             }
59578             this.fireEvent("cellselect", this, rowIndex, colIndex);
59579             this.fireEvent("selectionchange", this, this.selection);
59580         }
59581     },
59582
59583         //private
59584     isSelectable : function(rowIndex, colIndex, cm){
59585         return !cm.isHidden(colIndex);
59586     },
59587
59588     /** @ignore */
59589     handleKeyDown : function(e){
59590         //Roo.log('Cell Sel Model handleKeyDown');
59591         if(!e.isNavKeyPress()){
59592             return;
59593         }
59594         var g = this.grid, s = this.selection;
59595         if(!s){
59596             e.stopEvent();
59597             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
59598             if(cell){
59599                 this.select(cell[0], cell[1]);
59600             }
59601             return;
59602         }
59603         var sm = this;
59604         var walk = function(row, col, step){
59605             return g.walkCells(row, col, step, sm.isSelectable,  sm);
59606         };
59607         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
59608         var newCell;
59609
59610       
59611
59612         switch(k){
59613             case e.TAB:
59614                 // handled by onEditorKey
59615                 if (g.isEditor && g.editing) {
59616                     return;
59617                 }
59618                 if(e.shiftKey) {
59619                     newCell = walk(r, c-1, -1);
59620                 } else {
59621                     newCell = walk(r, c+1, 1);
59622                 }
59623                 break;
59624             
59625             case e.DOWN:
59626                newCell = walk(r+1, c, 1);
59627                 break;
59628             
59629             case e.UP:
59630                 newCell = walk(r-1, c, -1);
59631                 break;
59632             
59633             case e.RIGHT:
59634                 newCell = walk(r, c+1, 1);
59635                 break;
59636             
59637             case e.LEFT:
59638                 newCell = walk(r, c-1, -1);
59639                 break;
59640             
59641             case e.ENTER:
59642                 
59643                 if(g.isEditor && !g.editing){
59644                    g.startEditing(r, c);
59645                    e.stopEvent();
59646                    return;
59647                 }
59648                 
59649                 
59650              break;
59651         };
59652         if(newCell){
59653             this.select(newCell[0], newCell[1]);
59654             e.stopEvent();
59655             
59656         }
59657     },
59658
59659     acceptsNav : function(row, col, cm){
59660         return !cm.isHidden(col) && cm.isCellEditable(col, row);
59661     },
59662     /**
59663      * Selects a cell.
59664      * @param {Number} field (not used) - as it's normally used as a listener
59665      * @param {Number} e - event - fake it by using
59666      *
59667      * var e = Roo.EventObjectImpl.prototype;
59668      * e.keyCode = e.TAB
59669      *
59670      * 
59671      */
59672     onEditorKey : function(field, e){
59673         
59674         var k = e.getKey(),
59675             newCell,
59676             g = this.grid,
59677             ed = g.activeEditor,
59678             forward = false;
59679         ///Roo.log('onEditorKey' + k);
59680         
59681         
59682         if (this.enter_is_tab && k == e.ENTER) {
59683             k = e.TAB;
59684         }
59685         
59686         if(k == e.TAB){
59687             if(e.shiftKey){
59688                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
59689             }else{
59690                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59691                 forward = true;
59692             }
59693             
59694             e.stopEvent();
59695             
59696         } else if(k == e.ENTER &&  !e.ctrlKey){
59697             ed.completeEdit();
59698             e.stopEvent();
59699             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
59700         
59701                 } else if(k == e.ESC){
59702             ed.cancelEdit();
59703         }
59704                 
59705         if (newCell) {
59706             var ecall = { cell : newCell, forward : forward };
59707             this.fireEvent('beforeeditnext', ecall );
59708             newCell = ecall.cell;
59709                         forward = ecall.forward;
59710         }
59711                 
59712         if(newCell){
59713             //Roo.log('next cell after edit');
59714             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
59715         } else if (forward) {
59716             // tabbed past last
59717             this.fireEvent.defer(100, this, ['tabend',this]);
59718         }
59719     }
59720 });/*
59721  * Based on:
59722  * Ext JS Library 1.1.1
59723  * Copyright(c) 2006-2007, Ext JS, LLC.
59724  *
59725  * Originally Released Under LGPL - original licence link has changed is not relivant.
59726  *
59727  * Fork - LGPL
59728  * <script type="text/javascript">
59729  */
59730  
59731 /**
59732  * @class Roo.grid.EditorGrid
59733  * @extends Roo.grid.Grid
59734  * Class for creating and editable grid.
59735  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
59736  * The container MUST have some type of size defined for the grid to fill. The container will be 
59737  * automatically set to position relative if it isn't already.
59738  * @param {Object} dataSource The data model to bind to
59739  * @param {Object} colModel The column model with info about this grid's columns
59740  */
59741 Roo.grid.EditorGrid = function(container, config){
59742     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
59743     this.getGridEl().addClass("xedit-grid");
59744
59745     if(!this.selModel){
59746         this.selModel = new Roo.grid.CellSelectionModel();
59747     }
59748
59749     this.activeEditor = null;
59750
59751         this.addEvents({
59752             /**
59753              * @event beforeedit
59754              * Fires before cell editing is triggered. The edit event object has the following properties <br />
59755              * <ul style="padding:5px;padding-left:16px;">
59756              * <li>grid - This grid</li>
59757              * <li>record - The record being edited</li>
59758              * <li>field - The field name being edited</li>
59759              * <li>value - The value for the field being edited.</li>
59760              * <li>row - The grid row index</li>
59761              * <li>column - The grid column index</li>
59762              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59763              * </ul>
59764              * @param {Object} e An edit event (see above for description)
59765              */
59766             "beforeedit" : true,
59767             /**
59768              * @event afteredit
59769              * Fires after a cell is edited. <br />
59770              * <ul style="padding:5px;padding-left:16px;">
59771              * <li>grid - This grid</li>
59772              * <li>record - The record being edited</li>
59773              * <li>field - The field name being edited</li>
59774              * <li>value - The value being set</li>
59775              * <li>originalValue - The original value for the field, before the edit.</li>
59776              * <li>row - The grid row index</li>
59777              * <li>column - The grid column index</li>
59778              * </ul>
59779              * @param {Object} e An edit event (see above for description)
59780              */
59781             "afteredit" : true,
59782             /**
59783              * @event validateedit
59784              * Fires after a cell is edited, but before the value is set in the record. 
59785          * You can use this to modify the value being set in the field, Return false
59786              * to cancel the change. The edit event object has the following properties <br />
59787              * <ul style="padding:5px;padding-left:16px;">
59788          * <li>editor - This editor</li>
59789              * <li>grid - This grid</li>
59790              * <li>record - The record being edited</li>
59791              * <li>field - The field name being edited</li>
59792              * <li>value - The value being set</li>
59793              * <li>originalValue - The original value for the field, before the edit.</li>
59794              * <li>row - The grid row index</li>
59795              * <li>column - The grid column index</li>
59796              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
59797              * </ul>
59798              * @param {Object} e An edit event (see above for description)
59799              */
59800             "validateedit" : true
59801         });
59802     this.on("bodyscroll", this.stopEditing,  this);
59803     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
59804 };
59805
59806 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
59807     /**
59808      * @cfg {Number} clicksToEdit
59809      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
59810      */
59811     clicksToEdit: 2,
59812
59813     // private
59814     isEditor : true,
59815     // private
59816     trackMouseOver: false, // causes very odd FF errors
59817
59818     onCellDblClick : function(g, row, col){
59819         this.startEditing(row, col);
59820     },
59821
59822     onEditComplete : function(ed, value, startValue){
59823         this.editing = false;
59824         this.activeEditor = null;
59825         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
59826         var r = ed.record;
59827         var field = this.colModel.getDataIndex(ed.col);
59828         var e = {
59829             grid: this,
59830             record: r,
59831             field: field,
59832             originalValue: startValue,
59833             value: value,
59834             row: ed.row,
59835             column: ed.col,
59836             cancel:false,
59837             editor: ed
59838         };
59839         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
59840         cell.show();
59841           
59842         if(String(value) !== String(startValue)){
59843             
59844             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
59845                 r.set(field, e.value);
59846                 // if we are dealing with a combo box..
59847                 // then we also set the 'name' colum to be the displayField
59848                 if (ed.field.displayField && ed.field.name) {
59849                     r.set(ed.field.name, ed.field.el.dom.value);
59850                 }
59851                 
59852                 delete e.cancel; //?? why!!!
59853                 this.fireEvent("afteredit", e);
59854             }
59855         } else {
59856             this.fireEvent("afteredit", e); // always fire it!
59857         }
59858         this.view.focusCell(ed.row, ed.col);
59859     },
59860
59861     /**
59862      * Starts editing the specified for the specified row/column
59863      * @param {Number} rowIndex
59864      * @param {Number} colIndex
59865      */
59866     startEditing : function(row, col){
59867         this.stopEditing();
59868         if(this.colModel.isCellEditable(col, row)){
59869             this.view.ensureVisible(row, col, true);
59870           
59871             var r = this.dataSource.getAt(row);
59872             var field = this.colModel.getDataIndex(col);
59873             var cell = Roo.get(this.view.getCell(row,col));
59874             var e = {
59875                 grid: this,
59876                 record: r,
59877                 field: field,
59878                 value: r.data[field],
59879                 row: row,
59880                 column: col,
59881                 cancel:false 
59882             };
59883             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
59884                 this.editing = true;
59885                 var ed = this.colModel.getCellEditor(col, row);
59886                 
59887                 if (!ed) {
59888                     return;
59889                 }
59890                 if(!ed.rendered){
59891                     ed.render(ed.parentEl || document.body);
59892                 }
59893                 ed.field.reset();
59894                
59895                 cell.hide();
59896                 
59897                 (function(){ // complex but required for focus issues in safari, ie and opera
59898                     ed.row = row;
59899                     ed.col = col;
59900                     ed.record = r;
59901                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
59902                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
59903                     this.activeEditor = ed;
59904                     var v = r.data[field];
59905                     ed.startEdit(this.view.getCell(row, col), v);
59906                     // combo's with 'displayField and name set
59907                     if (ed.field.displayField && ed.field.name) {
59908                         ed.field.el.dom.value = r.data[ed.field.name];
59909                     }
59910                     
59911                     
59912                 }).defer(50, this);
59913             }
59914         }
59915     },
59916         
59917     /**
59918      * Stops any active editing
59919      */
59920     stopEditing : function(){
59921         if(this.activeEditor){
59922             this.activeEditor.completeEdit();
59923         }
59924         this.activeEditor = null;
59925     },
59926         
59927          /**
59928      * Called to get grid's drag proxy text, by default returns this.ddText.
59929      * @return {String}
59930      */
59931     getDragDropText : function(){
59932         var count = this.selModel.getSelectedCell() ? 1 : 0;
59933         return String.format(this.ddText, count, count == 1 ? '' : 's');
59934     }
59935         
59936 });/*
59937  * Based on:
59938  * Ext JS Library 1.1.1
59939  * Copyright(c) 2006-2007, Ext JS, LLC.
59940  *
59941  * Originally Released Under LGPL - original licence link has changed is not relivant.
59942  *
59943  * Fork - LGPL
59944  * <script type="text/javascript">
59945  */
59946
59947 // private - not really -- you end up using it !
59948 // This is a support class used internally by the Grid components
59949
59950 /**
59951  * @class Roo.grid.GridEditor
59952  * @extends Roo.Editor
59953  * Class for creating and editable grid elements.
59954  * @param {Object} config any settings (must include field)
59955  */
59956 Roo.grid.GridEditor = function(field, config){
59957     if (!config && field.field) {
59958         config = field;
59959         field = Roo.factory(config.field, Roo.form);
59960     }
59961     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
59962     field.monitorTab = false;
59963 };
59964
59965 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
59966     
59967     /**
59968      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
59969      */
59970     
59971     alignment: "tl-tl",
59972     autoSize: "width",
59973     hideEl : false,
59974     cls: "x-small-editor x-grid-editor",
59975     shim:false,
59976     shadow:"frame"
59977 });/*
59978  * Based on:
59979  * Ext JS Library 1.1.1
59980  * Copyright(c) 2006-2007, Ext JS, LLC.
59981  *
59982  * Originally Released Under LGPL - original licence link has changed is not relivant.
59983  *
59984  * Fork - LGPL
59985  * <script type="text/javascript">
59986  */
59987   
59988
59989   
59990 Roo.grid.PropertyRecord = Roo.data.Record.create([
59991     {name:'name',type:'string'},  'value'
59992 ]);
59993
59994
59995 Roo.grid.PropertyStore = function(grid, source){
59996     this.grid = grid;
59997     this.store = new Roo.data.Store({
59998         recordType : Roo.grid.PropertyRecord
59999     });
60000     this.store.on('update', this.onUpdate,  this);
60001     if(source){
60002         this.setSource(source);
60003     }
60004     Roo.grid.PropertyStore.superclass.constructor.call(this);
60005 };
60006
60007
60008
60009 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
60010     setSource : function(o){
60011         this.source = o;
60012         this.store.removeAll();
60013         var data = [];
60014         for(var k in o){
60015             if(this.isEditableValue(o[k])){
60016                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
60017             }
60018         }
60019         this.store.loadRecords({records: data}, {}, true);
60020     },
60021
60022     onUpdate : function(ds, record, type){
60023         if(type == Roo.data.Record.EDIT){
60024             var v = record.data['value'];
60025             var oldValue = record.modified['value'];
60026             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
60027                 this.source[record.id] = v;
60028                 record.commit();
60029                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
60030             }else{
60031                 record.reject();
60032             }
60033         }
60034     },
60035
60036     getProperty : function(row){
60037        return this.store.getAt(row);
60038     },
60039
60040     isEditableValue: function(val){
60041         if(val && val instanceof Date){
60042             return true;
60043         }else if(typeof val == 'object' || typeof val == 'function'){
60044             return false;
60045         }
60046         return true;
60047     },
60048
60049     setValue : function(prop, value){
60050         this.source[prop] = value;
60051         this.store.getById(prop).set('value', value);
60052     },
60053
60054     getSource : function(){
60055         return this.source;
60056     }
60057 });
60058
60059 Roo.grid.PropertyColumnModel = function(grid, store){
60060     this.grid = grid;
60061     var g = Roo.grid;
60062     g.PropertyColumnModel.superclass.constructor.call(this, [
60063         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
60064         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
60065     ]);
60066     this.store = store;
60067     this.bselect = Roo.DomHelper.append(document.body, {
60068         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
60069             {tag: 'option', value: 'true', html: 'true'},
60070             {tag: 'option', value: 'false', html: 'false'}
60071         ]
60072     });
60073     Roo.id(this.bselect);
60074     var f = Roo.form;
60075     this.editors = {
60076         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
60077         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
60078         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
60079         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
60080         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
60081     };
60082     this.renderCellDelegate = this.renderCell.createDelegate(this);
60083     this.renderPropDelegate = this.renderProp.createDelegate(this);
60084 };
60085
60086 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
60087     
60088     
60089     nameText : 'Name',
60090     valueText : 'Value',
60091     
60092     dateFormat : 'm/j/Y',
60093     
60094     
60095     renderDate : function(dateVal){
60096         return dateVal.dateFormat(this.dateFormat);
60097     },
60098
60099     renderBool : function(bVal){
60100         return bVal ? 'true' : 'false';
60101     },
60102
60103     isCellEditable : function(colIndex, rowIndex){
60104         return colIndex == 1;
60105     },
60106
60107     getRenderer : function(col){
60108         return col == 1 ?
60109             this.renderCellDelegate : this.renderPropDelegate;
60110     },
60111
60112     renderProp : function(v){
60113         return this.getPropertyName(v);
60114     },
60115
60116     renderCell : function(val){
60117         var rv = val;
60118         if(val instanceof Date){
60119             rv = this.renderDate(val);
60120         }else if(typeof val == 'boolean'){
60121             rv = this.renderBool(val);
60122         }
60123         return Roo.util.Format.htmlEncode(rv);
60124     },
60125
60126     getPropertyName : function(name){
60127         var pn = this.grid.propertyNames;
60128         return pn && pn[name] ? pn[name] : name;
60129     },
60130
60131     getCellEditor : function(colIndex, rowIndex){
60132         var p = this.store.getProperty(rowIndex);
60133         var n = p.data['name'], val = p.data['value'];
60134         
60135         if(typeof(this.grid.customEditors[n]) == 'string'){
60136             return this.editors[this.grid.customEditors[n]];
60137         }
60138         if(typeof(this.grid.customEditors[n]) != 'undefined'){
60139             return this.grid.customEditors[n];
60140         }
60141         if(val instanceof Date){
60142             return this.editors['date'];
60143         }else if(typeof val == 'number'){
60144             return this.editors['number'];
60145         }else if(typeof val == 'boolean'){
60146             return this.editors['boolean'];
60147         }else{
60148             return this.editors['string'];
60149         }
60150     }
60151 });
60152
60153 /**
60154  * @class Roo.grid.PropertyGrid
60155  * @extends Roo.grid.EditorGrid
60156  * This class represents the  interface of a component based property grid control.
60157  * <br><br>Usage:<pre><code>
60158  var grid = new Roo.grid.PropertyGrid("my-container-id", {
60159       
60160  });
60161  // set any options
60162  grid.render();
60163  * </code></pre>
60164   
60165  * @constructor
60166  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60167  * The container MUST have some type of size defined for the grid to fill. The container will be
60168  * automatically set to position relative if it isn't already.
60169  * @param {Object} config A config object that sets properties on this grid.
60170  */
60171 Roo.grid.PropertyGrid = function(container, config){
60172     config = config || {};
60173     var store = new Roo.grid.PropertyStore(this);
60174     this.store = store;
60175     var cm = new Roo.grid.PropertyColumnModel(this, store);
60176     store.store.sort('name', 'ASC');
60177     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
60178         ds: store.store,
60179         cm: cm,
60180         enableColLock:false,
60181         enableColumnMove:false,
60182         stripeRows:false,
60183         trackMouseOver: false,
60184         clicksToEdit:1
60185     }, config));
60186     this.getGridEl().addClass('x-props-grid');
60187     this.lastEditRow = null;
60188     this.on('columnresize', this.onColumnResize, this);
60189     this.addEvents({
60190          /**
60191              * @event beforepropertychange
60192              * Fires before a property changes (return false to stop?)
60193              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60194              * @param {String} id Record Id
60195              * @param {String} newval New Value
60196          * @param {String} oldval Old Value
60197              */
60198         "beforepropertychange": true,
60199         /**
60200              * @event propertychange
60201              * Fires after a property changes
60202              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
60203              * @param {String} id Record Id
60204              * @param {String} newval New Value
60205          * @param {String} oldval Old Value
60206              */
60207         "propertychange": true
60208     });
60209     this.customEditors = this.customEditors || {};
60210 };
60211 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
60212     
60213      /**
60214      * @cfg {Object} customEditors map of colnames=> custom editors.
60215      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
60216      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
60217      * false disables editing of the field.
60218          */
60219     
60220       /**
60221      * @cfg {Object} propertyNames map of property Names to their displayed value
60222          */
60223     
60224     render : function(){
60225         Roo.grid.PropertyGrid.superclass.render.call(this);
60226         this.autoSize.defer(100, this);
60227     },
60228
60229     autoSize : function(){
60230         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
60231         if(this.view){
60232             this.view.fitColumns();
60233         }
60234     },
60235
60236     onColumnResize : function(){
60237         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
60238         this.autoSize();
60239     },
60240     /**
60241      * Sets the data for the Grid
60242      * accepts a Key => Value object of all the elements avaiable.
60243      * @param {Object} data  to appear in grid.
60244      */
60245     setSource : function(source){
60246         this.store.setSource(source);
60247         //this.autoSize();
60248     },
60249     /**
60250      * Gets all the data from the grid.
60251      * @return {Object} data  data stored in grid
60252      */
60253     getSource : function(){
60254         return this.store.getSource();
60255     }
60256 });/*
60257   
60258  * Licence LGPL
60259  
60260  */
60261  
60262 /**
60263  * @class Roo.grid.Calendar
60264  * @extends Roo.util.Grid
60265  * This class extends the Grid to provide a calendar widget
60266  * <br><br>Usage:<pre><code>
60267  var grid = new Roo.grid.Calendar("my-container-id", {
60268      ds: myDataStore,
60269      cm: myColModel,
60270      selModel: mySelectionModel,
60271      autoSizeColumns: true,
60272      monitorWindowResize: false,
60273      trackMouseOver: true
60274      eventstore : real data store..
60275  });
60276  // set any options
60277  grid.render();
60278   
60279   * @constructor
60280  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
60281  * The container MUST have some type of size defined for the grid to fill. The container will be
60282  * automatically set to position relative if it isn't already.
60283  * @param {Object} config A config object that sets properties on this grid.
60284  */
60285 Roo.grid.Calendar = function(container, config){
60286         // initialize the container
60287         this.container = Roo.get(container);
60288         this.container.update("");
60289         this.container.setStyle("overflow", "hidden");
60290     this.container.addClass('x-grid-container');
60291
60292     this.id = this.container.id;
60293
60294     Roo.apply(this, config);
60295     // check and correct shorthanded configs
60296     
60297     var rows = [];
60298     var d =1;
60299     for (var r = 0;r < 6;r++) {
60300         
60301         rows[r]=[];
60302         for (var c =0;c < 7;c++) {
60303             rows[r][c]= '';
60304         }
60305     }
60306     if (this.eventStore) {
60307         this.eventStore= Roo.factory(this.eventStore, Roo.data);
60308         this.eventStore.on('load',this.onLoad, this);
60309         this.eventStore.on('beforeload',this.clearEvents, this);
60310          
60311     }
60312     
60313     this.dataSource = new Roo.data.Store({
60314             proxy: new Roo.data.MemoryProxy(rows),
60315             reader: new Roo.data.ArrayReader({}, [
60316                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
60317     });
60318
60319     this.dataSource.load();
60320     this.ds = this.dataSource;
60321     this.ds.xmodule = this.xmodule || false;
60322     
60323     
60324     var cellRender = function(v,x,r)
60325     {
60326         return String.format(
60327             '<div class="fc-day  fc-widget-content"><div>' +
60328                 '<div class="fc-event-container"></div>' +
60329                 '<div class="fc-day-number">{0}</div>'+
60330                 
60331                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
60332             '</div></div>', v);
60333     
60334     }
60335     
60336     
60337     this.colModel = new Roo.grid.ColumnModel( [
60338         {
60339             xtype: 'ColumnModel',
60340             xns: Roo.grid,
60341             dataIndex : 'weekday0',
60342             header : 'Sunday',
60343             renderer : cellRender
60344         },
60345         {
60346             xtype: 'ColumnModel',
60347             xns: Roo.grid,
60348             dataIndex : 'weekday1',
60349             header : 'Monday',
60350             renderer : cellRender
60351         },
60352         {
60353             xtype: 'ColumnModel',
60354             xns: Roo.grid,
60355             dataIndex : 'weekday2',
60356             header : 'Tuesday',
60357             renderer : cellRender
60358         },
60359         {
60360             xtype: 'ColumnModel',
60361             xns: Roo.grid,
60362             dataIndex : 'weekday3',
60363             header : 'Wednesday',
60364             renderer : cellRender
60365         },
60366         {
60367             xtype: 'ColumnModel',
60368             xns: Roo.grid,
60369             dataIndex : 'weekday4',
60370             header : 'Thursday',
60371             renderer : cellRender
60372         },
60373         {
60374             xtype: 'ColumnModel',
60375             xns: Roo.grid,
60376             dataIndex : 'weekday5',
60377             header : 'Friday',
60378             renderer : cellRender
60379         },
60380         {
60381             xtype: 'ColumnModel',
60382             xns: Roo.grid,
60383             dataIndex : 'weekday6',
60384             header : 'Saturday',
60385             renderer : cellRender
60386         }
60387     ]);
60388     this.cm = this.colModel;
60389     this.cm.xmodule = this.xmodule || false;
60390  
60391         
60392           
60393     //this.selModel = new Roo.grid.CellSelectionModel();
60394     //this.sm = this.selModel;
60395     //this.selModel.init(this);
60396     
60397     
60398     if(this.width){
60399         this.container.setWidth(this.width);
60400     }
60401
60402     if(this.height){
60403         this.container.setHeight(this.height);
60404     }
60405     /** @private */
60406         this.addEvents({
60407         // raw events
60408         /**
60409          * @event click
60410          * The raw click event for the entire grid.
60411          * @param {Roo.EventObject} e
60412          */
60413         "click" : true,
60414         /**
60415          * @event dblclick
60416          * The raw dblclick event for the entire grid.
60417          * @param {Roo.EventObject} e
60418          */
60419         "dblclick" : true,
60420         /**
60421          * @event contextmenu
60422          * The raw contextmenu event for the entire grid.
60423          * @param {Roo.EventObject} e
60424          */
60425         "contextmenu" : true,
60426         /**
60427          * @event mousedown
60428          * The raw mousedown event for the entire grid.
60429          * @param {Roo.EventObject} e
60430          */
60431         "mousedown" : true,
60432         /**
60433          * @event mouseup
60434          * The raw mouseup event for the entire grid.
60435          * @param {Roo.EventObject} e
60436          */
60437         "mouseup" : true,
60438         /**
60439          * @event mouseover
60440          * The raw mouseover event for the entire grid.
60441          * @param {Roo.EventObject} e
60442          */
60443         "mouseover" : true,
60444         /**
60445          * @event mouseout
60446          * The raw mouseout event for the entire grid.
60447          * @param {Roo.EventObject} e
60448          */
60449         "mouseout" : true,
60450         /**
60451          * @event keypress
60452          * The raw keypress event for the entire grid.
60453          * @param {Roo.EventObject} e
60454          */
60455         "keypress" : true,
60456         /**
60457          * @event keydown
60458          * The raw keydown event for the entire grid.
60459          * @param {Roo.EventObject} e
60460          */
60461         "keydown" : true,
60462
60463         // custom events
60464
60465         /**
60466          * @event cellclick
60467          * Fires when a cell is clicked
60468          * @param {Grid} this
60469          * @param {Number} rowIndex
60470          * @param {Number} columnIndex
60471          * @param {Roo.EventObject} e
60472          */
60473         "cellclick" : true,
60474         /**
60475          * @event celldblclick
60476          * Fires when a cell is double clicked
60477          * @param {Grid} this
60478          * @param {Number} rowIndex
60479          * @param {Number} columnIndex
60480          * @param {Roo.EventObject} e
60481          */
60482         "celldblclick" : true,
60483         /**
60484          * @event rowclick
60485          * Fires when a row is clicked
60486          * @param {Grid} this
60487          * @param {Number} rowIndex
60488          * @param {Roo.EventObject} e
60489          */
60490         "rowclick" : true,
60491         /**
60492          * @event rowdblclick
60493          * Fires when a row is double clicked
60494          * @param {Grid} this
60495          * @param {Number} rowIndex
60496          * @param {Roo.EventObject} e
60497          */
60498         "rowdblclick" : true,
60499         /**
60500          * @event headerclick
60501          * Fires when a header is clicked
60502          * @param {Grid} this
60503          * @param {Number} columnIndex
60504          * @param {Roo.EventObject} e
60505          */
60506         "headerclick" : true,
60507         /**
60508          * @event headerdblclick
60509          * Fires when a header cell is double clicked
60510          * @param {Grid} this
60511          * @param {Number} columnIndex
60512          * @param {Roo.EventObject} e
60513          */
60514         "headerdblclick" : true,
60515         /**
60516          * @event rowcontextmenu
60517          * Fires when a row is right clicked
60518          * @param {Grid} this
60519          * @param {Number} rowIndex
60520          * @param {Roo.EventObject} e
60521          */
60522         "rowcontextmenu" : true,
60523         /**
60524          * @event cellcontextmenu
60525          * Fires when a cell is right clicked
60526          * @param {Grid} this
60527          * @param {Number} rowIndex
60528          * @param {Number} cellIndex
60529          * @param {Roo.EventObject} e
60530          */
60531          "cellcontextmenu" : true,
60532         /**
60533          * @event headercontextmenu
60534          * Fires when a header is right clicked
60535          * @param {Grid} this
60536          * @param {Number} columnIndex
60537          * @param {Roo.EventObject} e
60538          */
60539         "headercontextmenu" : true,
60540         /**
60541          * @event bodyscroll
60542          * Fires when the body element is scrolled
60543          * @param {Number} scrollLeft
60544          * @param {Number} scrollTop
60545          */
60546         "bodyscroll" : true,
60547         /**
60548          * @event columnresize
60549          * Fires when the user resizes a column
60550          * @param {Number} columnIndex
60551          * @param {Number} newSize
60552          */
60553         "columnresize" : true,
60554         /**
60555          * @event columnmove
60556          * Fires when the user moves a column
60557          * @param {Number} oldIndex
60558          * @param {Number} newIndex
60559          */
60560         "columnmove" : true,
60561         /**
60562          * @event startdrag
60563          * Fires when row(s) start being dragged
60564          * @param {Grid} this
60565          * @param {Roo.GridDD} dd The drag drop object
60566          * @param {event} e The raw browser event
60567          */
60568         "startdrag" : true,
60569         /**
60570          * @event enddrag
60571          * Fires when a drag operation is complete
60572          * @param {Grid} this
60573          * @param {Roo.GridDD} dd The drag drop object
60574          * @param {event} e The raw browser event
60575          */
60576         "enddrag" : true,
60577         /**
60578          * @event dragdrop
60579          * Fires when dragged row(s) are dropped on a valid DD target
60580          * @param {Grid} this
60581          * @param {Roo.GridDD} dd The drag drop object
60582          * @param {String} targetId The target drag drop object
60583          * @param {event} e The raw browser event
60584          */
60585         "dragdrop" : true,
60586         /**
60587          * @event dragover
60588          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
60589          * @param {Grid} this
60590          * @param {Roo.GridDD} dd The drag drop object
60591          * @param {String} targetId The target drag drop object
60592          * @param {event} e The raw browser event
60593          */
60594         "dragover" : true,
60595         /**
60596          * @event dragenter
60597          *  Fires when the dragged row(s) first cross another DD target while being dragged
60598          * @param {Grid} this
60599          * @param {Roo.GridDD} dd The drag drop object
60600          * @param {String} targetId The target drag drop object
60601          * @param {event} e The raw browser event
60602          */
60603         "dragenter" : true,
60604         /**
60605          * @event dragout
60606          * Fires when the dragged row(s) leave another DD target while being dragged
60607          * @param {Grid} this
60608          * @param {Roo.GridDD} dd The drag drop object
60609          * @param {String} targetId The target drag drop object
60610          * @param {event} e The raw browser event
60611          */
60612         "dragout" : true,
60613         /**
60614          * @event rowclass
60615          * Fires when a row is rendered, so you can change add a style to it.
60616          * @param {GridView} gridview   The grid view
60617          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
60618          */
60619         'rowclass' : true,
60620
60621         /**
60622          * @event render
60623          * Fires when the grid is rendered
60624          * @param {Grid} grid
60625          */
60626         'render' : true,
60627             /**
60628              * @event select
60629              * Fires when a date is selected
60630              * @param {DatePicker} this
60631              * @param {Date} date The selected date
60632              */
60633         'select': true,
60634         /**
60635              * @event monthchange
60636              * Fires when the displayed month changes 
60637              * @param {DatePicker} this
60638              * @param {Date} date The selected month
60639              */
60640         'monthchange': true,
60641         /**
60642              * @event evententer
60643              * Fires when mouse over an event
60644              * @param {Calendar} this
60645              * @param {event} Event
60646              */
60647         'evententer': true,
60648         /**
60649              * @event eventleave
60650              * Fires when the mouse leaves an
60651              * @param {Calendar} this
60652              * @param {event}
60653              */
60654         'eventleave': true,
60655         /**
60656              * @event eventclick
60657              * Fires when the mouse click an
60658              * @param {Calendar} this
60659              * @param {event}
60660              */
60661         'eventclick': true,
60662         /**
60663              * @event eventrender
60664              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
60665              * @param {Calendar} this
60666              * @param {data} data to be modified
60667              */
60668         'eventrender': true
60669         
60670     });
60671
60672     Roo.grid.Grid.superclass.constructor.call(this);
60673     this.on('render', function() {
60674         this.view.el.addClass('x-grid-cal'); 
60675         
60676         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
60677
60678     },this);
60679     
60680     if (!Roo.grid.Calendar.style) {
60681         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
60682             
60683             
60684             '.x-grid-cal .x-grid-col' :  {
60685                 height: 'auto !important',
60686                 'vertical-align': 'top'
60687             },
60688             '.x-grid-cal  .fc-event-hori' : {
60689                 height: '14px'
60690             }
60691              
60692             
60693         }, Roo.id());
60694     }
60695
60696     
60697     
60698 };
60699 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
60700     /**
60701      * @cfg {Store} eventStore The store that loads events.
60702      */
60703     eventStore : 25,
60704
60705      
60706     activeDate : false,
60707     startDay : 0,
60708     autoWidth : true,
60709     monitorWindowResize : false,
60710
60711     
60712     resizeColumns : function() {
60713         var col = (this.view.el.getWidth() / 7) - 3;
60714         // loop through cols, and setWidth
60715         for(var i =0 ; i < 7 ; i++){
60716             this.cm.setColumnWidth(i, col);
60717         }
60718     },
60719      setDate :function(date) {
60720         
60721         Roo.log('setDate?');
60722         
60723         this.resizeColumns();
60724         var vd = this.activeDate;
60725         this.activeDate = date;
60726 //        if(vd && this.el){
60727 //            var t = date.getTime();
60728 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
60729 //                Roo.log('using add remove');
60730 //                
60731 //                this.fireEvent('monthchange', this, date);
60732 //                
60733 //                this.cells.removeClass("fc-state-highlight");
60734 //                this.cells.each(function(c){
60735 //                   if(c.dateValue == t){
60736 //                       c.addClass("fc-state-highlight");
60737 //                       setTimeout(function(){
60738 //                            try{c.dom.firstChild.focus();}catch(e){}
60739 //                       }, 50);
60740 //                       return false;
60741 //                   }
60742 //                   return true;
60743 //                });
60744 //                return;
60745 //            }
60746 //        }
60747         
60748         var days = date.getDaysInMonth();
60749         
60750         var firstOfMonth = date.getFirstDateOfMonth();
60751         var startingPos = firstOfMonth.getDay()-this.startDay;
60752         
60753         if(startingPos < this.startDay){
60754             startingPos += 7;
60755         }
60756         
60757         var pm = date.add(Date.MONTH, -1);
60758         var prevStart = pm.getDaysInMonth()-startingPos;
60759 //        
60760         
60761         
60762         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60763         
60764         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
60765         //this.cells.addClassOnOver('fc-state-hover');
60766         
60767         var cells = this.cells.elements;
60768         var textEls = this.textNodes;
60769         
60770         //Roo.each(cells, function(cell){
60771         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
60772         //});
60773         
60774         days += startingPos;
60775
60776         // convert everything to numbers so it's fast
60777         var day = 86400000;
60778         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
60779         //Roo.log(d);
60780         //Roo.log(pm);
60781         //Roo.log(prevStart);
60782         
60783         var today = new Date().clearTime().getTime();
60784         var sel = date.clearTime().getTime();
60785         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
60786         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
60787         var ddMatch = this.disabledDatesRE;
60788         var ddText = this.disabledDatesText;
60789         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
60790         var ddaysText = this.disabledDaysText;
60791         var format = this.format;
60792         
60793         var setCellClass = function(cal, cell){
60794             
60795             //Roo.log('set Cell Class');
60796             cell.title = "";
60797             var t = d.getTime();
60798             
60799             //Roo.log(d);
60800             
60801             
60802             cell.dateValue = t;
60803             if(t == today){
60804                 cell.className += " fc-today";
60805                 cell.className += " fc-state-highlight";
60806                 cell.title = cal.todayText;
60807             }
60808             if(t == sel){
60809                 // disable highlight in other month..
60810                 cell.className += " fc-state-highlight";
60811                 
60812             }
60813             // disabling
60814             if(t < min) {
60815                 //cell.className = " fc-state-disabled";
60816                 cell.title = cal.minText;
60817                 return;
60818             }
60819             if(t > max) {
60820                 //cell.className = " fc-state-disabled";
60821                 cell.title = cal.maxText;
60822                 return;
60823             }
60824             if(ddays){
60825                 if(ddays.indexOf(d.getDay()) != -1){
60826                     // cell.title = ddaysText;
60827                    // cell.className = " fc-state-disabled";
60828                 }
60829             }
60830             if(ddMatch && format){
60831                 var fvalue = d.dateFormat(format);
60832                 if(ddMatch.test(fvalue)){
60833                     cell.title = ddText.replace("%0", fvalue);
60834                    cell.className = " fc-state-disabled";
60835                 }
60836             }
60837             
60838             if (!cell.initialClassName) {
60839                 cell.initialClassName = cell.dom.className;
60840             }
60841             
60842             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
60843         };
60844
60845         var i = 0;
60846         
60847         for(; i < startingPos; i++) {
60848             cells[i].dayName =  (++prevStart);
60849             Roo.log(textEls[i]);
60850             d.setDate(d.getDate()+1);
60851             
60852             //cells[i].className = "fc-past fc-other-month";
60853             setCellClass(this, cells[i]);
60854         }
60855         
60856         var intDay = 0;
60857         
60858         for(; i < days; i++){
60859             intDay = i - startingPos + 1;
60860             cells[i].dayName =  (intDay);
60861             d.setDate(d.getDate()+1);
60862             
60863             cells[i].className = ''; // "x-date-active";
60864             setCellClass(this, cells[i]);
60865         }
60866         var extraDays = 0;
60867         
60868         for(; i < 42; i++) {
60869             //textEls[i].innerHTML = (++extraDays);
60870             
60871             d.setDate(d.getDate()+1);
60872             cells[i].dayName = (++extraDays);
60873             cells[i].className = "fc-future fc-other-month";
60874             setCellClass(this, cells[i]);
60875         }
60876         
60877         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
60878         
60879         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
60880         
60881         // this will cause all the cells to mis
60882         var rows= [];
60883         var i =0;
60884         for (var r = 0;r < 6;r++) {
60885             for (var c =0;c < 7;c++) {
60886                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
60887             }    
60888         }
60889         
60890         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
60891         for(i=0;i<cells.length;i++) {
60892             
60893             this.cells.elements[i].dayName = cells[i].dayName ;
60894             this.cells.elements[i].className = cells[i].className;
60895             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
60896             this.cells.elements[i].title = cells[i].title ;
60897             this.cells.elements[i].dateValue = cells[i].dateValue ;
60898         }
60899         
60900         
60901         
60902         
60903         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
60904         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
60905         
60906         ////if(totalRows != 6){
60907             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
60908            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
60909        // }
60910         
60911         this.fireEvent('monthchange', this, date);
60912         
60913         
60914     },
60915  /**
60916      * Returns the grid's SelectionModel.
60917      * @return {SelectionModel}
60918      */
60919     getSelectionModel : function(){
60920         if(!this.selModel){
60921             this.selModel = new Roo.grid.CellSelectionModel();
60922         }
60923         return this.selModel;
60924     },
60925
60926     load: function() {
60927         this.eventStore.load()
60928         
60929         
60930         
60931     },
60932     
60933     findCell : function(dt) {
60934         dt = dt.clearTime().getTime();
60935         var ret = false;
60936         this.cells.each(function(c){
60937             //Roo.log("check " +c.dateValue + '?=' + dt);
60938             if(c.dateValue == dt){
60939                 ret = c;
60940                 return false;
60941             }
60942             return true;
60943         });
60944         
60945         return ret;
60946     },
60947     
60948     findCells : function(rec) {
60949         var s = rec.data.start_dt.clone().clearTime().getTime();
60950        // Roo.log(s);
60951         var e= rec.data.end_dt.clone().clearTime().getTime();
60952        // Roo.log(e);
60953         var ret = [];
60954         this.cells.each(function(c){
60955              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
60956             
60957             if(c.dateValue > e){
60958                 return ;
60959             }
60960             if(c.dateValue < s){
60961                 return ;
60962             }
60963             ret.push(c);
60964         });
60965         
60966         return ret;    
60967     },
60968     
60969     findBestRow: function(cells)
60970     {
60971         var ret = 0;
60972         
60973         for (var i =0 ; i < cells.length;i++) {
60974             ret  = Math.max(cells[i].rows || 0,ret);
60975         }
60976         return ret;
60977         
60978     },
60979     
60980     
60981     addItem : function(rec)
60982     {
60983         // look for vertical location slot in
60984         var cells = this.findCells(rec);
60985         
60986         rec.row = this.findBestRow(cells);
60987         
60988         // work out the location.
60989         
60990         var crow = false;
60991         var rows = [];
60992         for(var i =0; i < cells.length; i++) {
60993             if (!crow) {
60994                 crow = {
60995                     start : cells[i],
60996                     end :  cells[i]
60997                 };
60998                 continue;
60999             }
61000             if (crow.start.getY() == cells[i].getY()) {
61001                 // on same row.
61002                 crow.end = cells[i];
61003                 continue;
61004             }
61005             // different row.
61006             rows.push(crow);
61007             crow = {
61008                 start: cells[i],
61009                 end : cells[i]
61010             };
61011             
61012         }
61013         
61014         rows.push(crow);
61015         rec.els = [];
61016         rec.rows = rows;
61017         rec.cells = cells;
61018         for (var i = 0; i < cells.length;i++) {
61019             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
61020             
61021         }
61022         
61023         
61024     },
61025     
61026     clearEvents: function() {
61027         
61028         if (!this.eventStore.getCount()) {
61029             return;
61030         }
61031         // reset number of rows in cells.
61032         Roo.each(this.cells.elements, function(c){
61033             c.rows = 0;
61034         });
61035         
61036         this.eventStore.each(function(e) {
61037             this.clearEvent(e);
61038         },this);
61039         
61040     },
61041     
61042     clearEvent : function(ev)
61043     {
61044         if (ev.els) {
61045             Roo.each(ev.els, function(el) {
61046                 el.un('mouseenter' ,this.onEventEnter, this);
61047                 el.un('mouseleave' ,this.onEventLeave, this);
61048                 el.remove();
61049             },this);
61050             ev.els = [];
61051         }
61052     },
61053     
61054     
61055     renderEvent : function(ev,ctr) {
61056         if (!ctr) {
61057              ctr = this.view.el.select('.fc-event-container',true).first();
61058         }
61059         
61060          
61061         this.clearEvent(ev);
61062             //code
61063        
61064         
61065         
61066         ev.els = [];
61067         var cells = ev.cells;
61068         var rows = ev.rows;
61069         this.fireEvent('eventrender', this, ev);
61070         
61071         for(var i =0; i < rows.length; i++) {
61072             
61073             cls = '';
61074             if (i == 0) {
61075                 cls += ' fc-event-start';
61076             }
61077             if ((i+1) == rows.length) {
61078                 cls += ' fc-event-end';
61079             }
61080             
61081             //Roo.log(ev.data);
61082             // how many rows should it span..
61083             var cg = this.eventTmpl.append(ctr,Roo.apply({
61084                 fccls : cls
61085                 
61086             }, ev.data) , true);
61087             
61088             
61089             cg.on('mouseenter' ,this.onEventEnter, this, ev);
61090             cg.on('mouseleave' ,this.onEventLeave, this, ev);
61091             cg.on('click', this.onEventClick, this, ev);
61092             
61093             ev.els.push(cg);
61094             
61095             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
61096             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
61097             //Roo.log(cg);
61098              
61099             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
61100             cg.setWidth(ebox.right - sbox.x -2);
61101         }
61102     },
61103     
61104     renderEvents: function()
61105     {   
61106         // first make sure there is enough space..
61107         
61108         if (!this.eventTmpl) {
61109             this.eventTmpl = new Roo.Template(
61110                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
61111                     '<div class="fc-event-inner">' +
61112                         '<span class="fc-event-time">{time}</span>' +
61113                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
61114                     '</div>' +
61115                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
61116                 '</div>'
61117             );
61118                 
61119         }
61120                
61121         
61122         
61123         this.cells.each(function(c) {
61124             //Roo.log(c.select('.fc-day-content div',true).first());
61125             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
61126         });
61127         
61128         var ctr = this.view.el.select('.fc-event-container',true).first();
61129         
61130         var cls;
61131         this.eventStore.each(function(ev){
61132             
61133             this.renderEvent(ev);
61134              
61135              
61136         }, this);
61137         this.view.layout();
61138         
61139     },
61140     
61141     onEventEnter: function (e, el,event,d) {
61142         this.fireEvent('evententer', this, el, event);
61143     },
61144     
61145     onEventLeave: function (e, el,event,d) {
61146         this.fireEvent('eventleave', this, el, event);
61147     },
61148     
61149     onEventClick: function (e, el,event,d) {
61150         this.fireEvent('eventclick', this, el, event);
61151     },
61152     
61153     onMonthChange: function () {
61154         this.store.load();
61155     },
61156     
61157     onLoad: function () {
61158         
61159         //Roo.log('calendar onload');
61160 //         
61161         if(this.eventStore.getCount() > 0){
61162             
61163            
61164             
61165             this.eventStore.each(function(d){
61166                 
61167                 
61168                 // FIXME..
61169                 var add =   d.data;
61170                 if (typeof(add.end_dt) == 'undefined')  {
61171                     Roo.log("Missing End time in calendar data: ");
61172                     Roo.log(d);
61173                     return;
61174                 }
61175                 if (typeof(add.start_dt) == 'undefined')  {
61176                     Roo.log("Missing Start time in calendar data: ");
61177                     Roo.log(d);
61178                     return;
61179                 }
61180                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
61181                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
61182                 add.id = add.id || d.id;
61183                 add.title = add.title || '??';
61184                 
61185                 this.addItem(d);
61186                 
61187              
61188             },this);
61189         }
61190         
61191         this.renderEvents();
61192     }
61193     
61194
61195 });
61196 /*
61197  grid : {
61198                 xtype: 'Grid',
61199                 xns: Roo.grid,
61200                 listeners : {
61201                     render : function ()
61202                     {
61203                         _this.grid = this;
61204                         
61205                         if (!this.view.el.hasClass('course-timesheet')) {
61206                             this.view.el.addClass('course-timesheet');
61207                         }
61208                         if (this.tsStyle) {
61209                             this.ds.load({});
61210                             return; 
61211                         }
61212                         Roo.log('width');
61213                         Roo.log(_this.grid.view.el.getWidth());
61214                         
61215                         
61216                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
61217                             '.course-timesheet .x-grid-row' : {
61218                                 height: '80px'
61219                             },
61220                             '.x-grid-row td' : {
61221                                 'vertical-align' : 0
61222                             },
61223                             '.course-edit-link' : {
61224                                 'color' : 'blue',
61225                                 'text-overflow' : 'ellipsis',
61226                                 'overflow' : 'hidden',
61227                                 'white-space' : 'nowrap',
61228                                 'cursor' : 'pointer'
61229                             },
61230                             '.sub-link' : {
61231                                 'color' : 'green'
61232                             },
61233                             '.de-act-sup-link' : {
61234                                 'color' : 'purple',
61235                                 'text-decoration' : 'line-through'
61236                             },
61237                             '.de-act-link' : {
61238                                 'color' : 'red',
61239                                 'text-decoration' : 'line-through'
61240                             },
61241                             '.course-timesheet .course-highlight' : {
61242                                 'border-top-style': 'dashed !important',
61243                                 'border-bottom-bottom': 'dashed !important'
61244                             },
61245                             '.course-timesheet .course-item' : {
61246                                 'font-family'   : 'tahoma, arial, helvetica',
61247                                 'font-size'     : '11px',
61248                                 'overflow'      : 'hidden',
61249                                 'padding-left'  : '10px',
61250                                 'padding-right' : '10px',
61251                                 'padding-top' : '10px' 
61252                             }
61253                             
61254                         }, Roo.id());
61255                                 this.ds.load({});
61256                     }
61257                 },
61258                 autoWidth : true,
61259                 monitorWindowResize : false,
61260                 cellrenderer : function(v,x,r)
61261                 {
61262                     return v;
61263                 },
61264                 sm : {
61265                     xtype: 'CellSelectionModel',
61266                     xns: Roo.grid
61267                 },
61268                 dataSource : {
61269                     xtype: 'Store',
61270                     xns: Roo.data,
61271                     listeners : {
61272                         beforeload : function (_self, options)
61273                         {
61274                             options.params = options.params || {};
61275                             options.params._month = _this.monthField.getValue();
61276                             options.params.limit = 9999;
61277                             options.params['sort'] = 'when_dt';    
61278                             options.params['dir'] = 'ASC';    
61279                             this.proxy.loadResponse = this.loadResponse;
61280                             Roo.log("load?");
61281                             //this.addColumns();
61282                         },
61283                         load : function (_self, records, options)
61284                         {
61285                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
61286                                 // if you click on the translation.. you can edit it...
61287                                 var el = Roo.get(this);
61288                                 var id = el.dom.getAttribute('data-id');
61289                                 var d = el.dom.getAttribute('data-date');
61290                                 var t = el.dom.getAttribute('data-time');
61291                                 //var id = this.child('span').dom.textContent;
61292                                 
61293                                 //Roo.log(this);
61294                                 Pman.Dialog.CourseCalendar.show({
61295                                     id : id,
61296                                     when_d : d,
61297                                     when_t : t,
61298                                     productitem_active : id ? 1 : 0
61299                                 }, function() {
61300                                     _this.grid.ds.load({});
61301                                 });
61302                            
61303                            });
61304                            
61305                            _this.panel.fireEvent('resize', [ '', '' ]);
61306                         }
61307                     },
61308                     loadResponse : function(o, success, response){
61309                             // this is overridden on before load..
61310                             
61311                             Roo.log("our code?");       
61312                             //Roo.log(success);
61313                             //Roo.log(response)
61314                             delete this.activeRequest;
61315                             if(!success){
61316                                 this.fireEvent("loadexception", this, o, response);
61317                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61318                                 return;
61319                             }
61320                             var result;
61321                             try {
61322                                 result = o.reader.read(response);
61323                             }catch(e){
61324                                 Roo.log("load exception?");
61325                                 this.fireEvent("loadexception", this, o, response, e);
61326                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
61327                                 return;
61328                             }
61329                             Roo.log("ready...");        
61330                             // loop through result.records;
61331                             // and set this.tdate[date] = [] << array of records..
61332                             _this.tdata  = {};
61333                             Roo.each(result.records, function(r){
61334                                 //Roo.log(r.data);
61335                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
61336                                     _this.tdata[r.data.when_dt.format('j')] = [];
61337                                 }
61338                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
61339                             });
61340                             
61341                             //Roo.log(_this.tdata);
61342                             
61343                             result.records = [];
61344                             result.totalRecords = 6;
61345                     
61346                             // let's generate some duumy records for the rows.
61347                             //var st = _this.dateField.getValue();
61348                             
61349                             // work out monday..
61350                             //st = st.add(Date.DAY, -1 * st.format('w'));
61351                             
61352                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61353                             
61354                             var firstOfMonth = date.getFirstDayOfMonth();
61355                             var days = date.getDaysInMonth();
61356                             var d = 1;
61357                             var firstAdded = false;
61358                             for (var i = 0; i < result.totalRecords ; i++) {
61359                                 //var d= st.add(Date.DAY, i);
61360                                 var row = {};
61361                                 var added = 0;
61362                                 for(var w = 0 ; w < 7 ; w++){
61363                                     if(!firstAdded && firstOfMonth != w){
61364                                         continue;
61365                                     }
61366                                     if(d > days){
61367                                         continue;
61368                                     }
61369                                     firstAdded = true;
61370                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
61371                                     row['weekday'+w] = String.format(
61372                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
61373                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
61374                                                     d,
61375                                                     date.format('Y-m-')+dd
61376                                                 );
61377                                     added++;
61378                                     if(typeof(_this.tdata[d]) != 'undefined'){
61379                                         Roo.each(_this.tdata[d], function(r){
61380                                             var is_sub = '';
61381                                             var deactive = '';
61382                                             var id = r.id;
61383                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
61384                                             if(r.parent_id*1>0){
61385                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
61386                                                 id = r.parent_id;
61387                                             }
61388                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
61389                                                 deactive = 'de-act-link';
61390                                             }
61391                                             
61392                                             row['weekday'+w] += String.format(
61393                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
61394                                                     id, //0
61395                                                     r.product_id_name, //1
61396                                                     r.when_dt.format('h:ia'), //2
61397                                                     is_sub, //3
61398                                                     deactive, //4
61399                                                     desc // 5
61400                                             );
61401                                         });
61402                                     }
61403                                     d++;
61404                                 }
61405                                 
61406                                 // only do this if something added..
61407                                 if(added > 0){ 
61408                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
61409                                 }
61410                                 
61411                                 
61412                                 // push it twice. (second one with an hour..
61413                                 
61414                             }
61415                             //Roo.log(result);
61416                             this.fireEvent("load", this, o, o.request.arg);
61417                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
61418                         },
61419                     sortInfo : {field: 'when_dt', direction : 'ASC' },
61420                     proxy : {
61421                         xtype: 'HttpProxy',
61422                         xns: Roo.data,
61423                         method : 'GET',
61424                         url : baseURL + '/Roo/Shop_course.php'
61425                     },
61426                     reader : {
61427                         xtype: 'JsonReader',
61428                         xns: Roo.data,
61429                         id : 'id',
61430                         fields : [
61431                             {
61432                                 'name': 'id',
61433                                 'type': 'int'
61434                             },
61435                             {
61436                                 'name': 'when_dt',
61437                                 'type': 'string'
61438                             },
61439                             {
61440                                 'name': 'end_dt',
61441                                 'type': 'string'
61442                             },
61443                             {
61444                                 'name': 'parent_id',
61445                                 'type': 'int'
61446                             },
61447                             {
61448                                 'name': 'product_id',
61449                                 'type': 'int'
61450                             },
61451                             {
61452                                 'name': 'productitem_id',
61453                                 'type': 'int'
61454                             },
61455                             {
61456                                 'name': 'guid',
61457                                 'type': 'int'
61458                             }
61459                         ]
61460                     }
61461                 },
61462                 toolbar : {
61463                     xtype: 'Toolbar',
61464                     xns: Roo,
61465                     items : [
61466                         {
61467                             xtype: 'Button',
61468                             xns: Roo.Toolbar,
61469                             listeners : {
61470                                 click : function (_self, e)
61471                                 {
61472                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61473                                     sd.setMonth(sd.getMonth()-1);
61474                                     _this.monthField.setValue(sd.format('Y-m-d'));
61475                                     _this.grid.ds.load({});
61476                                 }
61477                             },
61478                             text : "Back"
61479                         },
61480                         {
61481                             xtype: 'Separator',
61482                             xns: Roo.Toolbar
61483                         },
61484                         {
61485                             xtype: 'MonthField',
61486                             xns: Roo.form,
61487                             listeners : {
61488                                 render : function (_self)
61489                                 {
61490                                     _this.monthField = _self;
61491                                    // _this.monthField.set  today
61492                                 },
61493                                 select : function (combo, date)
61494                                 {
61495                                     _this.grid.ds.load({});
61496                                 }
61497                             },
61498                             value : (function() { return new Date(); })()
61499                         },
61500                         {
61501                             xtype: 'Separator',
61502                             xns: Roo.Toolbar
61503                         },
61504                         {
61505                             xtype: 'TextItem',
61506                             xns: Roo.Toolbar,
61507                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
61508                         },
61509                         {
61510                             xtype: 'Fill',
61511                             xns: Roo.Toolbar
61512                         },
61513                         {
61514                             xtype: 'Button',
61515                             xns: Roo.Toolbar,
61516                             listeners : {
61517                                 click : function (_self, e)
61518                                 {
61519                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
61520                                     sd.setMonth(sd.getMonth()+1);
61521                                     _this.monthField.setValue(sd.format('Y-m-d'));
61522                                     _this.grid.ds.load({});
61523                                 }
61524                             },
61525                             text : "Next"
61526                         }
61527                     ]
61528                 },
61529                  
61530             }
61531         };
61532         
61533         *//*
61534  * Based on:
61535  * Ext JS Library 1.1.1
61536  * Copyright(c) 2006-2007, Ext JS, LLC.
61537  *
61538  * Originally Released Under LGPL - original licence link has changed is not relivant.
61539  *
61540  * Fork - LGPL
61541  * <script type="text/javascript">
61542  */
61543  
61544 /**
61545  * @class Roo.LoadMask
61546  * A simple utility class for generically masking elements while loading data.  If the element being masked has
61547  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
61548  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
61549  * element's UpdateManager load indicator and will be destroyed after the initial load.
61550  * @constructor
61551  * Create a new LoadMask
61552  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
61553  * @param {Object} config The config object
61554  */
61555 Roo.LoadMask = function(el, config){
61556     this.el = Roo.get(el);
61557     Roo.apply(this, config);
61558     if(this.store){
61559         this.store.on('beforeload', this.onBeforeLoad, this);
61560         this.store.on('load', this.onLoad, this);
61561         this.store.on('loadexception', this.onLoadException, this);
61562         this.removeMask = false;
61563     }else{
61564         var um = this.el.getUpdateManager();
61565         um.showLoadIndicator = false; // disable the default indicator
61566         um.on('beforeupdate', this.onBeforeLoad, this);
61567         um.on('update', this.onLoad, this);
61568         um.on('failure', this.onLoad, this);
61569         this.removeMask = true;
61570     }
61571 };
61572
61573 Roo.LoadMask.prototype = {
61574     /**
61575      * @cfg {Boolean} removeMask
61576      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
61577      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
61578      */
61579     /**
61580      * @cfg {String} msg
61581      * The text to display in a centered loading message box (defaults to 'Loading...')
61582      */
61583     msg : 'Loading...',
61584     /**
61585      * @cfg {String} msgCls
61586      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
61587      */
61588     msgCls : 'x-mask-loading',
61589
61590     /**
61591      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
61592      * @type Boolean
61593      */
61594     disabled: false,
61595
61596     /**
61597      * Disables the mask to prevent it from being displayed
61598      */
61599     disable : function(){
61600        this.disabled = true;
61601     },
61602
61603     /**
61604      * Enables the mask so that it can be displayed
61605      */
61606     enable : function(){
61607         this.disabled = false;
61608     },
61609     
61610     onLoadException : function()
61611     {
61612         Roo.log(arguments);
61613         
61614         if (typeof(arguments[3]) != 'undefined') {
61615             Roo.MessageBox.alert("Error loading",arguments[3]);
61616         } 
61617         /*
61618         try {
61619             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
61620                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
61621             }   
61622         } catch(e) {
61623             
61624         }
61625         */
61626     
61627         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61628     },
61629     // private
61630     onLoad : function()
61631     {
61632         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
61633     },
61634
61635     // private
61636     onBeforeLoad : function(){
61637         if(!this.disabled){
61638             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
61639         }
61640     },
61641
61642     // private
61643     destroy : function(){
61644         if(this.store){
61645             this.store.un('beforeload', this.onBeforeLoad, this);
61646             this.store.un('load', this.onLoad, this);
61647             this.store.un('loadexception', this.onLoadException, this);
61648         }else{
61649             var um = this.el.getUpdateManager();
61650             um.un('beforeupdate', this.onBeforeLoad, this);
61651             um.un('update', this.onLoad, this);
61652             um.un('failure', this.onLoad, this);
61653         }
61654     }
61655 };/*
61656  * Based on:
61657  * Ext JS Library 1.1.1
61658  * Copyright(c) 2006-2007, Ext JS, LLC.
61659  *
61660  * Originally Released Under LGPL - original licence link has changed is not relivant.
61661  *
61662  * Fork - LGPL
61663  * <script type="text/javascript">
61664  */
61665
61666
61667 /**
61668  * @class Roo.XTemplate
61669  * @extends Roo.Template
61670  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
61671 <pre><code>
61672 var t = new Roo.XTemplate(
61673         '&lt;select name="{name}"&gt;',
61674                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
61675         '&lt;/select&gt;'
61676 );
61677  
61678 // then append, applying the master template values
61679  </code></pre>
61680  *
61681  * Supported features:
61682  *
61683  *  Tags:
61684
61685 <pre><code>
61686       {a_variable} - output encoded.
61687       {a_variable.format:("Y-m-d")} - call a method on the variable
61688       {a_variable:raw} - unencoded output
61689       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
61690       {a_variable:this.method_on_template(...)} - call a method on the template object.
61691  
61692 </code></pre>
61693  *  The tpl tag:
61694 <pre><code>
61695         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
61696         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
61697         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
61698         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
61699   
61700         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
61701         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
61702 </code></pre>
61703  *      
61704  */
61705 Roo.XTemplate = function()
61706 {
61707     Roo.XTemplate.superclass.constructor.apply(this, arguments);
61708     if (this.html) {
61709         this.compile();
61710     }
61711 };
61712
61713
61714 Roo.extend(Roo.XTemplate, Roo.Template, {
61715
61716     /**
61717      * The various sub templates
61718      */
61719     tpls : false,
61720     /**
61721      *
61722      * basic tag replacing syntax
61723      * WORD:WORD()
61724      *
61725      * // you can fake an object call by doing this
61726      *  x.t:(test,tesT) 
61727      * 
61728      */
61729     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
61730
61731     /**
61732      * compile the template
61733      *
61734      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
61735      *
61736      */
61737     compile: function()
61738     {
61739         var s = this.html;
61740      
61741         s = ['<tpl>', s, '</tpl>'].join('');
61742     
61743         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
61744             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
61745             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
61746             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
61747             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
61748             m,
61749             id     = 0,
61750             tpls   = [];
61751     
61752         while(true == !!(m = s.match(re))){
61753             var forMatch   = m[0].match(nameRe),
61754                 ifMatch   = m[0].match(ifRe),
61755                 execMatch   = m[0].match(execRe),
61756                 namedMatch   = m[0].match(namedRe),
61757                 
61758                 exp  = null, 
61759                 fn   = null,
61760                 exec = null,
61761                 name = forMatch && forMatch[1] ? forMatch[1] : '';
61762                 
61763             if (ifMatch) {
61764                 // if - puts fn into test..
61765                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
61766                 if(exp){
61767                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
61768                 }
61769             }
61770             
61771             if (execMatch) {
61772                 // exec - calls a function... returns empty if true is  returned.
61773                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
61774                 if(exp){
61775                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
61776                 }
61777             }
61778             
61779             
61780             if (name) {
61781                 // for = 
61782                 switch(name){
61783                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
61784                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
61785                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
61786                 }
61787             }
61788             var uid = namedMatch ? namedMatch[1] : id;
61789             
61790             
61791             tpls.push({
61792                 id:     namedMatch ? namedMatch[1] : id,
61793                 target: name,
61794                 exec:   exec,
61795                 test:   fn,
61796                 body:   m[1] || ''
61797             });
61798             if (namedMatch) {
61799                 s = s.replace(m[0], '');
61800             } else { 
61801                 s = s.replace(m[0], '{xtpl'+ id + '}');
61802             }
61803             ++id;
61804         }
61805         this.tpls = [];
61806         for(var i = tpls.length-1; i >= 0; --i){
61807             this.compileTpl(tpls[i]);
61808             this.tpls[tpls[i].id] = tpls[i];
61809         }
61810         this.master = tpls[tpls.length-1];
61811         return this;
61812     },
61813     /**
61814      * same as applyTemplate, except it's done to one of the subTemplates
61815      * when using named templates, you can do:
61816      *
61817      * var str = pl.applySubTemplate('your-name', values);
61818      *
61819      * 
61820      * @param {Number} id of the template
61821      * @param {Object} values to apply to template
61822      * @param {Object} parent (normaly the instance of this object)
61823      */
61824     applySubTemplate : function(id, values, parent)
61825     {
61826         
61827         
61828         var t = this.tpls[id];
61829         
61830         
61831         try { 
61832             if(t.test && !t.test.call(this, values, parent)){
61833                 return '';
61834             }
61835         } catch(e) {
61836             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
61837             Roo.log(e.toString());
61838             Roo.log(t.test);
61839             return ''
61840         }
61841         try { 
61842             
61843             if(t.exec && t.exec.call(this, values, parent)){
61844                 return '';
61845             }
61846         } catch(e) {
61847             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
61848             Roo.log(e.toString());
61849             Roo.log(t.exec);
61850             return ''
61851         }
61852         try {
61853             var vs = t.target ? t.target.call(this, values, parent) : values;
61854             parent = t.target ? values : parent;
61855             if(t.target && vs instanceof Array){
61856                 var buf = [];
61857                 for(var i = 0, len = vs.length; i < len; i++){
61858                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
61859                 }
61860                 return buf.join('');
61861             }
61862             return t.compiled.call(this, vs, parent);
61863         } catch (e) {
61864             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
61865             Roo.log(e.toString());
61866             Roo.log(t.compiled);
61867             return '';
61868         }
61869     },
61870
61871     compileTpl : function(tpl)
61872     {
61873         var fm = Roo.util.Format;
61874         var useF = this.disableFormats !== true;
61875         var sep = Roo.isGecko ? "+" : ",";
61876         var undef = function(str) {
61877             Roo.log("Property not found :"  + str);
61878             return '';
61879         };
61880         
61881         var fn = function(m, name, format, args)
61882         {
61883             //Roo.log(arguments);
61884             args = args ? args.replace(/\\'/g,"'") : args;
61885             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
61886             if (typeof(format) == 'undefined') {
61887                 format= 'htmlEncode';
61888             }
61889             if (format == 'raw' ) {
61890                 format = false;
61891             }
61892             
61893             if(name.substr(0, 4) == 'xtpl'){
61894                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
61895             }
61896             
61897             // build an array of options to determine if value is undefined..
61898             
61899             // basically get 'xxxx.yyyy' then do
61900             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
61901             //    (function () { Roo.log("Property not found"); return ''; })() :
61902             //    ......
61903             
61904             var udef_ar = [];
61905             var lookfor = '';
61906             Roo.each(name.split('.'), function(st) {
61907                 lookfor += (lookfor.length ? '.': '') + st;
61908                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
61909             });
61910             
61911             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
61912             
61913             
61914             if(format && useF){
61915                 
61916                 args = args ? ',' + args : "";
61917                  
61918                 if(format.substr(0, 5) != "this."){
61919                     format = "fm." + format + '(';
61920                 }else{
61921                     format = 'this.call("'+ format.substr(5) + '", ';
61922                     args = ", values";
61923                 }
61924                 
61925                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
61926             }
61927              
61928             if (args.length) {
61929                 // called with xxyx.yuu:(test,test)
61930                 // change to ()
61931                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
61932             }
61933             // raw.. - :raw modifier..
61934             return "'"+ sep + udef_st  + name + ")"+sep+"'";
61935             
61936         };
61937         var body;
61938         // branched to use + in gecko and [].join() in others
61939         if(Roo.isGecko){
61940             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
61941                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
61942                     "';};};";
61943         }else{
61944             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
61945             body.push(tpl.body.replace(/(\r\n|\n)/g,
61946                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
61947             body.push("'].join('');};};");
61948             body = body.join('');
61949         }
61950         
61951         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
61952        
61953         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
61954         eval(body);
61955         
61956         return this;
61957     },
61958
61959     applyTemplate : function(values){
61960         return this.master.compiled.call(this, values, {});
61961         //var s = this.subs;
61962     },
61963
61964     apply : function(){
61965         return this.applyTemplate.apply(this, arguments);
61966     }
61967
61968  });
61969
61970 Roo.XTemplate.from = function(el){
61971     el = Roo.getDom(el);
61972     return new Roo.XTemplate(el.value || el.innerHTML);
61973 };