roojs-all.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @static
23  */
24 var Roo = {}; 
25 /**
26  * Copies all the properties of config to obj.
27  * @param {Object} obj The receiver of the properties
28  * @param {Object} config The source of the properties
29  * @param {Object} defaults A different object that will also be applied for default values
30  * @return {Object} returns obj
31  * @member Roo apply
32  */
33
34  
35 Roo.apply = function(o, c, defaults){
36     if(defaults){
37         // no "this" reference for friendly out of scope calls
38         Roo.apply(o, defaults);
39     }
40     if(o && c && typeof c == 'object'){
41         for(var p in c){
42             o[p] = c[p];
43         }
44     }
45     return o;
46 };
47
48
49 (function(){
50     var idSeed = 0;
51     var ua = navigator.userAgent.toLowerCase();
52
53     var isStrict = document.compatMode == "CSS1Compat",
54         isOpera = ua.indexOf("opera") > -1,
55         isSafari = (/webkit|khtml/).test(ua),
56         isFirefox = ua.indexOf("firefox") > -1,
57         isIE = ua.indexOf("msie") > -1,
58         isIE7 = ua.indexOf("msie 7") > -1,
59         isIE11 = /trident.*rv\:11\./.test(ua),
60         isEdge = ua.indexOf("edge") > -1,
61         isGecko = !isSafari && ua.indexOf("gecko") > -1,
62         isBorderBox = isIE && !isStrict,
63         isWindows = (ua.indexOf("windows") != -1 || ua.indexOf("win32") != -1),
64         isMac = (ua.indexOf("macintosh") != -1 || ua.indexOf("mac os x") != -1),
65         isLinux = (ua.indexOf("linux") != -1),
66         isSecure = window.location.href.toLowerCase().indexOf("https") === 0,
67         isIOS = /iphone|ipad/.test(ua),
68         isAndroid = /android/.test(ua),
69         isTouch =  (function() {
70             try {
71                 if (ua.indexOf('chrome') != -1 && ua.indexOf('android') == -1) {
72                     window.addEventListener('touchstart', function __set_has_touch__ () {
73                         Roo.isTouch = true;
74                         window.removeEventListener('touchstart', __set_has_touch__);
75                     });
76                     return false; // no touch on chrome!?
77                 }
78                 document.createEvent("TouchEvent");  
79                 return true;  
80             } catch (e) {  
81                 return false;  
82             } 
83             
84         })();
85     // remove css image flicker
86         if(isIE && !isIE7){
87         try{
88             document.execCommand("BackgroundImageCache", false, true);
89         }catch(e){}
90     }
91     
92     Roo.apply(Roo, {
93         /**
94          * True if the browser is in strict mode
95          * @type Boolean
96          */
97         isStrict : isStrict,
98         /**
99          * True if the page is running over SSL
100          * @type Boolean
101          */
102         isSecure : isSecure,
103         /**
104          * True when the document is fully initialized and ready for action
105          * @type Boolean
106          */
107         isReady : false,
108         /**
109          * Turn on debugging output (currently only the factory uses this)
110          * @type Boolean
111          */
112         
113         debug: false,
114
115         /**
116          * True to automatically uncache orphaned Roo.Elements periodically (defaults to true)
117          * @type Boolean
118          */
119         enableGarbageCollector : true,
120
121         /**
122          * True to automatically purge event listeners after uncaching an element (defaults to false).
123          * Note: this only happens if enableGarbageCollector is true.
124          * @type Boolean
125          */
126         enableListenerCollection:false,
127
128         /**
129          * URL to a blank file used by Roo when in secure mode for iframe src and onReady src to prevent
130          * the IE insecure content warning (defaults to javascript:false).
131          * @type String
132          */
133         SSL_SECURE_URL : "javascript:false",
134
135         /**
136          * URL to a 1x1 transparent gif image used by Roo to create inline icons with CSS background images. (Defaults to
137          * "http://Roojs.com/s.gif" and you should change this to a URL on your server).
138          * @type String
139          */
140         BLANK_IMAGE_URL : "http:/"+"/localhost/s.gif",
141
142         emptyFn : function(){},
143         
144         /**
145          * Copies all the properties of config to obj if they don't already exist.
146          * @param {Object} obj The receiver of the properties
147          * @param {Object} config The source of the properties
148          * @return {Object} returns obj
149          */
150         applyIf : function(o, c){
151             if(o && c){
152                 for(var p in c){
153                     if(typeof o[p] == "undefined"){ o[p] = c[p]; }
154                 }
155             }
156             return o;
157         },
158
159         /**
160          * Applies event listeners to elements by selectors when the document is ready.
161          * The event name is specified with an @ suffix.
162 <pre><code>
163 Roo.addBehaviors({
164    // add a listener for click on all anchors in element with id foo
165    '#foo a@click' : function(e, t){
166        // do something
167    },
168
169    // add the same listener to multiple selectors (separated by comma BEFORE the @)
170    '#foo a, #bar span.some-class@mouseover' : function(){
171        // do something
172    }
173 });
174 </code></pre>
175          * @param {Object} obj The list of behaviors to apply
176          */
177         addBehaviors : function(o){
178             if(!Roo.isReady){
179                 Roo.onReady(function(){
180                     Roo.addBehaviors(o);
181                 });
182                 return;
183             }
184             var cache = {}; // simple cache for applying multiple behaviors to same selector does query multiple times
185             for(var b in o){
186                 var parts = b.split('@');
187                 if(parts[1]){ // for Object prototype breakers
188                     var s = parts[0];
189                     if(!cache[s]){
190                         cache[s] = Roo.select(s);
191                     }
192                     cache[s].on(parts[1], o[b]);
193                 }
194             }
195             cache = null;
196         },
197
198         /**
199          * Generates unique ids. If the element already has an id, it is unchanged
200          * @param {String/HTMLElement/Element} el (optional) The element to generate an id for
201          * @param {String} prefix (optional) Id prefix (defaults "Roo-gen")
202          * @return {String} The generated Id.
203          */
204         id : function(el, prefix){
205             prefix = prefix || "roo-gen";
206             el = Roo.getDom(el);
207             var id = prefix + (++idSeed);
208             return el ? (el.id ? el.id : (el.id = id)) : id;
209         },
210          
211        
212         /**
213          * Extends one class with another class and optionally overrides members with the passed literal. This class
214          * also adds the function "override()" to the class that can be used to override
215          * members on an instance.
216          * @param {Object} subclass The class inheriting the functionality
217          * @param {Object} superclass The class being extended
218          * @param {Object} overrides (optional) A literal with members
219          * @method extend
220          */
221         extend : function(){
222             // inline overrides
223             var io = function(o){
224                 for(var m in o){
225                     this[m] = o[m];
226                 }
227             };
228             return function(sb, sp, overrides){
229                 if(typeof sp == 'object'){ // eg. prototype, rather than function constructor..
230                     overrides = sp;
231                     sp = sb;
232                     sb = function(){sp.apply(this, arguments);};
233                 }
234                 var F = function(){}, sbp, spp = sp.prototype;
235                 F.prototype = spp;
236                 sbp = sb.prototype = new F();
237                 sbp.constructor=sb;
238                 sb.superclass=spp;
239                 
240                 if(spp.constructor == Object.prototype.constructor){
241                     spp.constructor=sp;
242                    
243                 }
244                 
245                 sb.override = function(o){
246                     Roo.override(sb, o);
247                 };
248                 sbp.override = io;
249                 Roo.override(sb, overrides);
250                 return sb;
251             };
252         }(),
253
254         /**
255          * Adds a list of functions to the prototype of an existing class, overwriting any existing methods with the same name.
256          * Usage:<pre><code>
257 Roo.override(MyClass, {
258     newMethod1: function(){
259         // etc.
260     },
261     newMethod2: function(foo){
262         // etc.
263     }
264 });
265  </code></pre>
266          * @param {Object} origclass The class to override
267          * @param {Object} overrides The list of functions to add to origClass.  This should be specified as an object literal
268          * containing one or more methods.
269          * @method override
270          */
271         override : function(origclass, overrides){
272             if(overrides){
273                 var p = origclass.prototype;
274                 for(var method in overrides){
275                     p[method] = overrides[method];
276                 }
277             }
278         },
279         /**
280          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
281          * <pre><code>
282 Roo.namespace('Company', 'Company.data');
283 Company.Widget = function() { ... }
284 Company.data.CustomStore = function(config) { ... }
285 </code></pre>
286          * @param {String} namespace1
287          * @param {String} namespace2
288          * @param {String} etc
289          * @method namespace
290          */
291         namespace : function(){
292             var a=arguments, o=null, i, j, d, rt;
293             for (i=0; i<a.length; ++i) {
294                 d=a[i].split(".");
295                 rt = d[0];
296                 /** eval:var:o */
297                 eval('if (typeof ' + rt + ' == "undefined"){' + rt + ' = {};} o = ' + rt + ';');
298                 for (j=1; j<d.length; ++j) {
299                     o[d[j]]=o[d[j]] || {};
300                     o=o[d[j]];
301                 }
302             }
303         },
304         /**
305          * Creates namespaces to be used for scoping variables and classes so that they are not global.  Usage:
306          * <pre><code>
307 Roo.factory({ xns: Roo.data, xtype : 'Store', .....});
308 Roo.factory(conf, Roo.data);
309 </code></pre>
310          * @param {String} classname
311          * @param {String} namespace (optional)
312          * @method factory
313          */
314          
315         factory : function(c, ns)
316         {
317             // no xtype, no ns or c.xns - or forced off by c.xns
318             if (!c.xtype   || (!ns && !c.xns) ||  (c.xns === false)) { // not enough info...
319                 return c;
320             }
321             ns = c.xns ? c.xns : ns; // if c.xns is set, then use that..
322             if (c.constructor == ns[c.xtype]) {// already created...
323                 return c;
324             }
325             if (ns[c.xtype]) {
326                 if (Roo.debug) { Roo.log("Roo.Factory(" + c.xtype + ")"); }
327                 var ret = new ns[c.xtype](c);
328                 ret.xns = false;
329                 return ret;
330             }
331             c.xns = false; // prevent recursion..
332             return c;
333         },
334          /**
335          * Logs to console if it can.
336          *
337          * @param {String|Object} string
338          * @method log
339          */
340         log : function(s)
341         {
342             if ((typeof(console) == 'undefined') || (typeof(console.log) == 'undefined')) {
343                 return; // alerT?
344             }
345             
346             console.log(s);
347         },
348         /**
349          * Takes an object and converts it to an encoded URL. e.g. Roo.urlEncode({foo: 1, bar: 2}); would return "foo=1&bar=2".  Optionally, property values can be arrays, instead of keys and the resulting string that's returned will contain a name/value pair for each array value.
350          * @param {Object} o
351          * @return {String}
352          */
353         urlEncode : function(o){
354             if(!o){
355                 return "";
356             }
357             var buf = [];
358             for(var key in o){
359                 var ov = o[key], k = Roo.encodeURIComponent(key);
360                 var type = typeof ov;
361                 if(type == 'undefined'){
362                     buf.push(k, "=&");
363                 }else if(type != "function" && type != "object"){
364                     buf.push(k, "=", Roo.encodeURIComponent(ov), "&");
365                 }else if(ov instanceof Array){
366                     if (ov.length) {
367                             for(var i = 0, len = ov.length; i < len; i++) {
368                                 buf.push(k, "=", Roo.encodeURIComponent(ov[i] === undefined ? '' : ov[i]), "&");
369                             }
370                         } else {
371                             buf.push(k, "=&");
372                         }
373                 }
374             }
375             buf.pop();
376             return buf.join("");
377         },
378          /**
379          * Safe version of encodeURIComponent
380          * @param {String} data 
381          * @return {String} 
382          */
383         
384         encodeURIComponent : function (data)
385         {
386             try {
387                 return encodeURIComponent(data);
388             } catch(e) {} // should be an uri encode error.
389             
390             if (data == '' || data == null){
391                return '';
392             }
393             // http://stackoverflow.com/questions/2596483/unicode-and-uri-encoding-decoding-and-escaping-in-javascript
394             function nibble_to_hex(nibble){
395                 var chars = '0123456789ABCDEF';
396                 return chars.charAt(nibble);
397             }
398             data = data.toString();
399             var buffer = '';
400             for(var i=0; i<data.length; i++){
401                 var c = data.charCodeAt(i);
402                 var bs = new Array();
403                 if (c > 0x10000){
404                         // 4 bytes
405                     bs[0] = 0xF0 | ((c & 0x1C0000) >>> 18);
406                     bs[1] = 0x80 | ((c & 0x3F000) >>> 12);
407                     bs[2] = 0x80 | ((c & 0xFC0) >>> 6);
408                     bs[3] = 0x80 | (c & 0x3F);
409                 }else if (c > 0x800){
410                          // 3 bytes
411                     bs[0] = 0xE0 | ((c & 0xF000) >>> 12);
412                     bs[1] = 0x80 | ((c & 0xFC0) >>> 6);
413                     bs[2] = 0x80 | (c & 0x3F);
414                 }else if (c > 0x80){
415                        // 2 bytes
416                     bs[0] = 0xC0 | ((c & 0x7C0) >>> 6);
417                     bs[1] = 0x80 | (c & 0x3F);
418                 }else{
419                         // 1 byte
420                     bs[0] = c;
421                 }
422                 for(var j=0; j<bs.length; j++){
423                     var b = bs[j];
424                     var hex = nibble_to_hex((b & 0xF0) >>> 4) 
425                             + nibble_to_hex(b &0x0F);
426                     buffer += '%'+hex;
427                }
428             }
429             return buffer;    
430              
431         },
432
433         /**
434          * Takes an encoded URL and and converts it to an object. e.g. Roo.urlDecode("foo=1&bar=2"); would return {foo: 1, bar: 2} or Roo.urlDecode("foo=1&bar=2&bar=3&bar=4", true); would return {foo: 1, bar: [2, 3, 4]}.
435          * @param {String} string
436          * @param {Boolean} overwrite (optional) Items of the same name will overwrite previous values instead of creating an an array (Defaults to false).
437          * @return {Object} A literal with members
438          */
439         urlDecode : function(string, overwrite){
440             if(!string || !string.length){
441                 return {};
442             }
443             var obj = {};
444             var pairs = string.split('&');
445             var pair, name, value;
446             for(var i = 0, len = pairs.length; i < len; i++){
447                 pair = pairs[i].split('=');
448                 name = decodeURIComponent(pair[0]);
449                 value = decodeURIComponent(pair[1]);
450                 if(overwrite !== true){
451                     if(typeof obj[name] == "undefined"){
452                         obj[name] = value;
453                     }else if(typeof obj[name] == "string"){
454                         obj[name] = [obj[name]];
455                         obj[name].push(value);
456                     }else{
457                         obj[name].push(value);
458                     }
459                 }else{
460                     obj[name] = value;
461                 }
462             }
463             return obj;
464         },
465
466         /**
467          * Iterates an array calling the passed function with each item, stopping if your function returns false. If the
468          * passed array is not really an array, your function is called once with it.
469          * The supplied function is called with (Object item, Number index, Array allItems).
470          * @param {Array/NodeList/Mixed} array
471          * @param {Function} fn
472          * @param {Object} scope
473          */
474         each : function(array, fn, scope){
475             if(typeof array.length == "undefined" || typeof array == "string"){
476                 array = [array];
477             }
478             for(var i = 0, len = array.length; i < len; i++){
479                 if(fn.call(scope || array[i], array[i], i, array) === false){ return i; };
480             }
481         },
482
483         // deprecated
484         combine : function(){
485             var as = arguments, l = as.length, r = [];
486             for(var i = 0; i < l; i++){
487                 var a = as[i];
488                 if(a instanceof Array){
489                     r = r.concat(a);
490                 }else if(a.length !== undefined && !a.substr){
491                     r = r.concat(Array.prototype.slice.call(a, 0));
492                 }else{
493                     r.push(a);
494                 }
495             }
496             return r;
497         },
498
499         /**
500          * Escapes the passed string for use in a regular expression
501          * @param {String} str
502          * @return {String}
503          */
504         escapeRe : function(s) {
505             return s.replace(/([.*+?^${}()|[\]\/\\])/g, "\\$1");
506         },
507
508         // internal
509         callback : function(cb, scope, args, delay){
510             if(typeof cb == "function"){
511                 if(delay){
512                     cb.defer(delay, scope, args || []);
513                 }else{
514                     cb.apply(scope, args || []);
515                 }
516             }
517         },
518
519         /**
520          * Return the dom node for the passed string (id), dom node, or Roo.Element
521          * @param {String/HTMLElement/Roo.Element} el
522          * @return HTMLElement
523          */
524         getDom : function(el){
525             if(!el){
526                 return null;
527             }
528             return el.dom ? el.dom : (typeof el == 'string' ? document.getElementById(el) : el);
529         },
530
531         /**
532         * Shorthand for {@link Roo.ComponentMgr#get}
533         * @param {String} id
534         * @return Roo.Component
535         */
536         getCmp : function(id){
537             return Roo.ComponentMgr.get(id);
538         },
539          
540         num : function(v, defaultValue){
541             if(typeof v != 'number'){
542                 return defaultValue;
543             }
544             return v;
545         },
546
547         destroy : function(){
548             for(var i = 0, a = arguments, len = a.length; i < len; i++) {
549                 var as = a[i];
550                 if(as){
551                     if(as.dom){
552                         as.removeAllListeners();
553                         as.remove();
554                         continue;
555                     }
556                     if(typeof as.purgeListeners == 'function'){
557                         as.purgeListeners();
558                     }
559                     if(typeof as.destroy == 'function'){
560                         as.destroy();
561                     }
562                 }
563             }
564         },
565
566         // inpired by a similar function in mootools library
567         /**
568          * Returns the type of object that is passed in. If the object passed in is null or undefined it
569          * return false otherwise it returns one of the following values:<ul>
570          * <li><b>string</b>: If the object passed is a string</li>
571          * <li><b>number</b>: If the object passed is a number</li>
572          * <li><b>boolean</b>: If the object passed is a boolean value</li>
573          * <li><b>function</b>: If the object passed is a function reference</li>
574          * <li><b>object</b>: If the object passed is an object</li>
575          * <li><b>array</b>: If the object passed is an array</li>
576          * <li><b>regexp</b>: If the object passed is a regular expression</li>
577          * <li><b>element</b>: If the object passed is a DOM Element</li>
578          * <li><b>nodelist</b>: If the object passed is a DOM NodeList</li>
579          * <li><b>textnode</b>: If the object passed is a DOM text node and contains something other than whitespace</li>
580          * <li><b>whitespace</b>: If the object passed is a DOM text node and contains only whitespace</li>
581          * @param {Mixed} object
582          * @return {String}
583          */
584         type : function(o){
585             if(o === undefined || o === null){
586                 return false;
587             }
588             if(o.htmlElement){
589                 return 'element';
590             }
591             var t = typeof o;
592             if(t == 'object' && o.nodeName) {
593                 switch(o.nodeType) {
594                     case 1: return 'element';
595                     case 3: return (/\S/).test(o.nodeValue) ? 'textnode' : 'whitespace';
596                 }
597             }
598             if(t == 'object' || t == 'function') {
599                 switch(o.constructor) {
600                     case Array: return 'array';
601                     case RegExp: return 'regexp';
602                 }
603                 if(typeof o.length == 'number' && typeof o.item == 'function') {
604                     return 'nodelist';
605                 }
606             }
607             return t;
608         },
609
610         /**
611          * Returns true if the passed value is null, undefined or an empty string (optional).
612          * @param {Mixed} value The value to test
613          * @param {Boolean} allowBlank (optional) Pass true if an empty string is not considered empty
614          * @return {Boolean}
615          */
616         isEmpty : function(v, allowBlank){
617             return v === null || v === undefined || (!allowBlank ? v === '' : false);
618         },
619         
620         /** @type Boolean */
621         isOpera : isOpera,
622         /** @type Boolean */
623         isSafari : isSafari,
624         /** @type Boolean */
625         isFirefox : isFirefox,
626         /** @type Boolean */
627         isIE : isIE,
628         /** @type Boolean */
629         isIE7 : isIE7,
630         /** @type Boolean */
631         isIE11 : isIE11,
632         /** @type Boolean */
633         isEdge : isEdge,
634         /** @type Boolean */
635         isGecko : isGecko,
636         /** @type Boolean */
637         isBorderBox : isBorderBox,
638         /** @type Boolean */
639         isWindows : isWindows,
640         /** @type Boolean */
641         isLinux : isLinux,
642         /** @type Boolean */
643         isMac : isMac,
644         /** @type Boolean */
645         isIOS : isIOS,
646         /** @type Boolean */
647         isAndroid : isAndroid,
648         /** @type Boolean */
649         isTouch : isTouch,
650
651         /**
652          * By default, Ext intelligently decides whether floating elements should be shimmed. If you are using flash,
653          * you may want to set this to true.
654          * @type Boolean
655          */
656         useShims : ((isIE && !isIE7) || (isGecko && isMac)),
657         
658         
659                 
660         /**
661          * Selects a single element as a Roo Element
662          * This is about as close as you can get to jQuery's $('do crazy stuff')
663          * @param {String} selector The selector/xpath query
664          * @param {Node} root (optional) The start of the query (defaults to document).
665          * @return {Roo.Element}
666          */
667         selectNode : function(selector, root) 
668         {
669             var node = Roo.DomQuery.selectNode(selector,root);
670             return node ? Roo.get(node) : new Roo.Element(false);
671         },
672                 /**
673                  * Find the current bootstrap width Grid size
674                  * Note xs is the default for smaller.. - this is currently used by grids to render correct columns
675                  * @returns {String} (xs|sm|md|lg|xl)
676                  */
677                 
678                 getGridSize : function()
679                 {
680                         var w = Roo.lib.Dom.getViewWidth();
681                         switch(true) {
682                                 case w > 1200:
683                                         return 'xl';
684                                 case w > 992:
685                                         return 'lg';
686                                 case w > 768:
687                                         return 'md';
688                                 case w > 576:
689                                         return 'sm';
690                                 default:
691                                         return 'xs'
692                         }
693                         
694                 }
695         
696     });
697
698
699 })();
700
701 Roo.namespace("Roo", "Roo.util", "Roo.grid", "Roo.dd", "Roo.tree", "Roo.data",
702                 "Roo.form", "Roo.menu", "Roo.state", "Roo.lib", "Roo.layout",
703                 "Roo.app", "Roo.ux",
704                 "Roo.bootstrap",
705                 "Roo.bootstrap.dash");
706 /*
707  * Based on:
708  * Ext JS Library 1.1.1
709  * Copyright(c) 2006-2007, Ext JS, LLC.
710  *
711  * Originally Released Under LGPL - original licence link has changed is not relivant.
712  *
713  * Fork - LGPL
714  * <script type="text/javascript">
715  */
716
717 (function() {    
718     // wrappedn so fnCleanup is not in global scope...
719     if(Roo.isIE) {
720         function fnCleanUp() {
721             var p = Function.prototype;
722             delete p.createSequence;
723             delete p.defer;
724             delete p.createDelegate;
725             delete p.createCallback;
726             delete p.createInterceptor;
727
728             window.detachEvent("onunload", fnCleanUp);
729         }
730         window.attachEvent("onunload", fnCleanUp);
731     }
732 })();
733
734
735 /**
736  * @class Function
737  * These functions are available on every Function object (any JavaScript function).
738  */
739 Roo.apply(Function.prototype, {
740      /**
741      * Creates a callback that passes arguments[0], arguments[1], arguments[2], ...
742      * Call directly on any function. Example: <code>myFunction.createCallback(myarg, myarg2)</code>
743      * Will create a function that is bound to those 2 args.
744      * @return {Function} The new function
745     */
746     createCallback : function(/*args...*/){
747         // make args available, in function below
748         var args = arguments;
749         var method = this;
750         return function() {
751             return method.apply(window, args);
752         };
753     },
754
755     /**
756      * Creates a delegate (callback) that sets the scope to obj.
757      * Call directly on any function. Example: <code>this.myFunction.createDelegate(this)</code>
758      * Will create a function that is automatically scoped to this.
759      * @param {Object} obj (optional) The object for which the scope is set
760      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
761      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
762      *                                             if a number the args are inserted at the specified position
763      * @return {Function} The new function
764      */
765     createDelegate : function(obj, args, appendArgs){
766         var method = this;
767         return function() {
768             var callArgs = args || arguments;
769             if(appendArgs === true){
770                 callArgs = Array.prototype.slice.call(arguments, 0);
771                 callArgs = callArgs.concat(args);
772             }else if(typeof appendArgs == "number"){
773                 callArgs = Array.prototype.slice.call(arguments, 0); // copy arguments first
774                 var applyArgs = [appendArgs, 0].concat(args); // create method call params
775                 Array.prototype.splice.apply(callArgs, applyArgs); // splice them in
776             }
777             return method.apply(obj || window, callArgs);
778         };
779     },
780
781     /**
782      * Calls this function after the number of millseconds specified.
783      * @param {Number} millis The number of milliseconds for the setTimeout call (if 0 the function is executed immediately)
784      * @param {Object} obj (optional) The object for which the scope is set
785      * @param {Array} args (optional) Overrides arguments for the call. (Defaults to the arguments passed by the caller)
786      * @param {Boolean/Number} appendArgs (optional) if True args are appended to call args instead of overriding,
787      *                                             if a number the args are inserted at the specified position
788      * @return {Number} The timeout id that can be used with clearTimeout
789      */
790     defer : function(millis, obj, args, appendArgs){
791         var fn = this.createDelegate(obj, args, appendArgs);
792         if(millis){
793             return setTimeout(fn, millis);
794         }
795         fn();
796         return 0;
797     },
798     /**
799      * Create a combined function call sequence of the original function + the passed function.
800      * The resulting function returns the results of the original function.
801      * The passed fcn is called with the parameters of the original function
802      * @param {Function} fcn The function to sequence
803      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
804      * @return {Function} The new function
805      */
806     createSequence : function(fcn, scope){
807         if(typeof fcn != "function"){
808             return this;
809         }
810         var method = this;
811         return function() {
812             var retval = method.apply(this || window, arguments);
813             fcn.apply(scope || this || window, arguments);
814             return retval;
815         };
816     },
817
818     /**
819      * Creates an interceptor function. The passed fcn is called before the original one. If it returns false, the original one is not called.
820      * The resulting function returns the results of the original function.
821      * The passed fcn is called with the parameters of the original function.
822      * @addon
823      * @param {Function} fcn The function to call before the original
824      * @param {Object} scope (optional) The scope of the passed fcn (Defaults to scope of original function or window)
825      * @return {Function} The new function
826      */
827     createInterceptor : function(fcn, scope){
828         if(typeof fcn != "function"){
829             return this;
830         }
831         var method = this;
832         return function() {
833             fcn.target = this;
834             fcn.method = method;
835             if(fcn.apply(scope || this || window, arguments) === false){
836                 return;
837             }
838             return method.apply(this || window, arguments);
839         };
840     }
841 });
842 /*
843  * Based on:
844  * Ext JS Library 1.1.1
845  * Copyright(c) 2006-2007, Ext JS, LLC.
846  *
847  * Originally Released Under LGPL - original licence link has changed is not relivant.
848  *
849  * Fork - LGPL
850  * <script type="text/javascript">
851  */
852
853 Roo.applyIf(String, {
854     
855     /** @scope String */
856     
857     /**
858      * Escapes the passed string for ' and \
859      * @param {String} string The string to escape
860      * @return {String} The escaped string
861      * @static
862      */
863     escape : function(string) {
864         return string.replace(/('|\\)/g, "\\$1");
865     },
866
867     /**
868      * Pads the left side of a string with a specified character.  This is especially useful
869      * for normalizing number and date strings.  Example usage:
870      * <pre><code>
871 var s = String.leftPad('123', 5, '0');
872 // s now contains the string: '00123'
873 </code></pre>
874      * @param {String} string The original string
875      * @param {Number} size The total length of the output string
876      * @param {String} char (optional) The character with which to pad the original string (defaults to empty string " ")
877      * @return {String} The padded string
878      * @static
879      */
880     leftPad : function (val, size, ch) {
881         var result = new String(val);
882         if(ch === null || ch === undefined || ch === '') {
883             ch = " ";
884         }
885         while (result.length < size) {
886             result = ch + result;
887         }
888         return result;
889     },
890
891     /**
892      * Allows you to define a tokenized string and pass an arbitrary number of arguments to replace the tokens.  Each
893      * token must be unique, and must increment in the format {0}, {1}, etc.  Example usage:
894      * <pre><code>
895 var cls = 'my-class', text = 'Some text';
896 var s = String.format('<div class="{0}">{1}</div>', cls, text);
897 // s now contains the string: '<div class="my-class">Some text</div>'
898 </code></pre>
899      * @param {String} string The tokenized string to be formatted
900      * @param {String} value1 The value to replace token {0}
901      * @param {String} value2 Etc...
902      * @return {String} The formatted string
903      * @static
904      */
905     format : function(format){
906         var args = Array.prototype.slice.call(arguments, 1);
907         return format.replace(/\{(\d+)\}/g, function(m, i){
908             return Roo.util.Format.htmlEncode(args[i]);
909         });
910     }
911   
912     
913 });
914
915 /**
916  * Utility function that allows you to easily switch a string between two alternating values.  The passed value
917  * is compared to the current string, and if they are equal, the other value that was passed in is returned.  If
918  * they are already different, the first value passed in is returned.  Note that this method returns the new value
919  * but does not change the current string.
920  * <pre><code>
921 // alternate sort directions
922 sort = sort.toggle('ASC', 'DESC');
923
924 // instead of conditional logic:
925 sort = (sort == 'ASC' ? 'DESC' : 'ASC');
926 </code></pre>
927  * @param {String} value The value to compare to the current string
928  * @param {String} other The new value to use if the string already equals the first value passed in
929  * @return {String} The new value
930  */
931  
932 String.prototype.toggle = function(value, other){
933     return this == value ? other : value;
934 };
935
936
937 /**
938   * Remove invalid unicode characters from a string 
939   *
940   * @return {String} The clean string
941   */
942 String.prototype.unicodeClean = function () {
943     return this.replace(/[\s\S]/g,
944         function(character) {
945             if (character.charCodeAt()< 256) {
946               return character;
947            }
948            try {
949                 encodeURIComponent(character);
950            } catch(e) { 
951               return '';
952            }
953            return character;
954         }
955     );
956 };
957   
958 /*
959  * Based on:
960  * Ext JS Library 1.1.1
961  * Copyright(c) 2006-2007, Ext JS, LLC.
962  *
963  * Originally Released Under LGPL - original licence link has changed is not relivant.
964  *
965  * Fork - LGPL
966  * <script type="text/javascript">
967  */
968
969  /**
970  * @class Number
971  */
972 Roo.applyIf(Number.prototype, {
973     /**
974      * Checks whether or not the current number is within a desired range.  If the number is already within the
975      * range it is returned, otherwise the min or max value is returned depending on which side of the range is
976      * exceeded.  Note that this method returns the constrained value but does not change the current number.
977      * @param {Number} min The minimum number in the range
978      * @param {Number} max The maximum number in the range
979      * @return {Number} The constrained value if outside the range, otherwise the current value
980      */
981     constrain : function(min, max){
982         return Math.min(Math.max(this, min), max);
983     }
984 });/*
985  * Based on:
986  * Ext JS Library 1.1.1
987  * Copyright(c) 2006-2007, Ext JS, LLC.
988  *
989  * Originally Released Under LGPL - original licence link has changed is not relivant.
990  *
991  * Fork - LGPL
992  * <script type="text/javascript">
993  */
994  /**
995  * @class Array
996  */
997 Roo.applyIf(Array.prototype, {
998     /**
999      * 
1000      * Checks whether or not the specified object exists in the array.
1001      * @param {Object} o The object to check for
1002      * @return {Number} The index of o in the array (or -1 if it is not found)
1003      */
1004     indexOf : function(o){
1005        for (var i = 0, len = this.length; i < len; i++){
1006               if(this[i] == o) { return i; }
1007        }
1008            return -1;
1009     },
1010
1011     /**
1012      * Removes the specified object from the array.  If the object is not found nothing happens.
1013      * @param {Object} o The object to remove
1014      */
1015     remove : function(o){
1016        var index = this.indexOf(o);
1017        if(index != -1){
1018            this.splice(index, 1);
1019        }
1020     },
1021     /**
1022      * Map (JS 1.6 compatibility)
1023      * @param {Function} function  to call
1024      */
1025     map : function(fun )
1026     {
1027         var len = this.length >>> 0;
1028         if (typeof fun != "function") {
1029             throw new TypeError();
1030         }
1031         var res = new Array(len);
1032         var thisp = arguments[1];
1033         for (var i = 0; i < len; i++)
1034         {
1035             if (i in this) {
1036                 res[i] = fun.call(thisp, this[i], i, this);
1037             }
1038         }
1039
1040         return res;
1041     },
1042     /**
1043      * equals
1044      * @param {Array} o The array to compare to
1045      * @returns {Boolean} true if the same
1046      */
1047     equals : function(b)
1048     {
1049             // https://stackoverflow.com/questions/3115982/how-to-check-if-two-arrays-are-equal-with-javascript
1050         if (this === b) {
1051             return true;
1052         }
1053         if (b == null) {
1054             return false;
1055         }
1056         if (this.length !== b.length) {
1057             return false;
1058         }
1059           
1060         // sort?? a.sort().equals(b.sort());
1061           
1062         for (var i = 0; i < this.length; ++i) {
1063             if (this[i] !== b[i]) {
1064             return false;
1065             }
1066         }
1067         return true;
1068     } 
1069     
1070     
1071     
1072     
1073 });
1074
1075 Roo.applyIf(Array, {
1076  /**
1077      * from
1078      * @static
1079      * @param {Array} o Or Array like object (eg. nodelist)
1080      * @returns {Array} 
1081      */
1082     from : function(o)
1083     {
1084         var ret= [];
1085     
1086         for (var i =0; i < o.length; i++) { 
1087             ret[i] = o[i];
1088         }
1089         return ret;
1090       
1091     }
1092 });
1093 /*
1094  * Based on:
1095  * Ext JS Library 1.1.1
1096  * Copyright(c) 2006-2007, Ext JS, LLC.
1097  *
1098  * Originally Released Under LGPL - original licence link has changed is not relivant.
1099  *
1100  * Fork - LGPL
1101  * <script type="text/javascript">
1102  */
1103
1104 /**
1105  * @class Date
1106  *
1107  * The date parsing and format syntax is a subset of
1108  * <a href="http://www.php.net/date">PHP's date() function</a>, and the formats that are
1109  * supported will provide results equivalent to their PHP versions.
1110  *
1111  * Following is the list of all currently supported formats:
1112  *<pre>
1113 Sample date:
1114 'Wed Jan 10 2007 15:05:01 GMT-0600 (Central Standard Time)'
1115
1116 Format  Output      Description
1117 ------  ----------  --------------------------------------------------------------
1118   d      10         Day of the month, 2 digits with leading zeros
1119   D      Wed        A textual representation of a day, three letters
1120   j      10         Day of the month without leading zeros
1121   l      Wednesday  A full textual representation of the day of the week
1122   S      th         English ordinal day of month suffix, 2 chars (use with j)
1123   w      3          Numeric representation of the day of the week
1124   z      9          The julian date, or day of the year (0-365)
1125   W      01         ISO-8601 2-digit week number of year, weeks starting on Monday (00-52)
1126   F      January    A full textual representation of the month
1127   m      01         Numeric representation of a month, with leading zeros
1128   M      Jan        Month name abbreviation, three letters
1129   n      1          Numeric representation of a month, without leading zeros
1130   t      31         Number of days in the given month
1131   L      0          Whether it's a leap year (1 if it is a leap year, else 0)
1132   Y      2007       A full numeric representation of a year, 4 digits
1133   y      07         A two digit representation of a year
1134   a      pm         Lowercase Ante meridiem and Post meridiem
1135   A      PM         Uppercase Ante meridiem and Post meridiem
1136   g      3          12-hour format of an hour without leading zeros
1137   G      15         24-hour format of an hour without leading zeros
1138   h      03         12-hour format of an hour with leading zeros
1139   H      15         24-hour format of an hour with leading zeros
1140   i      05         Minutes with leading zeros
1141   s      01         Seconds, with leading zeros
1142   O      -0600      Difference to Greenwich time (GMT) in hours (Allows +08, without minutes)
1143   P      -06:00     Difference to Greenwich time (GMT) with colon between hours and minutes
1144   T      CST        Timezone setting of the machine running the code
1145   Z      -21600     Timezone offset in seconds (negative if west of UTC, positive if east)
1146 </pre>
1147  *
1148  * Example usage (note that you must escape format specifiers with '\\' to render them as character literals):
1149  * <pre><code>
1150 var dt = new Date('1/10/2007 03:05:01 PM GMT-0600');
1151 document.write(dt.format('Y-m-d'));                         //2007-01-10
1152 document.write(dt.format('F j, Y, g:i a'));                 //January 10, 2007, 3:05 pm
1153 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
1154  </code></pre>
1155  *
1156  * Here are some standard date/time patterns that you might find helpful.  They
1157  * are not part of the source of Date.js, but to use them you can simply copy this
1158  * block of code into any script that is included after Date.js and they will also become
1159  * globally available on the Date object.  Feel free to add or remove patterns as needed in your code.
1160  * <pre><code>
1161 Date.patterns = {
1162     ISO8601Long:"Y-m-d H:i:s",
1163     ISO8601Short:"Y-m-d",
1164     ShortDate: "n/j/Y",
1165     LongDate: "l, F d, Y",
1166     FullDateTime: "l, F d, Y g:i:s A",
1167     MonthDay: "F d",
1168     ShortTime: "g:i A",
1169     LongTime: "g:i:s A",
1170     SortableDateTime: "Y-m-d\\TH:i:s",
1171     UniversalSortableDateTime: "Y-m-d H:i:sO",
1172     YearMonth: "F, Y"
1173 };
1174 </code></pre>
1175  *
1176  * Example usage:
1177  * <pre><code>
1178 var dt = new Date();
1179 document.write(dt.format(Date.patterns.ShortDate));
1180  </code></pre>
1181  */
1182
1183 /*
1184  * Most of the date-formatting functions below are the excellent work of Baron Schwartz.
1185  * They generate precompiled functions from date formats instead of parsing and
1186  * processing the pattern every time you format a date.  These functions are available
1187  * on every Date object (any javascript function).
1188  *
1189  * The original article and download are here:
1190  * http://www.xaprb.com/blog/2005/12/12/javascript-closures-for-runtime-efficiency/
1191  *
1192  */
1193  
1194  
1195  // was in core
1196 /**
1197  Returns the number of milliseconds between this date and date
1198  @param {Date} date (optional) Defaults to now
1199  @return {Number} The diff in milliseconds
1200  @member Date getElapsed
1201  */
1202 Date.prototype.getElapsed = function(date) {
1203         return Math.abs((date || new Date()).getTime()-this.getTime());
1204 };
1205 // was in date file..
1206
1207
1208 // private
1209 Date.parseFunctions = {count:0};
1210 // private
1211 Date.parseRegexes = [];
1212 // private
1213 Date.formatFunctions = {count:0};
1214
1215 // private
1216 Date.prototype.dateFormat = function(format) {
1217     if (Date.formatFunctions[format] == null) {
1218         Date.createNewFormat(format);
1219     }
1220     var func = Date.formatFunctions[format];
1221     return this[func]();
1222 };
1223
1224
1225 /**
1226  * Formats a date given the supplied format string
1227  * @param {String} format The format string
1228  * @return {String} The formatted date
1229  * @method
1230  */
1231 Date.prototype.format = Date.prototype.dateFormat;
1232
1233 // private
1234 Date.createNewFormat = function(format) {
1235     var funcName = "format" + Date.formatFunctions.count++;
1236     Date.formatFunctions[format] = funcName;
1237     var code = "Date.prototype." + funcName + " = function(){return ";
1238     var special = false;
1239     var ch = '';
1240     for (var i = 0; i < format.length; ++i) {
1241         ch = format.charAt(i);
1242         if (!special && ch == "\\") {
1243             special = true;
1244         }
1245         else if (special) {
1246             special = false;
1247             code += "'" + String.escape(ch) + "' + ";
1248         }
1249         else {
1250             code += Date.getFormatCode(ch);
1251         }
1252     }
1253     /** eval:var:zzzzzzzzzzzzz */
1254     eval(code.substring(0, code.length - 3) + ";}");
1255 };
1256
1257 // private
1258 Date.getFormatCode = function(character) {
1259     switch (character) {
1260     case "d":
1261         return "String.leftPad(this.getDate(), 2, '0') + ";
1262     case "D":
1263         return "Date.dayNames[this.getDay()].substring(0, 3) + ";
1264     case "j":
1265         return "this.getDate() + ";
1266     case "l":
1267         return "Date.dayNames[this.getDay()] + ";
1268     case "S":
1269         return "this.getSuffix() + ";
1270     case "w":
1271         return "this.getDay() + ";
1272     case "z":
1273         return "this.getDayOfYear() + ";
1274     case "W":
1275         return "this.getWeekOfYear() + ";
1276     case "F":
1277         return "Date.monthNames[this.getMonth()] + ";
1278     case "m":
1279         return "String.leftPad(this.getMonth() + 1, 2, '0') + ";
1280     case "M":
1281         return "Date.monthNames[this.getMonth()].substring(0, 3) + ";
1282     case "n":
1283         return "(this.getMonth() + 1) + ";
1284     case "t":
1285         return "this.getDaysInMonth() + ";
1286     case "L":
1287         return "(this.isLeapYear() ? 1 : 0) + ";
1288     case "Y":
1289         return "this.getFullYear() + ";
1290     case "y":
1291         return "('' + this.getFullYear()).substring(2, 4) + ";
1292     case "a":
1293         return "(this.getHours() < 12 ? 'am' : 'pm') + ";
1294     case "A":
1295         return "(this.getHours() < 12 ? 'AM' : 'PM') + ";
1296     case "g":
1297         return "((this.getHours() % 12) ? this.getHours() % 12 : 12) + ";
1298     case "G":
1299         return "this.getHours() + ";
1300     case "h":
1301         return "String.leftPad((this.getHours() % 12) ? this.getHours() % 12 : 12, 2, '0') + ";
1302     case "H":
1303         return "String.leftPad(this.getHours(), 2, '0') + ";
1304     case "i":
1305         return "String.leftPad(this.getMinutes(), 2, '0') + ";
1306     case "s":
1307         return "String.leftPad(this.getSeconds(), 2, '0') + ";
1308     case "O":
1309         return "this.getGMTOffset() + ";
1310     case "P":
1311         return "this.getGMTColonOffset() + ";
1312     case "T":
1313         return "this.getTimezone() + ";
1314     case "Z":
1315         return "(this.getTimezoneOffset() * -60) + ";
1316     default:
1317         return "'" + String.escape(character) + "' + ";
1318     }
1319 };
1320
1321 /**
1322  * Parses the passed string using the specified format. Note that this function expects dates in normal calendar
1323  * format, meaning that months are 1-based (1 = January) and not zero-based like in JavaScript dates.  Any part of
1324  * the date format that is not specified will default to the current date value for that part.  Time parts can also
1325  * be specified, but default to 0.  Keep in mind that the input date string must precisely match the specified format
1326  * string or the parse operation will fail.
1327  * Example Usage:
1328 <pre><code>
1329 //dt = Fri May 25 2007 (current date)
1330 var dt = new Date();
1331
1332 //dt = Thu May 25 2006 (today's month/day in 2006)
1333 dt = Date.parseDate("2006", "Y");
1334
1335 //dt = Sun Jan 15 2006 (all date parts specified)
1336 dt = Date.parseDate("2006-1-15", "Y-m-d");
1337
1338 //dt = Sun Jan 15 2006 15:20:01 GMT-0600 (CST)
1339 dt = Date.parseDate("2006-1-15 3:20:01 PM", "Y-m-d h:i:s A" );
1340 </code></pre>
1341  * @param {String} input The unparsed date as a string
1342  * @param {String} format The format the date is in
1343  * @return {Date} The parsed date
1344  * @static
1345  */
1346 Date.parseDate = function(input, format) {
1347     if (Date.parseFunctions[format] == null) {
1348         Date.createParser(format);
1349     }
1350     var func = Date.parseFunctions[format];
1351     return Date[func](input);
1352 };
1353 /**
1354  * @private
1355  */
1356
1357 Date.createParser = function(format) {
1358     var funcName = "parse" + Date.parseFunctions.count++;
1359     var regexNum = Date.parseRegexes.length;
1360     var currentGroup = 1;
1361     Date.parseFunctions[format] = funcName;
1362
1363     var code = "Date." + funcName + " = function(input){\n"
1364         + "var y = -1, m = -1, d = -1, h = -1, i = -1, s = -1, o, z, v;\n"
1365         + "var d = new Date();\n"
1366         + "y = d.getFullYear();\n"
1367         + "m = d.getMonth();\n"
1368         + "d = d.getDate();\n"
1369         + "if (typeof(input) !== 'string') { input = input.toString(); }\n"
1370         + "var results = input.match(Date.parseRegexes[" + regexNum + "]);\n"
1371         + "if (results && results.length > 0) {";
1372     var regex = "";
1373
1374     var special = false;
1375     var ch = '';
1376     for (var i = 0; i < format.length; ++i) {
1377         ch = format.charAt(i);
1378         if (!special && ch == "\\") {
1379             special = true;
1380         }
1381         else if (special) {
1382             special = false;
1383             regex += String.escape(ch);
1384         }
1385         else {
1386             var obj = Date.formatCodeToRegex(ch, currentGroup);
1387             currentGroup += obj.g;
1388             regex += obj.s;
1389             if (obj.g && obj.c) {
1390                 code += obj.c;
1391             }
1392         }
1393     }
1394
1395     code += "if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0 && s >= 0)\n"
1396         + "{v = new Date(y, m, d, h, i, s);}\n"
1397         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0 && i >= 0)\n"
1398         + "{v = new Date(y, m, d, h, i);}\n"
1399         + "else if (y >= 0 && m >= 0 && d > 0 && h >= 0)\n"
1400         + "{v = new Date(y, m, d, h);}\n"
1401         + "else if (y >= 0 && m >= 0 && d > 0)\n"
1402         + "{v = new Date(y, m, d);}\n"
1403         + "else if (y >= 0 && m >= 0)\n"
1404         + "{v = new Date(y, m);}\n"
1405         + "else if (y >= 0)\n"
1406         + "{v = new Date(y);}\n"
1407         + "}return (v && (z || o))?\n" // favour UTC offset over GMT offset
1408         + "    ((z)? v.add(Date.SECOND, (v.getTimezoneOffset() * 60) + (z*1)) :\n" // reset to UTC, then add offset
1409         + "        v.add(Date.HOUR, (v.getGMTOffset() / 100) + (o / -100))) : v\n" // reset to GMT, then add offset
1410         + ";}";
1411
1412     Date.parseRegexes[regexNum] = new RegExp("^" + regex + "$");
1413     /** eval:var:zzzzzzzzzzzzz */
1414     eval(code);
1415 };
1416
1417 // private
1418 Date.formatCodeToRegex = function(character, currentGroup) {
1419     switch (character) {
1420     case "D":
1421         return {g:0,
1422         c:null,
1423         s:"(?:Sun|Mon|Tue|Wed|Thu|Fri|Sat)"};
1424     case "j":
1425         return {g:1,
1426             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1427             s:"(\\d{1,2})"}; // day of month without leading zeroes
1428     case "d":
1429         return {g:1,
1430             c:"d = parseInt(results[" + currentGroup + "], 10);\n",
1431             s:"(\\d{2})"}; // day of month with leading zeroes
1432     case "l":
1433         return {g:0,
1434             c:null,
1435             s:"(?:" + Date.dayNames.join("|") + ")"};
1436     case "S":
1437         return {g:0,
1438             c:null,
1439             s:"(?:st|nd|rd|th)"};
1440     case "w":
1441         return {g:0,
1442             c:null,
1443             s:"\\d"};
1444     case "z":
1445         return {g:0,
1446             c:null,
1447             s:"(?:\\d{1,3})"};
1448     case "W":
1449         return {g:0,
1450             c:null,
1451             s:"(?:\\d{2})"};
1452     case "F":
1453         return {g:1,
1454             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "].substring(0, 3)], 10);\n",
1455             s:"(" + Date.monthNames.join("|") + ")"};
1456     case "M":
1457         return {g:1,
1458             c:"m = parseInt(Date.monthNumbers[results[" + currentGroup + "]], 10);\n",
1459             s:"(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)"};
1460     case "n":
1461         return {g:1,
1462             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1463             s:"(\\d{1,2})"}; // Numeric representation of a month, without leading zeros
1464     case "m":
1465         return {g:1,
1466             c:"m = parseInt(results[" + currentGroup + "], 10) - 1;\n",
1467             s:"(\\d{2})"}; // Numeric representation of a month, with leading zeros
1468     case "t":
1469         return {g:0,
1470             c:null,
1471             s:"\\d{1,2}"};
1472     case "L":
1473         return {g:0,
1474             c:null,
1475             s:"(?:1|0)"};
1476     case "Y":
1477         return {g:1,
1478             c:"y = parseInt(results[" + currentGroup + "], 10);\n",
1479             s:"(\\d{4})"};
1480     case "y":
1481         return {g:1,
1482             c:"var ty = parseInt(results[" + currentGroup + "], 10);\n"
1483                 + "y = ty > Date.y2kYear ? 1900 + ty : 2000 + ty;\n",
1484             s:"(\\d{1,2})"};
1485     case "a":
1486         return {g:1,
1487             c:"if (results[" + currentGroup + "] == 'am') {\n"
1488                 + "if (h == 12) { h = 0; }\n"
1489                 + "} else { if (h < 12) { h += 12; }}",
1490             s:"(am|pm)"};
1491     case "A":
1492         return {g:1,
1493             c:"if (results[" + currentGroup + "] == 'AM') {\n"
1494                 + "if (h == 12) { h = 0; }\n"
1495                 + "} else { if (h < 12) { h += 12; }}",
1496             s:"(AM|PM)"};
1497     case "g":
1498     case "G":
1499         return {g:1,
1500             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1501             s:"(\\d{1,2})"}; // 12/24-hr format  format of an hour without leading zeroes
1502     case "h":
1503     case "H":
1504         return {g:1,
1505             c:"h = parseInt(results[" + currentGroup + "], 10);\n",
1506             s:"(\\d{2})"}; //  12/24-hr format  format of an hour with leading zeroes
1507     case "i":
1508         return {g:1,
1509             c:"i = parseInt(results[" + currentGroup + "], 10);\n",
1510             s:"(\\d{2})"};
1511     case "s":
1512         return {g:1,
1513             c:"s = parseInt(results[" + currentGroup + "], 10);\n",
1514             s:"(\\d{2})"};
1515     case "O":
1516         return {g:1,
1517             c:[
1518                 "o = results[", currentGroup, "];\n",
1519                 "var sn = o.substring(0,1);\n", // get + / - sign
1520                 "var hr = o.substring(1,3)*1 + Math.floor(o.substring(3,5) / 60);\n", // get hours (performs minutes-to-hour conversion also)
1521                 "var mn = o.substring(3,5) % 60;\n", // get minutes
1522                 "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n", // -12hrs <= GMT offset <= 14hrs
1523                 "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1524             ].join(""),
1525             s:"([+\-]\\d{2,4})"};
1526     
1527     
1528     case "P":
1529         return {g:1,
1530                 c:[
1531                    "o = results[", currentGroup, "];\n",
1532                    "var sn = o.substring(0,1);\n",
1533                    "var hr = o.substring(1,3)*1 + Math.floor(o.substring(4,6) / 60);\n",
1534                    "var mn = o.substring(4,6) % 60;\n",
1535                    "o = ((-12 <= (hr*60 + mn)/60) && ((hr*60 + mn)/60 <= 14))?\n",
1536                         "    (sn + String.leftPad(hr, 2, 0) + String.leftPad(mn, 2, 0)) : null;\n"
1537             ].join(""),
1538             s:"([+\-]\\d{4})"};
1539     case "T":
1540         return {g:0,
1541             c:null,
1542             s:"[A-Z]{1,4}"}; // timezone abbrev. may be between 1 - 4 chars
1543     case "Z":
1544         return {g:1,
1545             c:"z = results[" + currentGroup + "];\n" // -43200 <= UTC offset <= 50400
1546                   + "z = (-43200 <= z*1 && z*1 <= 50400)? z : null;\n",
1547             s:"([+\-]?\\d{1,5})"}; // leading '+' sign is optional for UTC offset
1548     default:
1549         return {g:0,
1550             c:null,
1551             s:String.escape(character)};
1552     }
1553 };
1554
1555 /**
1556  * Get the timezone abbreviation of the current date (equivalent to the format specifier 'T').
1557  * @return {String} The abbreviated timezone name (e.g. 'CST')
1558  */
1559 Date.prototype.getTimezone = function() {
1560     return this.toString().replace(/^.*? ([A-Z]{1,4})[\-+][0-9]{4} .*$/, "$1");
1561 };
1562
1563 /**
1564  * Get the offset from GMT of the current date (equivalent to the format specifier 'O').
1565  * @return {String} The 4-character offset string prefixed with + or - (e.g. '-0600')
1566  */
1567 Date.prototype.getGMTOffset = function() {
1568     return (this.getTimezoneOffset() > 0 ? "-" : "+")
1569         + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1570         + String.leftPad(this.getTimezoneOffset() % 60, 2, "0");
1571 };
1572
1573 /**
1574  * Get the offset from GMT of the current date (equivalent to the format specifier 'P').
1575  * @return {String} 2-characters representing hours and 2-characters representing minutes
1576  * seperated by a colon and prefixed with + or - (e.g. '-06:00')
1577  */
1578 Date.prototype.getGMTColonOffset = function() {
1579         return (this.getTimezoneOffset() > 0 ? "-" : "+")
1580                 + String.leftPad(Math.abs(Math.floor(this.getTimezoneOffset() / 60)), 2, "0")
1581                 + ":"
1582                 + String.leftPad(this.getTimezoneOffset() %60, 2, "0");
1583 }
1584
1585 /**
1586  * Get the numeric day number of the year, adjusted for leap year.
1587  * @return {Number} 0 through 364 (365 in leap years)
1588  */
1589 Date.prototype.getDayOfYear = function() {
1590     var num = 0;
1591     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1592     for (var i = 0; i < this.getMonth(); ++i) {
1593         num += Date.daysInMonth[i];
1594     }
1595     return num + this.getDate() - 1;
1596 };
1597
1598 /**
1599  * Get the string representation of the numeric week number of the year
1600  * (equivalent to the format specifier 'W').
1601  * @return {String} '00' through '52'
1602  */
1603 Date.prototype.getWeekOfYear = function() {
1604     // Skip to Thursday of this week
1605     var now = this.getDayOfYear() + (4 - this.getDay());
1606     // Find the first Thursday of the year
1607     var jan1 = new Date(this.getFullYear(), 0, 1);
1608     var then = (7 - jan1.getDay() + 4);
1609     return String.leftPad(((now - then) / 7) + 1, 2, "0");
1610 };
1611
1612 /**
1613  * Whether or not the current date is in a leap year.
1614  * @return {Boolean} True if the current date is in a leap year, else false
1615  */
1616 Date.prototype.isLeapYear = function() {
1617     var year = this.getFullYear();
1618     return ((year & 3) == 0 && (year % 100 || (year % 400 == 0 && year)));
1619 };
1620
1621 /**
1622  * Get the first day of the current month, adjusted for leap year.  The returned value
1623  * is the numeric day index within the week (0-6) which can be used in conjunction with
1624  * the {@link #monthNames} array to retrieve the textual day name.
1625  * Example:
1626  *<pre><code>
1627 var dt = new Date('1/10/2007');
1628 document.write(Date.dayNames[dt.getFirstDayOfMonth()]); //output: 'Monday'
1629 </code></pre>
1630  * @return {Number} The day number (0-6)
1631  */
1632 Date.prototype.getFirstDayOfMonth = function() {
1633     var day = (this.getDay() - (this.getDate() - 1)) % 7;
1634     return (day < 0) ? (day + 7) : day;
1635 };
1636
1637 /**
1638  * Get the last day of the current month, adjusted for leap year.  The returned value
1639  * is the numeric day index within the week (0-6) which can be used in conjunction with
1640  * the {@link #monthNames} array to retrieve the textual day name.
1641  * Example:
1642  *<pre><code>
1643 var dt = new Date('1/10/2007');
1644 document.write(Date.dayNames[dt.getLastDayOfMonth()]); //output: 'Wednesday'
1645 </code></pre>
1646  * @return {Number} The day number (0-6)
1647  */
1648 Date.prototype.getLastDayOfMonth = function() {
1649     var day = (this.getDay() + (Date.daysInMonth[this.getMonth()] - this.getDate())) % 7;
1650     return (day < 0) ? (day + 7) : day;
1651 };
1652
1653
1654 /**
1655  * Get the first date of this date's month
1656  * @return {Date}
1657  */
1658 Date.prototype.getFirstDateOfMonth = function() {
1659     return new Date(this.getFullYear(), this.getMonth(), 1);
1660 };
1661
1662 /**
1663  * Get the last date of this date's month
1664  * @return {Date}
1665  */
1666 Date.prototype.getLastDateOfMonth = function() {
1667     return new Date(this.getFullYear(), this.getMonth(), this.getDaysInMonth());
1668 };
1669 /**
1670  * Get the number of days in the current month, adjusted for leap year.
1671  * @return {Number} The number of days in the month
1672  */
1673 Date.prototype.getDaysInMonth = function() {
1674     Date.daysInMonth[1] = this.isLeapYear() ? 29 : 28;
1675     return Date.daysInMonth[this.getMonth()];
1676 };
1677
1678 /**
1679  * Get the English ordinal suffix of the current day (equivalent to the format specifier 'S').
1680  * @return {String} 'st, 'nd', 'rd' or 'th'
1681  */
1682 Date.prototype.getSuffix = function() {
1683     switch (this.getDate()) {
1684         case 1:
1685         case 21:
1686         case 31:
1687             return "st";
1688         case 2:
1689         case 22:
1690             return "nd";
1691         case 3:
1692         case 23:
1693             return "rd";
1694         default:
1695             return "th";
1696     }
1697 };
1698
1699 // private
1700 Date.daysInMonth = [31,28,31,30,31,30,31,31,30,31,30,31];
1701
1702 /**
1703  * An array of textual month names.
1704  * Override these values for international dates, for example...
1705  * Date.monthNames = ['JanInYourLang', 'FebInYourLang', ...];
1706  * @type Array
1707  * @static
1708  */
1709 Date.monthNames =
1710    ["January",
1711     "February",
1712     "March",
1713     "April",
1714     "May",
1715     "June",
1716     "July",
1717     "August",
1718     "September",
1719     "October",
1720     "November",
1721     "December"];
1722
1723 /**
1724  * An array of textual day names.
1725  * Override these values for international dates, for example...
1726  * Date.dayNames = ['SundayInYourLang', 'MondayInYourLang', ...];
1727  * @type Array
1728  * @static
1729  */
1730 Date.dayNames =
1731    ["Sunday",
1732     "Monday",
1733     "Tuesday",
1734     "Wednesday",
1735     "Thursday",
1736     "Friday",
1737     "Saturday"];
1738
1739 // private
1740 Date.y2kYear = 50;
1741 // private
1742 Date.monthNumbers = {
1743     Jan:0,
1744     Feb:1,
1745     Mar:2,
1746     Apr:3,
1747     May:4,
1748     Jun:5,
1749     Jul:6,
1750     Aug:7,
1751     Sep:8,
1752     Oct:9,
1753     Nov:10,
1754     Dec:11};
1755
1756 /**
1757  * Creates and returns a new Date instance with the exact same date value as the called instance.
1758  * Dates are copied and passed by reference, so if a copied date variable is modified later, the original
1759  * variable will also be changed.  When the intention is to create a new variable that will not
1760  * modify the original instance, you should create a clone.
1761  *
1762  * Example of correctly cloning a date:
1763  * <pre><code>
1764 //wrong way:
1765 var orig = new Date('10/1/2006');
1766 var copy = orig;
1767 copy.setDate(5);
1768 document.write(orig);  //returns 'Thu Oct 05 2006'!
1769
1770 //correct way:
1771 var orig = new Date('10/1/2006');
1772 var copy = orig.clone();
1773 copy.setDate(5);
1774 document.write(orig);  //returns 'Thu Oct 01 2006'
1775 </code></pre>
1776  * @return {Date} The new Date instance
1777  */
1778 Date.prototype.clone = function() {
1779         return new Date(this.getTime());
1780 };
1781
1782 /**
1783  * Clears any time information from this date
1784  @param {Boolean} clone true to create a clone of this date, clear the time and return it
1785  @return {Date} this or the clone
1786  */
1787 Date.prototype.clearTime = function(clone){
1788     if(clone){
1789         return this.clone().clearTime();
1790     }
1791     this.setHours(0);
1792     this.setMinutes(0);
1793     this.setSeconds(0);
1794     this.setMilliseconds(0);
1795     return this;
1796 };
1797
1798 // private
1799 // safari setMonth is broken -- check that this is only donw once...
1800 if(Roo.isSafari && typeof(Date.brokenSetMonth) == 'undefined'){
1801     Date.brokenSetMonth = Date.prototype.setMonth;
1802         Date.prototype.setMonth = function(num){
1803                 if(num <= -1){
1804                         var n = Math.ceil(-num);
1805                         var back_year = Math.ceil(n/12);
1806                         var month = (n % 12) ? 12 - n % 12 : 0 ;
1807                         this.setFullYear(this.getFullYear() - back_year);
1808                         return Date.brokenSetMonth.call(this, month);
1809                 } else {
1810                         return Date.brokenSetMonth.apply(this, arguments);
1811                 }
1812         };
1813 }
1814
1815 /** Date interval constant 
1816 * @static 
1817 * @type String */
1818 Date.MILLI = "ms";
1819 /** Date interval constant 
1820 * @static 
1821 * @type String */
1822 Date.SECOND = "s";
1823 /** Date interval constant 
1824 * @static 
1825 * @type String */
1826 Date.MINUTE = "mi";
1827 /** Date interval constant 
1828 * @static 
1829 * @type String */
1830 Date.HOUR = "h";
1831 /** Date interval constant 
1832 * @static 
1833 * @type String */
1834 Date.DAY = "d";
1835 /** Date interval constant 
1836 * @static 
1837 * @type String */
1838 Date.MONTH = "mo";
1839 /** Date interval constant 
1840 * @static 
1841 * @type String */
1842 Date.YEAR = "y";
1843
1844 /**
1845  * Provides a convenient method of performing basic date arithmetic.  This method
1846  * does not modify the Date instance being called - it creates and returns
1847  * a new Date instance containing the resulting date value.
1848  *
1849  * Examples:
1850  * <pre><code>
1851 //Basic usage:
1852 var dt = new Date('10/29/2006').add(Date.DAY, 5);
1853 document.write(dt); //returns 'Fri Oct 06 2006 00:00:00'
1854
1855 //Negative values will subtract correctly:
1856 var dt2 = new Date('10/1/2006').add(Date.DAY, -5);
1857 document.write(dt2); //returns 'Tue Sep 26 2006 00:00:00'
1858
1859 //You can even chain several calls together in one line!
1860 var dt3 = new Date('10/1/2006').add(Date.DAY, 5).add(Date.HOUR, 8).add(Date.MINUTE, -30);
1861 document.write(dt3); //returns 'Fri Oct 06 2006 07:30:00'
1862  </code></pre>
1863  *
1864  * @param {String} interval   A valid date interval enum value
1865  * @param {Number} value      The amount to add to the current date
1866  * @return {Date} The new Date instance
1867  */
1868 Date.prototype.add = function(interval, value){
1869   var d = this.clone();
1870   if (!interval || value === 0) { return d; }
1871   switch(interval.toLowerCase()){
1872     case Date.MILLI:
1873       d.setMilliseconds(this.getMilliseconds() + value);
1874       break;
1875     case Date.SECOND:
1876       d.setSeconds(this.getSeconds() + value);
1877       break;
1878     case Date.MINUTE:
1879       d.setMinutes(this.getMinutes() + value);
1880       break;
1881     case Date.HOUR:
1882       d.setHours(this.getHours() + value);
1883       break;
1884     case Date.DAY:
1885       d.setDate(this.getDate() + value);
1886       break;
1887     case Date.MONTH:
1888       var day = this.getDate();
1889       if(day > 28){
1890           day = Math.min(day, this.getFirstDateOfMonth().add('mo', value).getLastDateOfMonth().getDate());
1891       }
1892       d.setDate(day);
1893       d.setMonth(this.getMonth() + value);
1894       break;
1895     case Date.YEAR:
1896       d.setFullYear(this.getFullYear() + value);
1897       break;
1898   }
1899   return d;
1900 };
1901 /**
1902  * @class Roo.lib.Dom
1903  * @licence LGPL
1904  * @static
1905  * 
1906  * Dom utils (from YIU afaik)
1907  *
1908  * 
1909  **/
1910 Roo.lib.Dom = {
1911     /**
1912      * Get the view width
1913      * @param {Boolean} full True will get the full document, otherwise it's the view width
1914      * @return {Number} The width
1915      */
1916      
1917     getViewWidth : function(full) {
1918         return full ? this.getDocumentWidth() : this.getViewportWidth();
1919     },
1920     /**
1921      * Get the view height
1922      * @param {Boolean} full True will get the full document, otherwise it's the view height
1923      * @return {Number} The height
1924      */
1925     getViewHeight : function(full) {
1926         return full ? this.getDocumentHeight() : this.getViewportHeight();
1927     },
1928     /**
1929      * Get the Full Document height 
1930      * @return {Number} The height
1931      */
1932     getDocumentHeight: function() {
1933         var scrollHeight = (document.compatMode != "CSS1Compat") ? document.body.scrollHeight : document.documentElement.scrollHeight;
1934         return Math.max(scrollHeight, this.getViewportHeight());
1935     },
1936     /**
1937      * Get the Full Document width
1938      * @return {Number} The width
1939      */
1940     getDocumentWidth: function() {
1941         var scrollWidth = (document.compatMode != "CSS1Compat") ? document.body.scrollWidth : document.documentElement.scrollWidth;
1942         return Math.max(scrollWidth, this.getViewportWidth());
1943     },
1944     /**
1945      * Get the Window Viewport height
1946      * @return {Number} The height
1947      */
1948     getViewportHeight: function() {
1949         var height = self.innerHeight;
1950         var mode = document.compatMode;
1951
1952         if ((mode || Roo.isIE) && !Roo.isOpera) {
1953             height = (mode == "CSS1Compat") ?
1954                      document.documentElement.clientHeight :
1955                      document.body.clientHeight;
1956         }
1957
1958         return height;
1959     },
1960     /**
1961      * Get the Window Viewport width
1962      * @return {Number} The width
1963      */
1964     getViewportWidth: function() {
1965         var width = self.innerWidth;
1966         var mode = document.compatMode;
1967
1968         if (mode || Roo.isIE) {
1969             width = (mode == "CSS1Compat") ?
1970                     document.documentElement.clientWidth :
1971                     document.body.clientWidth;
1972         }
1973         return width;
1974     },
1975
1976     isAncestor : function(p, c) {
1977         p = Roo.getDom(p);
1978         c = Roo.getDom(c);
1979         if (!p || !c) {
1980             return false;
1981         }
1982
1983         if (p.contains && !Roo.isSafari) {
1984             return p.contains(c);
1985         } else if (p.compareDocumentPosition) {
1986             return !!(p.compareDocumentPosition(c) & 16);
1987         } else {
1988             var parent = c.parentNode;
1989             while (parent) {
1990                 if (parent == p) {
1991                     return true;
1992                 }
1993                 else if (!parent.tagName || parent.tagName.toUpperCase() == "HTML") {
1994                     return false;
1995                 }
1996                 parent = parent.parentNode;
1997             }
1998             return false;
1999         }
2000     },
2001
2002     getRegion : function(el) {
2003         return Roo.lib.Region.getRegion(el);
2004     },
2005
2006     getY : function(el) {
2007         return this.getXY(el)[1];
2008     },
2009
2010     getX : function(el) {
2011         return this.getXY(el)[0];
2012     },
2013
2014     getXY : function(el) {
2015         var p, pe, b, scroll, bd = document.body;
2016         el = Roo.getDom(el);
2017         var fly = Roo.lib.AnimBase.fly;
2018         if (el.getBoundingClientRect) {
2019             b = el.getBoundingClientRect();
2020             scroll = fly(document).getScroll();
2021             return [b.left + scroll.left, b.top + scroll.top];
2022         }
2023         var x = 0, y = 0;
2024
2025         p = el;
2026
2027         var hasAbsolute = fly(el).getStyle("position") == "absolute";
2028
2029         while (p) {
2030
2031             x += p.offsetLeft;
2032             y += p.offsetTop;
2033
2034             if (!hasAbsolute && fly(p).getStyle("position") == "absolute") {
2035                 hasAbsolute = true;
2036             }
2037
2038             if (Roo.isGecko) {
2039                 pe = fly(p);
2040
2041                 var bt = parseInt(pe.getStyle("borderTopWidth"), 10) || 0;
2042                 var bl = parseInt(pe.getStyle("borderLeftWidth"), 10) || 0;
2043
2044
2045                 x += bl;
2046                 y += bt;
2047
2048
2049                 if (p != el && pe.getStyle('overflow') != 'visible') {
2050                     x += bl;
2051                     y += bt;
2052                 }
2053             }
2054             p = p.offsetParent;
2055         }
2056
2057         if (Roo.isSafari && hasAbsolute) {
2058             x -= bd.offsetLeft;
2059             y -= bd.offsetTop;
2060         }
2061
2062         if (Roo.isGecko && !hasAbsolute) {
2063             var dbd = fly(bd);
2064             x += parseInt(dbd.getStyle("borderLeftWidth"), 10) || 0;
2065             y += parseInt(dbd.getStyle("borderTopWidth"), 10) || 0;
2066         }
2067
2068         p = el.parentNode;
2069         while (p && p != bd) {
2070             if (!Roo.isOpera || (p.tagName != 'TR' && fly(p).getStyle("display") != "inline")) {
2071                 x -= p.scrollLeft;
2072                 y -= p.scrollTop;
2073             }
2074             p = p.parentNode;
2075         }
2076         return [x, y];
2077     },
2078  
2079   
2080
2081
2082     setXY : function(el, xy) {
2083         el = Roo.fly(el, '_setXY');
2084         el.position();
2085         var pts = el.translatePoints(xy);
2086         if (xy[0] !== false) {
2087             el.dom.style.left = pts.left + "px";
2088         }
2089         if (xy[1] !== false) {
2090             el.dom.style.top = pts.top + "px";
2091         }
2092     },
2093
2094     setX : function(el, x) {
2095         this.setXY(el, [x, false]);
2096     },
2097
2098     setY : function(el, y) {
2099         this.setXY(el, [false, y]);
2100     }
2101 };
2102 /*
2103  * Portions of this file are based on pieces of Yahoo User Interface Library
2104  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2105  * YUI licensed under the BSD License:
2106  * http://developer.yahoo.net/yui/license.txt
2107  * <script type="text/javascript">
2108  *
2109  */
2110
2111 Roo.lib.Event = function() {
2112     var loadComplete = false;
2113     var listeners = [];
2114     var unloadListeners = [];
2115     var retryCount = 0;
2116     var onAvailStack = [];
2117     var counter = 0;
2118     var lastError = null;
2119
2120     return {
2121         POLL_RETRYS: 200,
2122         POLL_INTERVAL: 20,
2123         EL: 0,
2124         TYPE: 1,
2125         FN: 2,
2126         WFN: 3,
2127         OBJ: 3,
2128         ADJ_SCOPE: 4,
2129         _interval: null,
2130
2131         startInterval: function() {
2132             if (!this._interval) {
2133                 var self = this;
2134                 var callback = function() {
2135                     self._tryPreloadAttach();
2136                 };
2137                 this._interval = setInterval(callback, this.POLL_INTERVAL);
2138
2139             }
2140         },
2141
2142         onAvailable: function(p_id, p_fn, p_obj, p_override) {
2143             onAvailStack.push({ id:         p_id,
2144                 fn:         p_fn,
2145                 obj:        p_obj,
2146                 override:   p_override,
2147                 checkReady: false    });
2148
2149             retryCount = this.POLL_RETRYS;
2150             this.startInterval();
2151         },
2152
2153
2154         addListener: function(el, eventName, fn) {
2155             el = Roo.getDom(el);
2156             if (!el || !fn) {
2157                 return false;
2158             }
2159
2160             if ("unload" == eventName) {
2161                 unloadListeners[unloadListeners.length] =
2162                 [el, eventName, fn];
2163                 return true;
2164             }
2165
2166             var wrappedFn = function(e) {
2167                 return fn(Roo.lib.Event.getEvent(e));
2168             };
2169
2170             var li = [el, eventName, fn, wrappedFn];
2171
2172             var index = listeners.length;
2173             listeners[index] = li;
2174
2175             this.doAdd(el, eventName, wrappedFn, false);
2176             return true;
2177
2178         },
2179
2180
2181         removeListener: function(el, eventName, fn) {
2182             var i, len;
2183
2184             el = Roo.getDom(el);
2185
2186             if(!fn) {
2187                 return this.purgeElement(el, false, eventName);
2188             }
2189
2190
2191             if ("unload" == eventName) {
2192
2193                 for (i = 0,len = unloadListeners.length; i < len; i++) {
2194                     var li = unloadListeners[i];
2195                     if (li &&
2196                         li[0] == el &&
2197                         li[1] == eventName &&
2198                         li[2] == fn) {
2199                         unloadListeners.splice(i, 1);
2200                         return true;
2201                     }
2202                 }
2203
2204                 return false;
2205             }
2206
2207             var cacheItem = null;
2208
2209
2210             var index = arguments[3];
2211
2212             if ("undefined" == typeof index) {
2213                 index = this._getCacheIndex(el, eventName, fn);
2214             }
2215
2216             if (index >= 0) {
2217                 cacheItem = listeners[index];
2218             }
2219
2220             if (!el || !cacheItem) {
2221                 return false;
2222             }
2223
2224             this.doRemove(el, eventName, cacheItem[this.WFN], false);
2225
2226             delete listeners[index][this.WFN];
2227             delete listeners[index][this.FN];
2228             listeners.splice(index, 1);
2229
2230             return true;
2231
2232         },
2233
2234
2235         getTarget: function(ev, resolveTextNode) {
2236             ev = ev.browserEvent || ev;
2237             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2238             var t = ev.target || ev.srcElement;
2239             return this.resolveTextNode(t);
2240         },
2241
2242
2243         resolveTextNode: function(node) {
2244             if (Roo.isSafari && node && 3 == node.nodeType) {
2245                 return node.parentNode;
2246             } else {
2247                 return node;
2248             }
2249         },
2250
2251
2252         getPageX: function(ev) {
2253             ev = ev.browserEvent || ev;
2254             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2255             var x = ev.pageX;
2256             if (!x && 0 !== x) {
2257                 x = ev.clientX || 0;
2258
2259                 if (Roo.isIE) {
2260                     x += this.getScroll()[1];
2261                 }
2262             }
2263
2264             return x;
2265         },
2266
2267
2268         getPageY: function(ev) {
2269             ev = ev.browserEvent || ev;
2270             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2271             var y = ev.pageY;
2272             if (!y && 0 !== y) {
2273                 y = ev.clientY || 0;
2274
2275                 if (Roo.isIE) {
2276                     y += this.getScroll()[0];
2277                 }
2278             }
2279
2280
2281             return y;
2282         },
2283
2284
2285         getXY: function(ev) {
2286             ev = ev.browserEvent || ev;
2287             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2288             return [this.getPageX(ev), this.getPageY(ev)];
2289         },
2290
2291
2292         getRelatedTarget: function(ev) {
2293             ev = ev.browserEvent || ev;
2294             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2295             var t = ev.relatedTarget;
2296             if (!t) {
2297                 if (ev.type == "mouseout") {
2298                     t = ev.toElement;
2299                 } else if (ev.type == "mouseover") {
2300                     t = ev.fromElement;
2301                 }
2302             }
2303
2304             return this.resolveTextNode(t);
2305         },
2306
2307
2308         getTime: function(ev) {
2309             ev = ev.browserEvent || ev;
2310             ev = ev.touches ? (ev.touches[0] || ev.changedTouches[0] || ev )  : ev;
2311             if (!ev.time) {
2312                 var t = new Date().getTime();
2313                 try {
2314                     ev.time = t;
2315                 } catch(ex) {
2316                     this.lastError = ex;
2317                     return t;
2318                 }
2319             }
2320
2321             return ev.time;
2322         },
2323
2324
2325         stopEvent: function(ev) {
2326             this.stopPropagation(ev);
2327             this.preventDefault(ev);
2328         },
2329
2330
2331         stopPropagation: function(ev) {
2332             ev = ev.browserEvent || ev;
2333             if (ev.stopPropagation) {
2334                 ev.stopPropagation();
2335             } else {
2336                 ev.cancelBubble = true;
2337             }
2338         },
2339
2340
2341         preventDefault: function(ev) {
2342             ev = ev.browserEvent || ev;
2343             if(ev.preventDefault) {
2344                 ev.preventDefault();
2345             } else {
2346                 ev.returnValue = false;
2347             }
2348         },
2349
2350
2351         getEvent: function(e) {
2352             var ev = e || window.event;
2353             if (!ev) {
2354                 var c = this.getEvent.caller;
2355                 while (c) {
2356                     ev = c.arguments[0];
2357                     if (ev && Event == ev.constructor) {
2358                         break;
2359                     }
2360                     c = c.caller;
2361                 }
2362             }
2363             return ev;
2364         },
2365
2366
2367         getCharCode: function(ev) {
2368             ev = ev.browserEvent || ev;
2369             return ev.charCode || ev.keyCode || 0;
2370         },
2371
2372
2373         _getCacheIndex: function(el, eventName, fn) {
2374             for (var i = 0,len = listeners.length; i < len; ++i) {
2375                 var li = listeners[i];
2376                 if (li &&
2377                     li[this.FN] == fn &&
2378                     li[this.EL] == el &&
2379                     li[this.TYPE] == eventName) {
2380                     return i;
2381                 }
2382             }
2383
2384             return -1;
2385         },
2386
2387
2388         elCache: {},
2389
2390
2391         getEl: function(id) {
2392             return document.getElementById(id);
2393         },
2394
2395
2396         clearCache: function() {
2397         },
2398
2399
2400         _load: function(e) {
2401             loadComplete = true;
2402             var EU = Roo.lib.Event;
2403
2404
2405             if (Roo.isIE) {
2406                 EU.doRemove(window, "load", EU._load);
2407             }
2408         },
2409
2410
2411         _tryPreloadAttach: function() {
2412
2413             if (this.locked) {
2414                 return false;
2415             }
2416
2417             this.locked = true;
2418
2419
2420             var tryAgain = !loadComplete;
2421             if (!tryAgain) {
2422                 tryAgain = (retryCount > 0);
2423             }
2424
2425
2426             var notAvail = [];
2427             for (var i = 0,len = onAvailStack.length; i < len; ++i) {
2428                 var item = onAvailStack[i];
2429                 if (item) {
2430                     var el = this.getEl(item.id);
2431
2432                     if (el) {
2433                         if (!item.checkReady ||
2434                             loadComplete ||
2435                             el.nextSibling ||
2436                             (document && document.body)) {
2437
2438                             var scope = el;
2439                             if (item.override) {
2440                                 if (item.override === true) {
2441                                     scope = item.obj;
2442                                 } else {
2443                                     scope = item.override;
2444                                 }
2445                             }
2446                             item.fn.call(scope, item.obj);
2447                             onAvailStack[i] = null;
2448                         }
2449                     } else {
2450                         notAvail.push(item);
2451                     }
2452                 }
2453             }
2454
2455             retryCount = (notAvail.length === 0) ? 0 : retryCount - 1;
2456
2457             if (tryAgain) {
2458
2459                 this.startInterval();
2460             } else {
2461                 clearInterval(this._interval);
2462                 this._interval = null;
2463             }
2464
2465             this.locked = false;
2466
2467             return true;
2468
2469         },
2470
2471
2472         purgeElement: function(el, recurse, eventName) {
2473             var elListeners = this.getListeners(el, eventName);
2474             if (elListeners) {
2475                 for (var i = 0,len = elListeners.length; i < len; ++i) {
2476                     var l = elListeners[i];
2477                     this.removeListener(el, l.type, l.fn);
2478                 }
2479             }
2480
2481             if (recurse && el && el.childNodes) {
2482                 for (i = 0,len = el.childNodes.length; i < len; ++i) {
2483                     this.purgeElement(el.childNodes[i], recurse, eventName);
2484                 }
2485             }
2486         },
2487
2488
2489         getListeners: function(el, eventName) {
2490             var results = [], searchLists;
2491             if (!eventName) {
2492                 searchLists = [listeners, unloadListeners];
2493             } else if (eventName == "unload") {
2494                 searchLists = [unloadListeners];
2495             } else {
2496                 searchLists = [listeners];
2497             }
2498
2499             for (var j = 0; j < searchLists.length; ++j) {
2500                 var searchList = searchLists[j];
2501                 if (searchList && searchList.length > 0) {
2502                     for (var i = 0,len = searchList.length; i < len; ++i) {
2503                         var l = searchList[i];
2504                         if (l && l[this.EL] === el &&
2505                             (!eventName || eventName === l[this.TYPE])) {
2506                             results.push({
2507                                 type:   l[this.TYPE],
2508                                 fn:     l[this.FN],
2509                                 obj:    l[this.OBJ],
2510                                 adjust: l[this.ADJ_SCOPE],
2511                                 index:  i
2512                             });
2513                         }
2514                     }
2515                 }
2516             }
2517
2518             return (results.length) ? results : null;
2519         },
2520
2521
2522         _unload: function(e) {
2523
2524             var EU = Roo.lib.Event, i, j, l, len, index;
2525
2526             for (i = 0,len = unloadListeners.length; i < len; ++i) {
2527                 l = unloadListeners[i];
2528                 if (l) {
2529                     var scope = window;
2530                     if (l[EU.ADJ_SCOPE]) {
2531                         if (l[EU.ADJ_SCOPE] === true) {
2532                             scope = l[EU.OBJ];
2533                         } else {
2534                             scope = l[EU.ADJ_SCOPE];
2535                         }
2536                     }
2537                     l[EU.FN].call(scope, EU.getEvent(e), l[EU.OBJ]);
2538                     unloadListeners[i] = null;
2539                     l = null;
2540                     scope = null;
2541                 }
2542             }
2543
2544             unloadListeners = null;
2545
2546             if (listeners && listeners.length > 0) {
2547                 j = listeners.length;
2548                 while (j) {
2549                     index = j - 1;
2550                     l = listeners[index];
2551                     if (l) {
2552                         EU.removeListener(l[EU.EL], l[EU.TYPE],
2553                                 l[EU.FN], index);
2554                     }
2555                     j = j - 1;
2556                 }
2557                 l = null;
2558
2559                 EU.clearCache();
2560             }
2561
2562             EU.doRemove(window, "unload", EU._unload);
2563
2564         },
2565
2566
2567         getScroll: function() {
2568             var dd = document.documentElement, db = document.body;
2569             if (dd && (dd.scrollTop || dd.scrollLeft)) {
2570                 return [dd.scrollTop, dd.scrollLeft];
2571             } else if (db) {
2572                 return [db.scrollTop, db.scrollLeft];
2573             } else {
2574                 return [0, 0];
2575             }
2576         },
2577
2578
2579         doAdd: function () {
2580             if (window.addEventListener) {
2581                 return function(el, eventName, fn, capture) {
2582                     el.addEventListener(eventName, fn, (capture));
2583                 };
2584             } else if (window.attachEvent) {
2585                 return function(el, eventName, fn, capture) {
2586                     el.attachEvent("on" + eventName, fn);
2587                 };
2588             } else {
2589                 return function() {
2590                 };
2591             }
2592         }(),
2593
2594
2595         doRemove: function() {
2596             if (window.removeEventListener) {
2597                 return function (el, eventName, fn, capture) {
2598                     el.removeEventListener(eventName, fn, (capture));
2599                 };
2600             } else if (window.detachEvent) {
2601                 return function (el, eventName, fn) {
2602                     el.detachEvent("on" + eventName, fn);
2603                 };
2604             } else {
2605                 return function() {
2606                 };
2607             }
2608         }()
2609     };
2610     
2611 }();
2612 (function() {     
2613    
2614     var E = Roo.lib.Event;
2615     E.on = E.addListener;
2616     E.un = E.removeListener;
2617
2618     if (document && document.body) {
2619         E._load();
2620     } else {
2621         E.doAdd(window, "load", E._load);
2622     }
2623     E.doAdd(window, "unload", E._unload);
2624     E._tryPreloadAttach();
2625 })();
2626
2627  
2628
2629 (function() {
2630     /**
2631      * @class Roo.lib.Ajax
2632      *
2633      * provide a simple Ajax request utility functions
2634      * 
2635      * Portions of this file are based on pieces of Yahoo User Interface Library
2636     * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
2637     * YUI licensed under the BSD License:
2638     * http://developer.yahoo.net/yui/license.txt
2639     * <script type="text/javascript">
2640     *
2641      *
2642      */
2643     Roo.lib.Ajax = {
2644         /**
2645          * @static 
2646          */
2647         request : function(method, uri, cb, data, options) {
2648             if(options){
2649                 var hs = options.headers;
2650                 if(hs){
2651                     for(var h in hs){
2652                         if(hs.hasOwnProperty(h)){
2653                             this.initHeader(h, hs[h], false);
2654                         }
2655                     }
2656                 }
2657                 if(options.xmlData){
2658                     this.initHeader('Content-Type', 'text/xml', false);
2659                     method = 'POST';
2660                     data = options.xmlData;
2661                 }
2662             }
2663
2664             return this.asyncRequest(method, uri, cb, data);
2665         },
2666         /**
2667          * serialize a form
2668          *
2669          * @static
2670          * @param {DomForm} form element
2671          * @return {String} urlencode form output.
2672          */
2673         serializeForm : function(form) {
2674             if(typeof form == 'string') {
2675                 form = (document.getElementById(form) || document.forms[form]);
2676             }
2677
2678             var el, name, val, disabled, data = '', hasSubmit = false;
2679             for (var i = 0; i < form.elements.length; i++) {
2680                 el = form.elements[i];
2681                 disabled = form.elements[i].disabled;
2682                 name = form.elements[i].name;
2683                 val = form.elements[i].value;
2684
2685                 if (!disabled && name){
2686                     switch (el.type)
2687                             {
2688                         case 'select-one':
2689                         case 'select-multiple':
2690                             for (var j = 0; j < el.options.length; j++) {
2691                                 if (el.options[j].selected) {
2692                                     if (Roo.isIE) {
2693                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].attributes['value'].specified ? el.options[j].value : el.options[j].text) + '&';
2694                                     }
2695                                     else {
2696                                         data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(el.options[j].hasAttribute('value') ? el.options[j].value : el.options[j].text) + '&';
2697                                     }
2698                                 }
2699                             }
2700                             break;
2701                         case 'radio':
2702                         case 'checkbox':
2703                             if (el.checked) {
2704                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2705                             }
2706                             break;
2707                         case 'file':
2708
2709                         case undefined:
2710
2711                         case 'reset':
2712
2713                         case 'button':
2714
2715                             break;
2716                         case 'submit':
2717                             if(hasSubmit == false) {
2718                                 data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2719                                 hasSubmit = true;
2720                             }
2721                             break;
2722                         default:
2723                             data += Roo.encodeURIComponent(name) + '=' + Roo.encodeURIComponent(val) + '&';
2724                             break;
2725                     }
2726                 }
2727             }
2728             data = data.substr(0, data.length - 1);
2729             return data;
2730         },
2731
2732         headers:{},
2733
2734         hasHeaders:false,
2735
2736         useDefaultHeader:true,
2737
2738         defaultPostHeader:'application/x-www-form-urlencoded',
2739
2740         useDefaultXhrHeader:true,
2741
2742         defaultXhrHeader:'XMLHttpRequest',
2743
2744         hasDefaultHeaders:true,
2745
2746         defaultHeaders:{},
2747
2748         poll:{},
2749
2750         timeout:{},
2751
2752         pollInterval:50,
2753
2754         transactionId:0,
2755
2756         setProgId:function(id)
2757         {
2758             this.activeX.unshift(id);
2759         },
2760
2761         setDefaultPostHeader:function(b)
2762         {
2763             this.useDefaultHeader = b;
2764         },
2765
2766         setDefaultXhrHeader:function(b)
2767         {
2768             this.useDefaultXhrHeader = b;
2769         },
2770
2771         setPollingInterval:function(i)
2772         {
2773             if (typeof i == 'number' && isFinite(i)) {
2774                 this.pollInterval = i;
2775             }
2776         },
2777
2778         createXhrObject:function(transactionId)
2779         {
2780             var obj,http;
2781             try
2782             {
2783
2784                 http = new XMLHttpRequest();
2785
2786                 obj = { conn:http, tId:transactionId };
2787             }
2788             catch(e)
2789             {
2790                 for (var i = 0; i < this.activeX.length; ++i) {
2791                     try
2792                     {
2793
2794                         http = new ActiveXObject(this.activeX[i]);
2795
2796                         obj = { conn:http, tId:transactionId };
2797                         break;
2798                     }
2799                     catch(e) {
2800                     }
2801                 }
2802             }
2803             finally
2804             {
2805                 return obj;
2806             }
2807         },
2808
2809         getConnectionObject:function()
2810         {
2811             var o;
2812             var tId = this.transactionId;
2813
2814             try
2815             {
2816                 o = this.createXhrObject(tId);
2817                 if (o) {
2818                     this.transactionId++;
2819                 }
2820             }
2821             catch(e) {
2822             }
2823             finally
2824             {
2825                 return o;
2826             }
2827         },
2828
2829         asyncRequest:function(method, uri, callback, postData)
2830         {
2831             var o = this.getConnectionObject();
2832
2833             if (!o) {
2834                 return null;
2835             }
2836             else {
2837                 o.conn.open(method, uri, true);
2838
2839                 if (this.useDefaultXhrHeader) {
2840                     if (!this.defaultHeaders['X-Requested-With']) {
2841                         this.initHeader('X-Requested-With', this.defaultXhrHeader, true);
2842                     }
2843                 }
2844
2845                 if(postData && this.useDefaultHeader){
2846                     this.initHeader('Content-Type', this.defaultPostHeader);
2847                 }
2848
2849                  if (this.hasDefaultHeaders || this.hasHeaders) {
2850                     this.setHeader(o);
2851                 }
2852
2853                 this.handleReadyState(o, callback);
2854                 o.conn.send(postData || null);
2855
2856                 return o;
2857             }
2858         },
2859
2860         handleReadyState:function(o, callback)
2861         {
2862             var oConn = this;
2863
2864             if (callback && callback.timeout) {
2865                 
2866                 this.timeout[o.tId] = window.setTimeout(function() {
2867                     oConn.abort(o, callback, true);
2868                 }, callback.timeout);
2869             }
2870
2871             this.poll[o.tId] = window.setInterval(
2872                     function() {
2873                         if (o.conn && o.conn.readyState == 4) {
2874                             window.clearInterval(oConn.poll[o.tId]);
2875                             delete oConn.poll[o.tId];
2876
2877                             if(callback && callback.timeout) {
2878                                 window.clearTimeout(oConn.timeout[o.tId]);
2879                                 delete oConn.timeout[o.tId];
2880                             }
2881
2882                             oConn.handleTransactionResponse(o, callback);
2883                         }
2884                     }
2885                     , this.pollInterval);
2886         },
2887
2888         handleTransactionResponse:function(o, callback, isAbort)
2889         {
2890
2891             if (!callback) {
2892                 this.releaseObject(o);
2893                 return;
2894             }
2895
2896             var httpStatus, responseObject;
2897
2898             try
2899             {
2900                 if (o.conn.status !== undefined && o.conn.status != 0) {
2901                     httpStatus = o.conn.status;
2902                 }
2903                 else {
2904                     httpStatus = 13030;
2905                 }
2906             }
2907             catch(e) {
2908
2909
2910                 httpStatus = 13030;
2911             }
2912
2913             if (httpStatus >= 200 && httpStatus < 300) {
2914                 responseObject = this.createResponseObject(o, callback.argument);
2915                 if (callback.success) {
2916                     if (!callback.scope) {
2917                         callback.success(responseObject);
2918                     }
2919                     else {
2920
2921
2922                         callback.success.apply(callback.scope, [responseObject]);
2923                     }
2924                 }
2925             }
2926             else {
2927                 switch (httpStatus) {
2928
2929                     case 12002:
2930                     case 12029:
2931                     case 12030:
2932                     case 12031:
2933                     case 12152:
2934                     case 13030:
2935                         responseObject = this.createExceptionObject(o.tId, callback.argument, (isAbort ? isAbort : false));
2936                         if (callback.failure) {
2937                             if (!callback.scope) {
2938                                 callback.failure(responseObject);
2939                             }
2940                             else {
2941                                 callback.failure.apply(callback.scope, [responseObject]);
2942                             }
2943                         }
2944                         break;
2945                     default:
2946                         responseObject = this.createResponseObject(o, callback.argument);
2947                         if (callback.failure) {
2948                             if (!callback.scope) {
2949                                 callback.failure(responseObject);
2950                             }
2951                             else {
2952                                 callback.failure.apply(callback.scope, [responseObject]);
2953                             }
2954                         }
2955                 }
2956             }
2957
2958             this.releaseObject(o);
2959             responseObject = null;
2960         },
2961
2962         createResponseObject:function(o, callbackArg)
2963         {
2964             var obj = {};
2965             var headerObj = {};
2966
2967             try
2968             {
2969                 var headerStr = o.conn.getAllResponseHeaders();
2970                 var header = headerStr.split('\n');
2971                 for (var i = 0; i < header.length; i++) {
2972                     var delimitPos = header[i].indexOf(':');
2973                     if (delimitPos != -1) {
2974                         headerObj[header[i].substring(0, delimitPos)] = header[i].substring(delimitPos + 2);
2975                     }
2976                 }
2977             }
2978             catch(e) {
2979             }
2980
2981             obj.tId = o.tId;
2982             obj.status = o.conn.status;
2983             obj.statusText = o.conn.statusText;
2984             obj.getResponseHeader = headerObj;
2985             obj.getAllResponseHeaders = headerStr;
2986             obj.responseText = o.conn.responseText;
2987             obj.responseXML = o.conn.responseXML;
2988
2989             if (typeof callbackArg !== undefined) {
2990                 obj.argument = callbackArg;
2991             }
2992
2993             return obj;
2994         },
2995
2996         createExceptionObject:function(tId, callbackArg, isAbort)
2997         {
2998             var COMM_CODE = 0;
2999             var COMM_ERROR = 'communication failure';
3000             var ABORT_CODE = -1;
3001             var ABORT_ERROR = 'transaction aborted';
3002
3003             var obj = {};
3004
3005             obj.tId = tId;
3006             if (isAbort) {
3007                 obj.status = ABORT_CODE;
3008                 obj.statusText = ABORT_ERROR;
3009             }
3010             else {
3011                 obj.status = COMM_CODE;
3012                 obj.statusText = COMM_ERROR;
3013             }
3014
3015             if (callbackArg) {
3016                 obj.argument = callbackArg;
3017             }
3018
3019             return obj;
3020         },
3021
3022         initHeader:function(label, value, isDefault)
3023         {
3024             var headerObj = (isDefault) ? this.defaultHeaders : this.headers;
3025
3026             if (headerObj[label] === undefined) {
3027                 headerObj[label] = value;
3028             }
3029             else {
3030
3031
3032                 headerObj[label] = value + "," + headerObj[label];
3033             }
3034
3035             if (isDefault) {
3036                 this.hasDefaultHeaders = true;
3037             }
3038             else {
3039                 this.hasHeaders = true;
3040             }
3041         },
3042
3043
3044         setHeader:function(o)
3045         {
3046             if (this.hasDefaultHeaders) {
3047                 for (var prop in this.defaultHeaders) {
3048                     if (this.defaultHeaders.hasOwnProperty(prop)) {
3049                         o.conn.setRequestHeader(prop, this.defaultHeaders[prop]);
3050                     }
3051                 }
3052             }
3053
3054             if (this.hasHeaders) {
3055                 for (var prop in this.headers) {
3056                     if (this.headers.hasOwnProperty(prop)) {
3057                         o.conn.setRequestHeader(prop, this.headers[prop]);
3058                     }
3059                 }
3060                 this.headers = {};
3061                 this.hasHeaders = false;
3062             }
3063         },
3064
3065         resetDefaultHeaders:function() {
3066             delete this.defaultHeaders;
3067             this.defaultHeaders = {};
3068             this.hasDefaultHeaders = false;
3069         },
3070
3071         abort:function(o, callback, isTimeout)
3072         {
3073             if(this.isCallInProgress(o)) {
3074                 o.conn.abort();
3075                 window.clearInterval(this.poll[o.tId]);
3076                 delete this.poll[o.tId];
3077                 if (isTimeout) {
3078                     delete this.timeout[o.tId];
3079                 }
3080
3081                 this.handleTransactionResponse(o, callback, true);
3082
3083                 return true;
3084             }
3085             else {
3086                 return false;
3087             }
3088         },
3089
3090
3091         isCallInProgress:function(o)
3092         {
3093             if (o && o.conn) {
3094                 return o.conn.readyState != 4 && o.conn.readyState != 0;
3095             }
3096             else {
3097
3098                 return false;
3099             }
3100         },
3101
3102
3103         releaseObject:function(o)
3104         {
3105
3106             o.conn = null;
3107
3108             o = null;
3109         },
3110
3111         activeX:[
3112         'MSXML2.XMLHTTP.3.0',
3113         'MSXML2.XMLHTTP',
3114         'Microsoft.XMLHTTP'
3115         ]
3116
3117
3118     };
3119 })();/*
3120  * Portions of this file are based on pieces of Yahoo User Interface Library
3121  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3122  * YUI licensed under the BSD License:
3123  * http://developer.yahoo.net/yui/license.txt
3124  * <script type="text/javascript">
3125  *
3126  */
3127
3128 Roo.lib.Region = function(t, r, b, l) {
3129     this.top = t;
3130     this[1] = t;
3131     this.right = r;
3132     this.bottom = b;
3133     this.left = l;
3134     this[0] = l;
3135 };
3136
3137
3138 Roo.lib.Region.prototype = {
3139     contains : function(region) {
3140         return ( region.left >= this.left &&
3141                  region.right <= this.right &&
3142                  region.top >= this.top &&
3143                  region.bottom <= this.bottom    );
3144
3145     },
3146
3147     getArea : function() {
3148         return ( (this.bottom - this.top) * (this.right - this.left) );
3149     },
3150
3151     intersect : function(region) {
3152         var t = Math.max(this.top, region.top);
3153         var r = Math.min(this.right, region.right);
3154         var b = Math.min(this.bottom, region.bottom);
3155         var l = Math.max(this.left, region.left);
3156
3157         if (b >= t && r >= l) {
3158             return new Roo.lib.Region(t, r, b, l);
3159         } else {
3160             return null;
3161         }
3162     },
3163     union : function(region) {
3164         var t = Math.min(this.top, region.top);
3165         var r = Math.max(this.right, region.right);
3166         var b = Math.max(this.bottom, region.bottom);
3167         var l = Math.min(this.left, region.left);
3168
3169         return new Roo.lib.Region(t, r, b, l);
3170     },
3171
3172     adjust : function(t, l, b, r) {
3173         this.top += t;
3174         this.left += l;
3175         this.right += r;
3176         this.bottom += b;
3177         return this;
3178     }
3179 };
3180
3181 Roo.lib.Region.getRegion = function(el) {
3182     var p = Roo.lib.Dom.getXY(el);
3183
3184     var t = p[1];
3185     var r = p[0] + el.offsetWidth;
3186     var b = p[1] + el.offsetHeight;
3187     var l = p[0];
3188
3189     return new Roo.lib.Region(t, r, b, l);
3190 };
3191 /*
3192  * Portions of this file are based on pieces of Yahoo User Interface Library
3193  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3194  * YUI licensed under the BSD License:
3195  * http://developer.yahoo.net/yui/license.txt
3196  * <script type="text/javascript">
3197  *
3198  */
3199 //@@dep Roo.lib.Region
3200
3201
3202 Roo.lib.Point = function(x, y) {
3203     if (x instanceof Array) {
3204         y = x[1];
3205         x = x[0];
3206     }
3207     this.x = this.right = this.left = this[0] = x;
3208     this.y = this.top = this.bottom = this[1] = y;
3209 };
3210
3211 Roo.lib.Point.prototype = new Roo.lib.Region();
3212 /*
3213  * Portions of this file are based on pieces of Yahoo User Interface Library
3214  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3215  * YUI licensed under the BSD License:
3216  * http://developer.yahoo.net/yui/license.txt
3217  * <script type="text/javascript">
3218  *
3219  */
3220  
3221 (function() {   
3222
3223     Roo.lib.Anim = {
3224         scroll : function(el, args, duration, easing, cb, scope) {
3225             this.run(el, args, duration, easing, cb, scope, Roo.lib.Scroll);
3226         },
3227
3228         motion : function(el, args, duration, easing, cb, scope) {
3229             this.run(el, args, duration, easing, cb, scope, Roo.lib.Motion);
3230         },
3231
3232         color : function(el, args, duration, easing, cb, scope) {
3233             this.run(el, args, duration, easing, cb, scope, Roo.lib.ColorAnim);
3234         },
3235
3236         run : function(el, args, duration, easing, cb, scope, type) {
3237             type = type || Roo.lib.AnimBase;
3238             if (typeof easing == "string") {
3239                 easing = Roo.lib.Easing[easing];
3240             }
3241             var anim = new type(el, args, duration, easing);
3242             anim.animateX(function() {
3243                 Roo.callback(cb, scope);
3244             });
3245             return anim;
3246         }
3247     };
3248 })();/*
3249  * Portions of this file are based on pieces of Yahoo User Interface Library
3250  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3251  * YUI licensed under the BSD License:
3252  * http://developer.yahoo.net/yui/license.txt
3253  * <script type="text/javascript">
3254  *
3255  */
3256
3257 (function() {    
3258     var libFlyweight;
3259     
3260     function fly(el) {
3261         if (!libFlyweight) {
3262             libFlyweight = new Roo.Element.Flyweight();
3263         }
3264         libFlyweight.dom = el;
3265         return libFlyweight;
3266     }
3267
3268     // since this uses fly! - it cant be in DOM (which does not have fly yet..)
3269     
3270    
3271     
3272     Roo.lib.AnimBase = function(el, attributes, duration, method) {
3273         if (el) {
3274             this.init(el, attributes, duration, method);
3275         }
3276     };
3277
3278     Roo.lib.AnimBase.fly = fly;
3279     
3280     
3281     
3282     Roo.lib.AnimBase.prototype = {
3283
3284         toString: function() {
3285             var el = this.getEl();
3286             var id = el.id || el.tagName;
3287             return ("Anim " + id);
3288         },
3289
3290         patterns: {
3291             noNegatives:        /width|height|opacity|padding/i,
3292             offsetAttribute:  /^((width|height)|(top|left))$/,
3293             defaultUnit:        /width|height|top$|bottom$|left$|right$/i,
3294             offsetUnit:         /\d+(em|%|en|ex|pt|in|cm|mm|pc)$/i
3295         },
3296
3297
3298         doMethod: function(attr, start, end) {
3299             return this.method(this.currentFrame, start, end - start, this.totalFrames);
3300         },
3301
3302
3303         setAttribute: function(attr, val, unit) {
3304             if (this.patterns.noNegatives.test(attr)) {
3305                 val = (val > 0) ? val : 0;
3306             }
3307
3308             Roo.fly(this.getEl(), '_anim').setStyle(attr, val + unit);
3309         },
3310
3311
3312         getAttribute: function(attr) {
3313             var el = this.getEl();
3314             var val = fly(el).getStyle(attr);
3315
3316             if (val !== 'auto' && !this.patterns.offsetUnit.test(val)) {
3317                 return parseFloat(val);
3318             }
3319
3320             var a = this.patterns.offsetAttribute.exec(attr) || [];
3321             var pos = !!( a[3] );
3322             var box = !!( a[2] );
3323
3324
3325             if (box || (fly(el).getStyle('position') == 'absolute' && pos)) {
3326                 val = el['offset' + a[0].charAt(0).toUpperCase() + a[0].substr(1)];
3327             } else {
3328                 val = 0;
3329             }
3330
3331             return val;
3332         },
3333
3334
3335         getDefaultUnit: function(attr) {
3336             if (this.patterns.defaultUnit.test(attr)) {
3337                 return 'px';
3338             }
3339
3340             return '';
3341         },
3342
3343         animateX : function(callback, scope) {
3344             var f = function() {
3345                 this.onComplete.removeListener(f);
3346                 if (typeof callback == "function") {
3347                     callback.call(scope || this, this);
3348                 }
3349             };
3350             this.onComplete.addListener(f, this);
3351             this.animate();
3352         },
3353
3354
3355         setRuntimeAttribute: function(attr) {
3356             var start;
3357             var end;
3358             var attributes = this.attributes;
3359
3360             this.runtimeAttributes[attr] = {};
3361
3362             var isset = function(prop) {
3363                 return (typeof prop !== 'undefined');
3364             };
3365
3366             if (!isset(attributes[attr]['to']) && !isset(attributes[attr]['by'])) {
3367                 return false;
3368             }
3369
3370             start = ( isset(attributes[attr]['from']) ) ? attributes[attr]['from'] : this.getAttribute(attr);
3371
3372
3373             if (isset(attributes[attr]['to'])) {
3374                 end = attributes[attr]['to'];
3375             } else if (isset(attributes[attr]['by'])) {
3376                 if (start.constructor == Array) {
3377                     end = [];
3378                     for (var i = 0, len = start.length; i < len; ++i) {
3379                         end[i] = start[i] + attributes[attr]['by'][i];
3380                     }
3381                 } else {
3382                     end = start + attributes[attr]['by'];
3383                 }
3384             }
3385
3386             this.runtimeAttributes[attr].start = start;
3387             this.runtimeAttributes[attr].end = end;
3388
3389
3390             this.runtimeAttributes[attr].unit = ( isset(attributes[attr].unit) ) ? attributes[attr]['unit'] : this.getDefaultUnit(attr);
3391         },
3392
3393
3394         init: function(el, attributes, duration, method) {
3395
3396             var isAnimated = false;
3397
3398
3399             var startTime = null;
3400
3401
3402             var actualFrames = 0;
3403
3404
3405             el = Roo.getDom(el);
3406
3407
3408             this.attributes = attributes || {};
3409
3410
3411             this.duration = duration || 1;
3412
3413
3414             this.method = method || Roo.lib.Easing.easeNone;
3415
3416
3417             this.useSeconds = true;
3418
3419
3420             this.currentFrame = 0;
3421
3422
3423             this.totalFrames = Roo.lib.AnimMgr.fps;
3424
3425
3426             this.getEl = function() {
3427                 return el;
3428             };
3429
3430
3431             this.isAnimated = function() {
3432                 return isAnimated;
3433             };
3434
3435
3436             this.getStartTime = function() {
3437                 return startTime;
3438             };
3439
3440             this.runtimeAttributes = {};
3441
3442
3443             this.animate = function() {
3444                 if (this.isAnimated()) {
3445                     return false;
3446                 }
3447
3448                 this.currentFrame = 0;
3449
3450                 this.totalFrames = ( this.useSeconds ) ? Math.ceil(Roo.lib.AnimMgr.fps * this.duration) : this.duration;
3451
3452                 Roo.lib.AnimMgr.registerElement(this);
3453             };
3454
3455
3456             this.stop = function(finish) {
3457                 if (finish) {
3458                     this.currentFrame = this.totalFrames;
3459                     this._onTween.fire();
3460                 }
3461                 Roo.lib.AnimMgr.stop(this);
3462             };
3463
3464             var onStart = function() {
3465                 this.onStart.fire();
3466
3467                 this.runtimeAttributes = {};
3468                 for (var attr in this.attributes) {
3469                     this.setRuntimeAttribute(attr);
3470                 }
3471
3472                 isAnimated = true;
3473                 actualFrames = 0;
3474                 startTime = new Date();
3475             };
3476
3477
3478             var onTween = function() {
3479                 var data = {
3480                     duration: new Date() - this.getStartTime(),
3481                     currentFrame: this.currentFrame
3482                 };
3483
3484                 data.toString = function() {
3485                     return (
3486                             'duration: ' + data.duration +
3487                             ', currentFrame: ' + data.currentFrame
3488                             );
3489                 };
3490
3491                 this.onTween.fire(data);
3492
3493                 var runtimeAttributes = this.runtimeAttributes;
3494
3495                 for (var attr in runtimeAttributes) {
3496                     this.setAttribute(attr, this.doMethod(attr, runtimeAttributes[attr].start, runtimeAttributes[attr].end), runtimeAttributes[attr].unit);
3497                 }
3498
3499                 actualFrames += 1;
3500             };
3501
3502             var onComplete = function() {
3503                 var actual_duration = (new Date() - startTime) / 1000 ;
3504
3505                 var data = {
3506                     duration: actual_duration,
3507                     frames: actualFrames,
3508                     fps: actualFrames / actual_duration
3509                 };
3510
3511                 data.toString = function() {
3512                     return (
3513                             'duration: ' + data.duration +
3514                             ', frames: ' + data.frames +
3515                             ', fps: ' + data.fps
3516                             );
3517                 };
3518
3519                 isAnimated = false;
3520                 actualFrames = 0;
3521                 this.onComplete.fire(data);
3522             };
3523
3524
3525             this._onStart = new Roo.util.Event(this);
3526             this.onStart = new Roo.util.Event(this);
3527             this.onTween = new Roo.util.Event(this);
3528             this._onTween = new Roo.util.Event(this);
3529             this.onComplete = new Roo.util.Event(this);
3530             this._onComplete = new Roo.util.Event(this);
3531             this._onStart.addListener(onStart);
3532             this._onTween.addListener(onTween);
3533             this._onComplete.addListener(onComplete);
3534         }
3535     };
3536 })();
3537 /*
3538  * Portions of this file are based on pieces of Yahoo User Interface Library
3539  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3540  * YUI licensed under the BSD License:
3541  * http://developer.yahoo.net/yui/license.txt
3542  * <script type="text/javascript">
3543  *
3544  */
3545
3546 Roo.lib.AnimMgr = new function() {
3547
3548     var thread = null;
3549
3550
3551     var queue = [];
3552
3553
3554     var tweenCount = 0;
3555
3556
3557     this.fps = 1000;
3558
3559
3560     this.delay = 1;
3561
3562
3563     this.registerElement = function(tween) {
3564         queue[queue.length] = tween;
3565         tweenCount += 1;
3566         tween._onStart.fire();
3567         this.start();
3568     };
3569
3570
3571     this.unRegister = function(tween, index) {
3572         tween._onComplete.fire();
3573         index = index || getIndex(tween);
3574         if (index != -1) {
3575             queue.splice(index, 1);
3576         }
3577
3578         tweenCount -= 1;
3579         if (tweenCount <= 0) {
3580             this.stop();
3581         }
3582     };
3583
3584
3585     this.start = function() {
3586         if (thread === null) {
3587             thread = setInterval(this.run, this.delay);
3588         }
3589     };
3590
3591
3592     this.stop = function(tween) {
3593         if (!tween) {
3594             clearInterval(thread);
3595
3596             for (var i = 0, len = queue.length; i < len; ++i) {
3597                 if (queue[0].isAnimated()) {
3598                     this.unRegister(queue[0], 0);
3599                 }
3600             }
3601
3602             queue = [];
3603             thread = null;
3604             tweenCount = 0;
3605         }
3606         else {
3607             this.unRegister(tween);
3608         }
3609     };
3610
3611
3612     this.run = function() {
3613         for (var i = 0, len = queue.length; i < len; ++i) {
3614             var tween = queue[i];
3615             if (!tween || !tween.isAnimated()) {
3616                 continue;
3617             }
3618
3619             if (tween.currentFrame < tween.totalFrames || tween.totalFrames === null)
3620             {
3621                 tween.currentFrame += 1;
3622
3623                 if (tween.useSeconds) {
3624                     correctFrame(tween);
3625                 }
3626                 tween._onTween.fire();
3627             }
3628             else {
3629                 Roo.lib.AnimMgr.stop(tween, i);
3630             }
3631         }
3632     };
3633
3634     var getIndex = function(anim) {
3635         for (var i = 0, len = queue.length; i < len; ++i) {
3636             if (queue[i] == anim) {
3637                 return i;
3638             }
3639         }
3640         return -1;
3641     };
3642
3643
3644     var correctFrame = function(tween) {
3645         var frames = tween.totalFrames;
3646         var frame = tween.currentFrame;
3647         var expected = (tween.currentFrame * tween.duration * 1000 / tween.totalFrames);
3648         var elapsed = (new Date() - tween.getStartTime());
3649         var tweak = 0;
3650
3651         if (elapsed < tween.duration * 1000) {
3652             tweak = Math.round((elapsed / expected - 1) * tween.currentFrame);
3653         } else {
3654             tweak = frames - (frame + 1);
3655         }
3656         if (tweak > 0 && isFinite(tweak)) {
3657             if (tween.currentFrame + tweak >= frames) {
3658                 tweak = frames - (frame + 1);
3659             }
3660
3661             tween.currentFrame += tweak;
3662         }
3663     };
3664 };
3665
3666     /*
3667  * Portions of this file are based on pieces of Yahoo User Interface Library
3668  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
3669  * YUI licensed under the BSD License:
3670  * http://developer.yahoo.net/yui/license.txt
3671  * <script type="text/javascript">
3672  *
3673  */
3674 Roo.lib.Bezier = new function() {
3675
3676         this.getPosition = function(points, t) {
3677             var n = points.length;
3678             var tmp = [];
3679
3680             for (var i = 0; i < n; ++i) {
3681                 tmp[i] = [points[i][0], points[i][1]];
3682             }
3683
3684             for (var j = 1; j < n; ++j) {
3685                 for (i = 0; i < n - j; ++i) {
3686                     tmp[i][0] = (1 - t) * tmp[i][0] + t * tmp[parseInt(i + 1, 10)][0];
3687                     tmp[i][1] = (1 - t) * tmp[i][1] + t * tmp[parseInt(i + 1, 10)][1];
3688                 }
3689             }
3690
3691             return [ tmp[0][0], tmp[0][1] ];
3692
3693         };
3694     }; 
3695
3696 /**
3697  * @class Roo.lib.Color
3698  * @constructor
3699  * An abstract Color implementation. Concrete Color implementations should use
3700  * an instance of this function as their prototype, and implement the getRGB and
3701  * getHSL functions. getRGB should return an object representing the RGB
3702  * components of this Color, with the red, green, and blue components in the
3703  * range [0,255] and the alpha component in the range [0,100]. getHSL should
3704  * return an object representing the HSL components of this Color, with the hue
3705  * component in the range [0,360), the saturation and lightness components in
3706  * the range [0,100], and the alpha component in the range [0,1].
3707  *
3708  *
3709  * Color.js
3710  *
3711  * Functions for Color handling and processing.
3712  *
3713  * http://www.safalra.com/web-design/javascript/Color-handling-and-processing/
3714  *
3715  * The author of this program, Safalra (Stephen Morley), irrevocably releases all
3716  * rights to this program, with the intention of it becoming part of the public
3717  * domain. Because this program is released into the public domain, it comes with
3718  * no warranty either expressed or implied, to the extent permitted by law.
3719  * 
3720  * For more free and public domain JavaScript code by the same author, visit:
3721  * http://www.safalra.com/web-design/javascript/
3722  * 
3723  */
3724 Roo.lib.Color = function() { }
3725
3726
3727 Roo.apply(Roo.lib.Color.prototype, {
3728   
3729   rgb : null,
3730   hsv : null,
3731   hsl : null,
3732   
3733   /**
3734    * getIntegerRGB
3735    * @return {Object} an object representing the RGBA components of this Color. The red,
3736    * green, and blue components are converted to integers in the range [0,255].
3737    * The alpha is a value in the range [0,1].
3738    */
3739   getIntegerRGB : function(){
3740
3741     // get the RGB components of this Color
3742     var rgb = this.getRGB();
3743
3744     // return the integer components
3745     return {
3746       'r' : Math.round(rgb.r),
3747       'g' : Math.round(rgb.g),
3748       'b' : Math.round(rgb.b),
3749       'a' : rgb.a
3750     };
3751
3752   },
3753
3754   /**
3755    * getPercentageRGB
3756    * @return {Object} an object representing the RGBA components of this Color. The red,
3757    * green, and blue components are converted to numbers in the range [0,100].
3758    * The alpha is a value in the range [0,1].
3759    */
3760   getPercentageRGB : function(){
3761
3762     // get the RGB components of this Color
3763     var rgb = this.getRGB();
3764
3765     // return the percentage components
3766     return {
3767       'r' : 100 * rgb.r / 255,
3768       'g' : 100 * rgb.g / 255,
3769       'b' : 100 * rgb.b / 255,
3770       'a' : rgb.a
3771     };
3772
3773   },
3774
3775   /**
3776    * getCSSHexadecimalRGB
3777    * @return {String} a string representing this Color as a CSS hexadecimal RGB Color
3778    * value - that is, a string of the form #RRGGBB where each of RR, GG, and BB
3779    * are two-digit hexadecimal numbers.
3780    */
3781   getCSSHexadecimalRGB : function()
3782   {
3783
3784     // get the integer RGB components
3785     var rgb = this.getIntegerRGB();
3786
3787     // determine the hexadecimal equivalents
3788     var r16 = rgb.r.toString(16);
3789     var g16 = rgb.g.toString(16);
3790     var b16 = rgb.b.toString(16);
3791
3792     // return the CSS RGB Color value
3793     return '#'
3794         + (r16.length == 2 ? r16 : '0' + r16)
3795         + (g16.length == 2 ? g16 : '0' + g16)
3796         + (b16.length == 2 ? b16 : '0' + b16);
3797
3798   },
3799
3800   /**
3801    * getCSSIntegerRGB
3802    * @return {String} a string representing this Color as a CSS integer RGB Color
3803    * value - that is, a string of the form rgb(r,g,b) where each of r, g, and b
3804    * are integers in the range [0,255].
3805    */
3806   getCSSIntegerRGB : function(){
3807
3808     // get the integer RGB components
3809     var rgb = this.getIntegerRGB();
3810
3811     // return the CSS RGB Color value
3812     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ')';
3813
3814   },
3815
3816   /**
3817    * getCSSIntegerRGBA
3818    * @return {String} Returns a string representing this Color as a CSS integer RGBA Color
3819    * value - that is, a string of the form rgba(r,g,b,a) where each of r, g, and
3820    * b are integers in the range [0,255] and a is in the range [0,1].
3821    */
3822   getCSSIntegerRGBA : function(){
3823
3824     // get the integer RGB components
3825     var rgb = this.getIntegerRGB();
3826
3827     // return the CSS integer RGBA Color value
3828     return 'rgb(' + rgb.r + ',' + rgb.g + ',' + rgb.b + ',' + rgb.a + ')';
3829
3830   },
3831
3832   /**
3833    * getCSSPercentageRGB
3834    * @return {String} a string representing this Color as a CSS percentage RGB Color
3835    * value - that is, a string of the form rgb(r%,g%,b%) where each of r, g, and
3836    * b are in the range [0,100].
3837    */
3838   getCSSPercentageRGB : function(){
3839
3840     // get the percentage RGB components
3841     var rgb = this.getPercentageRGB();
3842
3843     // return the CSS RGB Color value
3844     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%)';
3845
3846   },
3847
3848   /**
3849    * getCSSPercentageRGBA
3850    * @return {String} a string representing this Color as a CSS percentage RGBA Color
3851    * value - that is, a string of the form rgba(r%,g%,b%,a) where each of r, g,
3852    * and b are in the range [0,100] and a is in the range [0,1].
3853    */
3854   getCSSPercentageRGBA : function(){
3855
3856     // get the percentage RGB components
3857     var rgb = this.getPercentageRGB();
3858
3859     // return the CSS percentage RGBA Color value
3860     return 'rgb(' + rgb.r + '%,' + rgb.g + '%,' + rgb.b + '%,' + rgb.a + ')';
3861
3862   },
3863
3864   /**
3865    * getCSSHSL
3866    * @return {String} a string representing this Color as a CSS HSL Color value - that
3867    * is, a string of the form hsl(h,s%,l%) where h is in the range [0,100] and
3868    * s and l are in the range [0,100].
3869    */
3870   getCSSHSL : function(){
3871
3872     // get the HSL components
3873     var hsl = this.getHSL();
3874
3875     // return the CSS HSL Color value
3876     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%)';
3877
3878   },
3879
3880   /**
3881    * getCSSHSLA
3882    * @return {String} a string representing this Color as a CSS HSLA Color value - that
3883    * is, a string of the form hsla(h,s%,l%,a) where h is in the range [0,100],
3884    * s and l are in the range [0,100], and a is in the range [0,1].
3885    */
3886   getCSSHSLA : function(){
3887
3888     // get the HSL components
3889     var hsl = this.getHSL();
3890
3891     // return the CSS HSL Color value
3892     return 'hsl(' + hsl.h + ',' + hsl.s + '%,' + hsl.l + '%,' + hsl.a + ')';
3893
3894   },
3895
3896   /**
3897    * Sets the Color of the specified node to this Color. This functions sets
3898    * the CSS 'color' property for the node. The parameter is:
3899    * 
3900    * @param {DomElement} node - the node whose Color should be set
3901    */
3902   setNodeColor : function(node){
3903
3904     // set the Color of the node
3905     node.style.color = this.getCSSHexadecimalRGB();
3906
3907   },
3908
3909   /**
3910    * Sets the background Color of the specified node to this Color. This
3911    * functions sets the CSS 'background-color' property for the node. The
3912    * parameter is:
3913    *
3914    * @param {DomElement} node - the node whose background Color should be set
3915    */
3916   setNodeBackgroundColor : function(node){
3917
3918     // set the background Color of the node
3919     node.style.backgroundColor = this.getCSSHexadecimalRGB();
3920
3921   },
3922   // convert between formats..
3923   toRGB: function()
3924   {
3925     var r = this.getIntegerRGB();
3926     return new Roo.lib.RGBColor(r.r,r.g,r.b,r.a);
3927     
3928   },
3929   toHSL : function()
3930   {
3931      var hsl = this.getHSL();
3932   // return the CSS HSL Color value
3933     return new Roo.lib.HSLColor(hsl.h,  hsl.s, hsl.l ,  hsl.a );
3934     
3935   },
3936   
3937   toHSV : function()
3938   {
3939     var rgb = this.toRGB();
3940     var hsv = rgb.getHSV();
3941    // return the CSS HSL Color value
3942     return new Roo.lib.HSVColor(hsv.h,  hsv.s, hsv.v ,  hsv.a );
3943     
3944   },
3945   
3946   // modify  v = 0 ... 1 (eg. 0.5)
3947   saturate : function(v)
3948   {
3949       var rgb = this.toRGB();
3950       var hsv = rgb.getHSV();
3951       return new Roo.lib.HSVColor(hsv.h,  hsv.s * v, hsv.v ,  hsv.a );
3952       
3953   
3954   },
3955   
3956    
3957   /**
3958    * getRGB
3959    * @return {Object} the RGB and alpha components of this Color as an object with r,
3960    * g, b, and a properties. r, g, and b are in the range [0,255] and a is in
3961    * the range [0,1].
3962    */
3963   getRGB: function(){
3964    
3965     // return the RGB components
3966     return {
3967       'r' : this.rgb.r,
3968       'g' : this.rgb.g,
3969       'b' : this.rgb.b,
3970       'a' : this.alpha
3971     };
3972
3973   },
3974
3975   /**
3976    * getHSV
3977    * @return {Object} the HSV and alpha components of this Color as an object with h,
3978    * s, v, and a properties. h is in the range [0,360), s and v are in the range
3979    * [0,100], and a is in the range [0,1].
3980    */
3981   getHSV : function()
3982   {
3983     
3984     // calculate the HSV components if necessary
3985     if (this.hsv == null) {
3986       this.calculateHSV();
3987     }
3988
3989     // return the HSV components
3990     return {
3991       'h' : this.hsv.h,
3992       's' : this.hsv.s,
3993       'v' : this.hsv.v,
3994       'a' : this.alpha
3995     };
3996
3997   },
3998
3999   /**
4000    * getHSL
4001    * @return {Object} the HSL and alpha components of this Color as an object with h,
4002    * s, l, and a properties. h is in the range [0,360), s and l are in the range
4003    * [0,100], and a is in the range [0,1].
4004    */
4005   getHSL : function(){
4006     
4007      
4008     // calculate the HSV components if necessary
4009     if (this.hsl == null) { this.calculateHSL(); }
4010
4011     // return the HSL components
4012     return {
4013       'h' : this.hsl.h,
4014       's' : this.hsl.s,
4015       'l' : this.hsl.l,
4016       'a' : this.alpha
4017     };
4018
4019   }
4020   
4021
4022 });
4023
4024
4025 /**
4026  * @class Roo.lib.RGBColor
4027  * @extends Roo.lib.Color
4028  * Creates a Color specified in the RGB Color space, with an optional alpha
4029  * component. The parameters are:
4030  * @constructor
4031  * 
4032
4033  * @param {Number} r - the red component, clipped to the range [0,255]
4034  * @param {Number} g - the green component, clipped to the range [0,255]
4035  * @param {Number} b - the blue component, clipped to the range [0,255]
4036  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4037  *     optional and defaults to 1
4038  */
4039 Roo.lib.RGBColor = function (r, g, b, a){
4040
4041   // store the alpha component after clipping it if necessary
4042   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4043
4044   // store the RGB components after clipping them if necessary
4045   this.rgb =
4046       {
4047         'r' : Math.max(0, Math.min(255, r)),
4048         'g' : Math.max(0, Math.min(255, g)),
4049         'b' : Math.max(0, Math.min(255, b))
4050       };
4051
4052   // initialise the HSV and HSL components to null
4053   
4054
4055   /* 
4056    * //private returns the HSV or HSL hue component of this RGBColor. The hue is in the
4057    * range [0,360). The parameters are:
4058    *
4059    * maximum - the maximum of the RGB component values
4060    * range   - the range of the RGB component values
4061    */
4062    
4063
4064 }
4065 // this does an 'exteds'
4066 Roo.extend(Roo.lib.RGBColor, Roo.lib.Color, {
4067
4068   
4069     getHue  : function(maximum, range)
4070     {
4071       var rgb = this.rgb;
4072        
4073       // check whether the range is zero
4074       if (range == 0){
4075   
4076         // set the hue to zero (any hue is acceptable as the Color is grey)
4077         var hue = 0;
4078   
4079       }else{
4080   
4081         // determine which of the components has the highest value and set the hue
4082         switch (maximum){
4083   
4084           // red has the highest value
4085           case rgb.r:
4086             var hue = (rgb.g - rgb.b) / range * 60;
4087             if (hue < 0) { hue += 360; }
4088             break;
4089   
4090           // green has the highest value
4091           case rgb.g:
4092             var hue = (rgb.b - rgb.r) / range * 60 + 120;
4093             break;
4094   
4095           // blue has the highest value
4096           case rgb.b:
4097             var hue = (rgb.r - rgb.g) / range * 60 + 240;
4098             break;
4099   
4100         }
4101   
4102       }
4103   
4104       // return the hue
4105       return hue;
4106   
4107     },
4108
4109   /* //private Calculates and stores the HSV components of this RGBColor so that they can
4110    * be returned be the getHSV function.
4111    */
4112    calculateHSV : function(){
4113     var rgb = this.rgb;
4114     // get the maximum and range of the RGB component values
4115     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4116     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4117
4118     // store the HSV components
4119     this.hsv =
4120         {
4121           'h' : this.getHue(maximum, range),
4122           's' : (maximum == 0 ? 0 : 100 * range / maximum),
4123           'v' : maximum / 2.55
4124         };
4125
4126   },
4127
4128   /* //private Calculates and stores the HSL components of this RGBColor so that they can
4129    * be returned be the getHSL function.
4130    */
4131    calculateHSL : function(){
4132     var rgb = this.rgb;
4133     // get the maximum and range of the RGB component values
4134     var maximum = Math.max(rgb.r, rgb.g, rgb.b);
4135     var range   = maximum - Math.min(rgb.r, rgb.g, rgb.b);
4136
4137     // determine the lightness in the range [0,1]
4138     var l = maximum / 255 - range / 510;
4139
4140     // store the HSL components
4141     this.hsl =
4142         {
4143           'h' : this.getHue(maximum, range),
4144           's' : (range == 0 ? 0 : range / 2.55 / (l < 0.5 ? l * 2 : 2 - l * 2)),
4145           'l' : 100 * l
4146         };
4147
4148   }
4149
4150 });
4151
4152 /**
4153  * @class Roo.lib.HSVColor
4154  * @extends Roo.lib.Color
4155  * Creates a Color specified in the HSV Color space, with an optional alpha
4156  * component. The parameters are:
4157  * @constructor
4158  *
4159  * @param {Number} h - the hue component, wrapped to the range [0,360)
4160  * @param {Number} s - the saturation component, clipped to the range [0,100]
4161  * @param {Number} v - the value component, clipped to the range [0,100]
4162  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4163  *     optional and defaults to 1
4164  */
4165 Roo.lib.HSVColor = function (h, s, v, a){
4166
4167   // store the alpha component after clipping it if necessary
4168   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4169
4170   // store the HSV components after clipping or wrapping them if necessary
4171   this.hsv =
4172       {
4173         'h' : (h % 360 + 360) % 360,
4174         's' : Math.max(0, Math.min(100, s)),
4175         'v' : Math.max(0, Math.min(100, v))
4176       };
4177
4178   // initialise the RGB and HSL components to null
4179   this.rgb = null;
4180   this.hsl = null;
4181 }
4182
4183 Roo.extend(Roo.lib.HSVColor, Roo.lib.Color, {
4184   /* Calculates and stores the RGB components of this HSVColor so that they can
4185    * be returned be the getRGB function.
4186    */
4187   calculateRGB: function ()
4188   {
4189     var hsv = this.hsv;
4190     // check whether the saturation is zero
4191     if (hsv.s == 0){
4192
4193       // set the Color to the appropriate shade of grey
4194       var r = hsv.v;
4195       var g = hsv.v;
4196       var b = hsv.v;
4197
4198     }else{
4199
4200       // set some temporary values
4201       var f  = hsv.h / 60 - Math.floor(hsv.h / 60);
4202       var p  = hsv.v * (1 - hsv.s / 100);
4203       var q  = hsv.v * (1 - hsv.s / 100 * f);
4204       var t  = hsv.v * (1 - hsv.s / 100 * (1 - f));
4205
4206       // set the RGB Color components to their temporary values
4207       switch (Math.floor(hsv.h / 60)){
4208         case 0: var r = hsv.v; var g = t; var b = p; break;
4209         case 1: var r = q; var g = hsv.v; var b = p; break;
4210         case 2: var r = p; var g = hsv.v; var b = t; break;
4211         case 3: var r = p; var g = q; var b = hsv.v; break;
4212         case 4: var r = t; var g = p; var b = hsv.v; break;
4213         case 5: var r = hsv.v; var g = p; var b = q; break;
4214       }
4215
4216     }
4217
4218     // store the RGB components
4219     this.rgb =
4220         {
4221           'r' : r * 2.55,
4222           'g' : g * 2.55,
4223           'b' : b * 2.55
4224         };
4225
4226   },
4227
4228   /* Calculates and stores the HSL components of this HSVColor so that they can
4229    * be returned be the getHSL function.
4230    */
4231   calculateHSL : function (){
4232
4233     var hsv = this.hsv;
4234     // determine the lightness in the range [0,100]
4235     var l = (2 - hsv.s / 100) * hsv.v / 2;
4236
4237     // store the HSL components
4238     this.hsl =
4239         {
4240           'h' : hsv.h,
4241           's' : hsv.s * hsv.v / (l < 50 ? l * 2 : 200 - l * 2),
4242           'l' : l
4243         };
4244
4245     // correct a division-by-zero error
4246     if (isNaN(hsl.s)) { hsl.s = 0; }
4247
4248   } 
4249  
4250
4251 });
4252  
4253
4254 /**
4255  * @class Roo.lib.HSLColor
4256  * @extends Roo.lib.Color
4257  *
4258  * @constructor
4259  * Creates a Color specified in the HSL Color space, with an optional alpha
4260  * component. The parameters are:
4261  *
4262  * @param {Number} h - the hue component, wrapped to the range [0,360)
4263  * @param {Number} s - the saturation component, clipped to the range [0,100]
4264  * @param {Number} l - the lightness component, clipped to the range [0,100]
4265  * @param {Number} a - the alpha component, clipped to the range [0,1] - this parameter is
4266  *     optional and defaults to 1
4267  */
4268
4269 Roo.lib.HSLColor = function(h, s, l, a){
4270
4271   // store the alpha component after clipping it if necessary
4272   this.alpha = (a === undefined ? 1 : Math.max(0, Math.min(1, a)));
4273
4274   // store the HSL components after clipping or wrapping them if necessary
4275   this.hsl =
4276       {
4277         'h' : (h % 360 + 360) % 360,
4278         's' : Math.max(0, Math.min(100, s)),
4279         'l' : Math.max(0, Math.min(100, l))
4280       };
4281
4282   // initialise the RGB and HSV components to null
4283 }
4284
4285 Roo.extend(Roo.lib.HSLColor, Roo.lib.Color, {
4286
4287   /* Calculates and stores the RGB components of this HSLColor so that they can
4288    * be returned be the getRGB function.
4289    */
4290   calculateRGB: function (){
4291
4292     // check whether the saturation is zero
4293     if (this.hsl.s == 0){
4294
4295       // store the RGB components representing the appropriate shade of grey
4296       this.rgb =
4297           {
4298             'r' : this.hsl.l * 2.55,
4299             'g' : this.hsl.l * 2.55,
4300             'b' : this.hsl.l * 2.55
4301           };
4302
4303     }else{
4304
4305       // set some temporary values
4306       var p = this.hsl.l < 50
4307             ? this.hsl.l * (1 + hsl.s / 100)
4308             : this.hsl.l + hsl.s - hsl.l * hsl.s / 100;
4309       var q = 2 * hsl.l - p;
4310
4311       // initialise the RGB components
4312       this.rgb =
4313           {
4314             'r' : (h + 120) / 60 % 6,
4315             'g' : h / 60,
4316             'b' : (h + 240) / 60 % 6
4317           };
4318
4319       // loop over the RGB components
4320       for (var key in this.rgb){
4321
4322         // ensure that the property is not inherited from the root object
4323         if (this.rgb.hasOwnProperty(key)){
4324
4325           // set the component to its value in the range [0,100]
4326           if (this.rgb[key] < 1){
4327             this.rgb[key] = q + (p - q) * this.rgb[key];
4328           }else if (this.rgb[key] < 3){
4329             this.rgb[key] = p;
4330           }else if (this.rgb[key] < 4){
4331             this.rgb[key] = q + (p - q) * (4 - this.rgb[key]);
4332           }else{
4333             this.rgb[key] = q;
4334           }
4335
4336           // set the component to its value in the range [0,255]
4337           this.rgb[key] *= 2.55;
4338
4339         }
4340
4341       }
4342
4343     }
4344
4345   },
4346
4347   /* Calculates and stores the HSV components of this HSLColor so that they can
4348    * be returned be the getHSL function.
4349    */
4350    calculateHSV : function(){
4351
4352     // set a temporary value
4353     var t = this.hsl.s * (this.hsl.l < 50 ? this.hsl.l : 100 - this.hsl.l) / 100;
4354
4355     // store the HSV components
4356     this.hsv =
4357         {
4358           'h' : this.hsl.h,
4359           's' : 200 * t / (this.hsl.l + t),
4360           'v' : t + this.hsl.l
4361         };
4362
4363     // correct a division-by-zero error
4364     if (isNaN(this.hsv.s)) { this.hsv.s = 0; }
4365
4366   }
4367  
4368
4369 });
4370 /*
4371  * Portions of this file are based on pieces of Yahoo User Interface Library
4372  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4373  * YUI licensed under the BSD License:
4374  * http://developer.yahoo.net/yui/license.txt
4375  * <script type="text/javascript">
4376  *
4377  */
4378 (function() {
4379
4380     Roo.lib.ColorAnim = function(el, attributes, duration, method) {
4381         Roo.lib.ColorAnim.superclass.constructor.call(this, el, attributes, duration, method);
4382     };
4383
4384     Roo.extend(Roo.lib.ColorAnim, Roo.lib.AnimBase);
4385
4386     var fly = Roo.lib.AnimBase.fly;
4387     var Y = Roo.lib;
4388     var superclass = Y.ColorAnim.superclass;
4389     var proto = Y.ColorAnim.prototype;
4390
4391     proto.toString = function() {
4392         var el = this.getEl();
4393         var id = el.id || el.tagName;
4394         return ("ColorAnim " + id);
4395     };
4396
4397     proto.patterns.color = /color$/i;
4398     proto.patterns.rgb = /^rgb\(([0-9]+)\s*,\s*([0-9]+)\s*,\s*([0-9]+)\)$/i;
4399     proto.patterns.hex = /^#?([0-9A-F]{2})([0-9A-F]{2})([0-9A-F]{2})$/i;
4400     proto.patterns.hex3 = /^#?([0-9A-F]{1})([0-9A-F]{1})([0-9A-F]{1})$/i;
4401     proto.patterns.transparent = /^transparent|rgba\(0, 0, 0, 0\)$/;
4402
4403
4404     proto.parseColor = function(s) {
4405         if (s.length == 3) {
4406             return s;
4407         }
4408
4409         var c = this.patterns.hex.exec(s);
4410         if (c && c.length == 4) {
4411             return [ parseInt(c[1], 16), parseInt(c[2], 16), parseInt(c[3], 16) ];
4412         }
4413
4414         c = this.patterns.rgb.exec(s);
4415         if (c && c.length == 4) {
4416             return [ parseInt(c[1], 10), parseInt(c[2], 10), parseInt(c[3], 10) ];
4417         }
4418
4419         c = this.patterns.hex3.exec(s);
4420         if (c && c.length == 4) {
4421             return [ parseInt(c[1] + c[1], 16), parseInt(c[2] + c[2], 16), parseInt(c[3] + c[3], 16) ];
4422         }
4423
4424         return null;
4425     };
4426     // since this uses fly! - it cant be in ColorAnim (which does not have fly yet..)
4427     proto.getAttribute = function(attr) {
4428         var el = this.getEl();
4429         if (this.patterns.color.test(attr)) {
4430             var val = fly(el).getStyle(attr);
4431
4432             if (this.patterns.transparent.test(val)) {
4433                 var parent = el.parentNode;
4434                 val = fly(parent).getStyle(attr);
4435
4436                 while (parent && this.patterns.transparent.test(val)) {
4437                     parent = parent.parentNode;
4438                     val = fly(parent).getStyle(attr);
4439                     if (parent.tagName.toUpperCase() == 'HTML') {
4440                         val = '#fff';
4441                     }
4442                 }
4443             }
4444         } else {
4445             val = superclass.getAttribute.call(this, attr);
4446         }
4447
4448         return val;
4449     };
4450     proto.getAttribute = function(attr) {
4451         var el = this.getEl();
4452         if (this.patterns.color.test(attr)) {
4453             var val = fly(el).getStyle(attr);
4454
4455             if (this.patterns.transparent.test(val)) {
4456                 var parent = el.parentNode;
4457                 val = fly(parent).getStyle(attr);
4458
4459                 while (parent && this.patterns.transparent.test(val)) {
4460                     parent = parent.parentNode;
4461                     val = fly(parent).getStyle(attr);
4462                     if (parent.tagName.toUpperCase() == 'HTML') {
4463                         val = '#fff';
4464                     }
4465                 }
4466             }
4467         } else {
4468             val = superclass.getAttribute.call(this, attr);
4469         }
4470
4471         return val;
4472     };
4473
4474     proto.doMethod = function(attr, start, end) {
4475         var val;
4476
4477         if (this.patterns.color.test(attr)) {
4478             val = [];
4479             for (var i = 0, len = start.length; i < len; ++i) {
4480                 val[i] = superclass.doMethod.call(this, attr, start[i], end[i]);
4481             }
4482
4483             val = 'rgb(' + Math.floor(val[0]) + ',' + Math.floor(val[1]) + ',' + Math.floor(val[2]) + ')';
4484         }
4485         else {
4486             val = superclass.doMethod.call(this, attr, start, end);
4487         }
4488
4489         return val;
4490     };
4491
4492     proto.setRuntimeAttribute = function(attr) {
4493         superclass.setRuntimeAttribute.call(this, attr);
4494
4495         if (this.patterns.color.test(attr)) {
4496             var attributes = this.attributes;
4497             var start = this.parseColor(this.runtimeAttributes[attr].start);
4498             var end = this.parseColor(this.runtimeAttributes[attr].end);
4499
4500             if (typeof attributes[attr]['to'] === 'undefined' && typeof attributes[attr]['by'] !== 'undefined') {
4501                 end = this.parseColor(attributes[attr].by);
4502
4503                 for (var i = 0, len = start.length; i < len; ++i) {
4504                     end[i] = start[i] + end[i];
4505                 }
4506             }
4507
4508             this.runtimeAttributes[attr].start = start;
4509             this.runtimeAttributes[attr].end = end;
4510         }
4511     };
4512 })();
4513
4514 /*
4515  * Portions of this file are based on pieces of Yahoo User Interface Library
4516  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4517  * YUI licensed under the BSD License:
4518  * http://developer.yahoo.net/yui/license.txt
4519  * <script type="text/javascript">
4520  *
4521  */
4522 Roo.lib.Easing = {
4523
4524
4525     easeNone: function (t, b, c, d) {
4526         return c * t / d + b;
4527     },
4528
4529
4530     easeIn: function (t, b, c, d) {
4531         return c * (t /= d) * t + b;
4532     },
4533
4534
4535     easeOut: function (t, b, c, d) {
4536         return -c * (t /= d) * (t - 2) + b;
4537     },
4538
4539
4540     easeBoth: function (t, b, c, d) {
4541         if ((t /= d / 2) < 1) {
4542             return c / 2 * t * t + b;
4543         }
4544
4545         return -c / 2 * ((--t) * (t - 2) - 1) + b;
4546     },
4547
4548
4549     easeInStrong: function (t, b, c, d) {
4550         return c * (t /= d) * t * t * t + b;
4551     },
4552
4553
4554     easeOutStrong: function (t, b, c, d) {
4555         return -c * ((t = t / d - 1) * t * t * t - 1) + b;
4556     },
4557
4558
4559     easeBothStrong: function (t, b, c, d) {
4560         if ((t /= d / 2) < 1) {
4561             return c / 2 * t * t * t * t + b;
4562         }
4563
4564         return -c / 2 * ((t -= 2) * t * t * t - 2) + b;
4565     },
4566
4567
4568
4569     elasticIn: function (t, b, c, d, a, p) {
4570         if (t == 0) {
4571             return b;
4572         }
4573         if ((t /= d) == 1) {
4574             return b + c;
4575         }
4576         if (!p) {
4577             p = d * .3;
4578         }
4579
4580         if (!a || a < Math.abs(c)) {
4581             a = c;
4582             var s = p / 4;
4583         }
4584         else {
4585             var s = p / (2 * Math.PI) * Math.asin(c / a);
4586         }
4587
4588         return -(a * Math.pow(2, 10 * (t -= 1)) * Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4589     },
4590
4591
4592     elasticOut: function (t, b, c, d, a, p) {
4593         if (t == 0) {
4594             return b;
4595         }
4596         if ((t /= d) == 1) {
4597             return b + c;
4598         }
4599         if (!p) {
4600             p = d * .3;
4601         }
4602
4603         if (!a || a < Math.abs(c)) {
4604             a = c;
4605             var s = p / 4;
4606         }
4607         else {
4608             var s = p / (2 * Math.PI) * Math.asin(c / a);
4609         }
4610
4611         return a * Math.pow(2, -10 * t) * Math.sin((t * d - s) * (2 * Math.PI) / p) + c + b;
4612     },
4613
4614
4615     elasticBoth: function (t, b, c, d, a, p) {
4616         if (t == 0) {
4617             return b;
4618         }
4619
4620         if ((t /= d / 2) == 2) {
4621             return b + c;
4622         }
4623
4624         if (!p) {
4625             p = d * (.3 * 1.5);
4626         }
4627
4628         if (!a || a < Math.abs(c)) {
4629             a = c;
4630             var s = p / 4;
4631         }
4632         else {
4633             var s = p / (2 * Math.PI) * Math.asin(c / a);
4634         }
4635
4636         if (t < 1) {
4637             return -.5 * (a * Math.pow(2, 10 * (t -= 1)) *
4638                           Math.sin((t * d - s) * (2 * Math.PI) / p)) + b;
4639         }
4640         return a * Math.pow(2, -10 * (t -= 1)) *
4641                Math.sin((t * d - s) * (2 * Math.PI) / p) * .5 + c + b;
4642     },
4643
4644
4645
4646     backIn: function (t, b, c, d, s) {
4647         if (typeof s == 'undefined') {
4648             s = 1.70158;
4649         }
4650         return c * (t /= d) * t * ((s + 1) * t - s) + b;
4651     },
4652
4653
4654     backOut: function (t, b, c, d, s) {
4655         if (typeof s == 'undefined') {
4656             s = 1.70158;
4657         }
4658         return c * ((t = t / d - 1) * t * ((s + 1) * t + s) + 1) + b;
4659     },
4660
4661
4662     backBoth: function (t, b, c, d, s) {
4663         if (typeof s == 'undefined') {
4664             s = 1.70158;
4665         }
4666
4667         if ((t /= d / 2 ) < 1) {
4668             return c / 2 * (t * t * (((s *= (1.525)) + 1) * t - s)) + b;
4669         }
4670         return c / 2 * ((t -= 2) * t * (((s *= (1.525)) + 1) * t + s) + 2) + b;
4671     },
4672
4673
4674     bounceIn: function (t, b, c, d) {
4675         return c - Roo.lib.Easing.bounceOut(d - t, 0, c, d) + b;
4676     },
4677
4678
4679     bounceOut: function (t, b, c, d) {
4680         if ((t /= d) < (1 / 2.75)) {
4681             return c * (7.5625 * t * t) + b;
4682         } else if (t < (2 / 2.75)) {
4683             return c * (7.5625 * (t -= (1.5 / 2.75)) * t + .75) + b;
4684         } else if (t < (2.5 / 2.75)) {
4685             return c * (7.5625 * (t -= (2.25 / 2.75)) * t + .9375) + b;
4686         }
4687         return c * (7.5625 * (t -= (2.625 / 2.75)) * t + .984375) + b;
4688     },
4689
4690
4691     bounceBoth: function (t, b, c, d) {
4692         if (t < d / 2) {
4693             return Roo.lib.Easing.bounceIn(t * 2, 0, c, d) * .5 + b;
4694         }
4695         return Roo.lib.Easing.bounceOut(t * 2 - d, 0, c, d) * .5 + c * .5 + b;
4696     }
4697 };/*
4698  * Portions of this file are based on pieces of Yahoo User Interface Library
4699  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4700  * YUI licensed under the BSD License:
4701  * http://developer.yahoo.net/yui/license.txt
4702  * <script type="text/javascript">
4703  *
4704  */
4705     (function() {
4706         Roo.lib.Motion = function(el, attributes, duration, method) {
4707             if (el) {
4708                 Roo.lib.Motion.superclass.constructor.call(this, el, attributes, duration, method);
4709             }
4710         };
4711
4712         Roo.extend(Roo.lib.Motion, Roo.lib.ColorAnim);
4713
4714
4715         var Y = Roo.lib;
4716         var superclass = Y.Motion.superclass;
4717         var proto = Y.Motion.prototype;
4718
4719         proto.toString = function() {
4720             var el = this.getEl();
4721             var id = el.id || el.tagName;
4722             return ("Motion " + id);
4723         };
4724
4725         proto.patterns.points = /^points$/i;
4726
4727         proto.setAttribute = function(attr, val, unit) {
4728             if (this.patterns.points.test(attr)) {
4729                 unit = unit || 'px';
4730                 superclass.setAttribute.call(this, 'left', val[0], unit);
4731                 superclass.setAttribute.call(this, 'top', val[1], unit);
4732             } else {
4733                 superclass.setAttribute.call(this, attr, val, unit);
4734             }
4735         };
4736
4737         proto.getAttribute = function(attr) {
4738             if (this.patterns.points.test(attr)) {
4739                 var val = [
4740                         superclass.getAttribute.call(this, 'left'),
4741                         superclass.getAttribute.call(this, 'top')
4742                         ];
4743             } else {
4744                 val = superclass.getAttribute.call(this, attr);
4745             }
4746
4747             return val;
4748         };
4749
4750         proto.doMethod = function(attr, start, end) {
4751             var val = null;
4752
4753             if (this.patterns.points.test(attr)) {
4754                 var t = this.method(this.currentFrame, 0, 100, this.totalFrames) / 100;
4755                 val = Y.Bezier.getPosition(this.runtimeAttributes[attr], t);
4756             } else {
4757                 val = superclass.doMethod.call(this, attr, start, end);
4758             }
4759             return val;
4760         };
4761
4762         proto.setRuntimeAttribute = function(attr) {
4763             if (this.patterns.points.test(attr)) {
4764                 var el = this.getEl();
4765                 var attributes = this.attributes;
4766                 var start;
4767                 var control = attributes['points']['control'] || [];
4768                 var end;
4769                 var i, len;
4770
4771                 if (control.length > 0 && !(control[0] instanceof Array)) {
4772                     control = [control];
4773                 } else {
4774                     var tmp = [];
4775                     for (i = 0,len = control.length; i < len; ++i) {
4776                         tmp[i] = control[i];
4777                     }
4778                     control = tmp;
4779                 }
4780
4781                 Roo.fly(el).position();
4782
4783                 if (isset(attributes['points']['from'])) {
4784                     Roo.lib.Dom.setXY(el, attributes['points']['from']);
4785                 }
4786                 else {
4787                     Roo.lib.Dom.setXY(el, Roo.lib.Dom.getXY(el));
4788                 }
4789
4790                 start = this.getAttribute('points');
4791
4792
4793                 if (isset(attributes['points']['to'])) {
4794                     end = translateValues.call(this, attributes['points']['to'], start);
4795
4796                     var pageXY = Roo.lib.Dom.getXY(this.getEl());
4797                     for (i = 0,len = control.length; i < len; ++i) {
4798                         control[i] = translateValues.call(this, control[i], start);
4799                     }
4800
4801
4802                 } else if (isset(attributes['points']['by'])) {
4803                     end = [ start[0] + attributes['points']['by'][0], start[1] + attributes['points']['by'][1] ];
4804
4805                     for (i = 0,len = control.length; i < len; ++i) {
4806                         control[i] = [ start[0] + control[i][0], start[1] + control[i][1] ];
4807                     }
4808                 }
4809
4810                 this.runtimeAttributes[attr] = [start];
4811
4812                 if (control.length > 0) {
4813                     this.runtimeAttributes[attr] = this.runtimeAttributes[attr].concat(control);
4814                 }
4815
4816                 this.runtimeAttributes[attr][this.runtimeAttributes[attr].length] = end;
4817             }
4818             else {
4819                 superclass.setRuntimeAttribute.call(this, attr);
4820             }
4821         };
4822
4823         var translateValues = function(val, start) {
4824             var pageXY = Roo.lib.Dom.getXY(this.getEl());
4825             val = [ val[0] - pageXY[0] + start[0], val[1] - pageXY[1] + start[1] ];
4826
4827             return val;
4828         };
4829
4830         var isset = function(prop) {
4831             return (typeof prop !== 'undefined');
4832         };
4833     })();
4834 /*
4835  * Portions of this file are based on pieces of Yahoo User Interface Library
4836  * Copyright (c) 2007, Yahoo! Inc. All rights reserved.
4837  * YUI licensed under the BSD License:
4838  * http://developer.yahoo.net/yui/license.txt
4839  * <script type="text/javascript">
4840  *
4841  */
4842     (function() {
4843         Roo.lib.Scroll = function(el, attributes, duration, method) {
4844             if (el) {
4845                 Roo.lib.Scroll.superclass.constructor.call(this, el, attributes, duration, method);
4846             }
4847         };
4848
4849         Roo.extend(Roo.lib.Scroll, Roo.lib.ColorAnim);
4850
4851
4852         var Y = Roo.lib;
4853         var superclass = Y.Scroll.superclass;
4854         var proto = Y.Scroll.prototype;
4855
4856         proto.toString = function() {
4857             var el = this.getEl();
4858             var id = el.id || el.tagName;
4859             return ("Scroll " + id);
4860         };
4861
4862         proto.doMethod = function(attr, start, end) {
4863             var val = null;
4864
4865             if (attr == 'scroll') {
4866                 val = [
4867                         this.method(this.currentFrame, start[0], end[0] - start[0], this.totalFrames),
4868                         this.method(this.currentFrame, start[1], end[1] - start[1], this.totalFrames)
4869                         ];
4870
4871             } else {
4872                 val = superclass.doMethod.call(this, attr, start, end);
4873             }
4874             return val;
4875         };
4876
4877         proto.getAttribute = function(attr) {
4878             var val = null;
4879             var el = this.getEl();
4880
4881             if (attr == 'scroll') {
4882                 val = [ el.scrollLeft, el.scrollTop ];
4883             } else {
4884                 val = superclass.getAttribute.call(this, attr);
4885             }
4886
4887             return val;
4888         };
4889
4890         proto.setAttribute = function(attr, val, unit) {
4891             var el = this.getEl();
4892
4893             if (attr == 'scroll') {
4894                 el.scrollLeft = val[0];
4895                 el.scrollTop = val[1];
4896             } else {
4897                 superclass.setAttribute.call(this, attr, val, unit);
4898             }
4899         };
4900     })();
4901 /*
4902  * Based on:
4903  * Ext JS Library 1.1.1
4904  * Copyright(c) 2006-2007, Ext JS, LLC.
4905  *
4906  * Originally Released Under LGPL - original licence link has changed is not relivant.
4907  *
4908  * Fork - LGPL
4909  * <script type="text/javascript">
4910  */
4911
4912
4913 // nasty IE9 hack - what a pile of crap that is..
4914
4915  if (typeof Range != "undefined" && typeof Range.prototype.createContextualFragment == "undefined") {
4916     Range.prototype.createContextualFragment = function (html) {
4917         var doc = window.document;
4918         var container = doc.createElement("div");
4919         container.innerHTML = html;
4920         var frag = doc.createDocumentFragment(), n;
4921         while ((n = container.firstChild)) {
4922             frag.appendChild(n);
4923         }
4924         return frag;
4925     };
4926 }
4927
4928 /**
4929  * @class Roo.DomHelper
4930  * Utility class for working with DOM and/or Templates. It transparently supports using HTML fragments or DOM.
4931  * 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>.
4932  * @static
4933  */
4934 Roo.DomHelper = function(){
4935     var tempTableEl = null;
4936     var emptyTags = /^(?:br|frame|hr|img|input|link|meta|range|spacer|wbr|area|param|col)$/i;
4937     var tableRe = /^table|tbody|tr|td$/i;
4938     var xmlns = {};
4939     // build as innerHTML where available
4940     /** @ignore */
4941     var createHtml = function(o){
4942         if(typeof o == 'string'){
4943             return o;
4944         }
4945         var b = "";
4946         if(!o.tag){
4947             o.tag = "div";
4948         }
4949         b += "<" + o.tag;
4950         for(var attr in o){
4951             if(attr == "tag" || attr == "children" || attr == "cn" || attr == "html" || typeof o[attr] == "function") { continue; }
4952             if(attr == "style"){
4953                 var s = o["style"];
4954                 if(typeof s == "function"){
4955                     s = s.call();
4956                 }
4957                 if(typeof s == "string"){
4958                     b += ' style="' + s + '"';
4959                 }else if(typeof s == "object"){
4960                     b += ' style="';
4961                     for(var key in s){
4962                         if(typeof s[key] != "function"){
4963                             b += key + ":" + s[key] + ";";
4964                         }
4965                     }
4966                     b += '"';
4967                 }
4968             }else{
4969                 if(attr == "cls"){
4970                     b += ' class="' + o["cls"] + '"';
4971                 }else if(attr == "htmlFor"){
4972                     b += ' for="' + o["htmlFor"] + '"';
4973                 }else{
4974                     b += " " + attr + '="' + o[attr] + '"';
4975                 }
4976             }
4977         }
4978         if(emptyTags.test(o.tag)){
4979             b += "/>";
4980         }else{
4981             b += ">";
4982             var cn = o.children || o.cn;
4983             if(cn){
4984                 //http://bugs.kde.org/show_bug.cgi?id=71506
4985                 if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
4986                     for(var i = 0, len = cn.length; i < len; i++) {
4987                         b += createHtml(cn[i], b);
4988                     }
4989                 }else{
4990                     b += createHtml(cn, b);
4991                 }
4992             }
4993             if(o.html){
4994                 b += o.html;
4995             }
4996             b += "</" + o.tag + ">";
4997         }
4998         return b;
4999     };
5000
5001     // build as dom
5002     /** @ignore */
5003     var createDom = function(o, parentNode){
5004          
5005         // defininition craeted..
5006         var ns = false;
5007         if (o.ns && o.ns != 'html') {
5008                
5009             if (o.xmlns && typeof(xmlns[o.ns]) == 'undefined') {
5010                 xmlns[o.ns] = o.xmlns;
5011                 ns = o.xmlns;
5012             }
5013             if (typeof(xmlns[o.ns]) == 'undefined') {
5014                 console.log("Trying to create namespace element " + o.ns + ", however no xmlns was sent to builder previously");
5015             }
5016             ns = xmlns[o.ns];
5017         }
5018         
5019         
5020         if (typeof(o) == 'string') {
5021             return parentNode.appendChild(document.createTextNode(o));
5022         }
5023         o.tag = o.tag || div;
5024         if (o.ns && Roo.isIE) {
5025             ns = false;
5026             o.tag = o.ns + ':' + o.tag;
5027             
5028         }
5029         var el = ns ? document.createElementNS( ns, o.tag||'div') :  document.createElement(o.tag||'div');
5030         var useSet = el.setAttribute ? true : false; // In IE some elements don't have setAttribute
5031         for(var attr in o){
5032             
5033             if(attr == "tag" || attr == "ns" ||attr == "xmlns" ||attr == "children" || attr == "cn" || attr == "html" || 
5034                     attr == "style" || typeof o[attr] == "function") { continue; }
5035                     
5036             if(attr=="cls" && Roo.isIE){
5037                 el.className = o["cls"];
5038             }else{
5039                 if(useSet) { el.setAttribute(attr=="cls" ? 'class' : attr, o[attr]);}
5040                 else { 
5041                     el[attr] = o[attr];
5042                 }
5043             }
5044         }
5045         Roo.DomHelper.applyStyles(el, o.style);
5046         var cn = o.children || o.cn;
5047         if(cn){
5048             //http://bugs.kde.org/show_bug.cgi?id=71506
5049              if((cn instanceof Array) || (Roo.isSafari && typeof(cn.join) == "function")){
5050                 for(var i = 0, len = cn.length; i < len; i++) {
5051                     createDom(cn[i], el);
5052                 }
5053             }else{
5054                 createDom(cn, el);
5055             }
5056         }
5057         if(o.html){
5058             el.innerHTML = o.html;
5059         }
5060         if(parentNode){
5061            parentNode.appendChild(el);
5062         }
5063         return el;
5064     };
5065
5066     var ieTable = function(depth, s, h, e){
5067         tempTableEl.innerHTML = [s, h, e].join('');
5068         var i = -1, el = tempTableEl;
5069         while(++i < depth && el.firstChild){
5070             el = el.firstChild;
5071         }
5072         return el;
5073     };
5074
5075     // kill repeat to save bytes
5076     var ts = '<table>',
5077         te = '</table>',
5078         tbs = ts+'<tbody>',
5079         tbe = '</tbody>'+te,
5080         trs = tbs + '<tr>',
5081         tre = '</tr>'+tbe;
5082
5083     /**
5084      * @ignore
5085      * Nasty code for IE's broken table implementation
5086      */
5087     var insertIntoTable = function(tag, where, el, html){
5088         if(!tempTableEl){
5089             tempTableEl = document.createElement('div');
5090         }
5091         var node;
5092         var before = null;
5093         if(tag == 'td'){
5094             if(where == 'afterbegin' || where == 'beforeend'){ // INTO a TD
5095                 return;
5096             }
5097             if(where == 'beforebegin'){
5098                 before = el;
5099                 el = el.parentNode;
5100             } else{
5101                 before = el.nextSibling;
5102                 el = el.parentNode;
5103             }
5104             node = ieTable(4, trs, html, tre);
5105         }
5106         else if(tag == 'tr'){
5107             if(where == 'beforebegin'){
5108                 before = el;
5109                 el = el.parentNode;
5110                 node = ieTable(3, tbs, html, tbe);
5111             } else if(where == 'afterend'){
5112                 before = el.nextSibling;
5113                 el = el.parentNode;
5114                 node = ieTable(3, tbs, html, tbe);
5115             } else{ // INTO a TR
5116                 if(where == 'afterbegin'){
5117                     before = el.firstChild;
5118                 }
5119                 node = ieTable(4, trs, html, tre);
5120             }
5121         } else if(tag == 'tbody'){
5122             if(where == 'beforebegin'){
5123                 before = el;
5124                 el = el.parentNode;
5125                 node = ieTable(2, ts, html, te);
5126             } else if(where == 'afterend'){
5127                 before = el.nextSibling;
5128                 el = el.parentNode;
5129                 node = ieTable(2, ts, html, te);
5130             } else{
5131                 if(where == 'afterbegin'){
5132                     before = el.firstChild;
5133                 }
5134                 node = ieTable(3, tbs, html, tbe);
5135             }
5136         } else{ // TABLE
5137             if(where == 'beforebegin' || where == 'afterend'){ // OUTSIDE the table
5138                 return;
5139             }
5140             if(where == 'afterbegin'){
5141                 before = el.firstChild;
5142             }
5143             node = ieTable(2, ts, html, te);
5144         }
5145         el.insertBefore(node, before);
5146         return node;
5147     };
5148
5149     return {
5150     /** True to force the use of DOM instead of html fragments @type Boolean */
5151     useDom : false,
5152
5153     /**
5154      * Returns the markup for the passed Element(s) config
5155      * @param {Object} o The Dom object spec (and children)
5156      * @return {String}
5157      */
5158     markup : function(o){
5159         return createHtml(o);
5160     },
5161
5162     /**
5163      * Applies a style specification to an element
5164      * @param {String/HTMLElement} el The element to apply styles to
5165      * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5166      * a function which returns such a specification.
5167      */
5168     applyStyles : function(el, styles){
5169         if(styles){
5170            el = Roo.fly(el);
5171            if(typeof styles == "string"){
5172                var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5173                var matches;
5174                while ((matches = re.exec(styles)) != null){
5175                    el.setStyle(matches[1], matches[2]);
5176                }
5177            }else if (typeof styles == "object"){
5178                for (var style in styles){
5179                   el.setStyle(style, styles[style]);
5180                }
5181            }else if (typeof styles == "function"){
5182                 Roo.DomHelper.applyStyles(el, styles.call());
5183            }
5184         }
5185     },
5186
5187     /**
5188      * Inserts an HTML fragment into the Dom
5189      * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5190      * @param {HTMLElement} el The context element
5191      * @param {String} html The HTML fragmenet
5192      * @return {HTMLElement} The new node
5193      */
5194     insertHtml : function(where, el, html){
5195         where = where.toLowerCase();
5196         if(el.insertAdjacentHTML){
5197             if(tableRe.test(el.tagName)){
5198                 var rs;
5199                 if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5200                     return rs;
5201                 }
5202             }
5203             switch(where){
5204                 case "beforebegin":
5205                     el.insertAdjacentHTML('BeforeBegin', html);
5206                     return el.previousSibling;
5207                 case "afterbegin":
5208                     el.insertAdjacentHTML('AfterBegin', html);
5209                     return el.firstChild;
5210                 case "beforeend":
5211                     el.insertAdjacentHTML('BeforeEnd', html);
5212                     return el.lastChild;
5213                 case "afterend":
5214                     el.insertAdjacentHTML('AfterEnd', html);
5215                     return el.nextSibling;
5216             }
5217             throw 'Illegal insertion point -> "' + where + '"';
5218         }
5219         var range = el.ownerDocument.createRange();
5220         var frag;
5221         switch(where){
5222              case "beforebegin":
5223                 range.setStartBefore(el);
5224                 frag = range.createContextualFragment(html);
5225                 el.parentNode.insertBefore(frag, el);
5226                 return el.previousSibling;
5227              case "afterbegin":
5228                 if(el.firstChild){
5229                     range.setStartBefore(el.firstChild);
5230                     frag = range.createContextualFragment(html);
5231                     el.insertBefore(frag, el.firstChild);
5232                     return el.firstChild;
5233                 }else{
5234                     el.innerHTML = html;
5235                     return el.firstChild;
5236                 }
5237             case "beforeend":
5238                 if(el.lastChild){
5239                     range.setStartAfter(el.lastChild);
5240                     frag = range.createContextualFragment(html);
5241                     el.appendChild(frag);
5242                     return el.lastChild;
5243                 }else{
5244                     el.innerHTML = html;
5245                     return el.lastChild;
5246                 }
5247             case "afterend":
5248                 range.setStartAfter(el);
5249                 frag = range.createContextualFragment(html);
5250                 el.parentNode.insertBefore(frag, el.nextSibling);
5251                 return el.nextSibling;
5252             }
5253             throw 'Illegal insertion point -> "' + where + '"';
5254     },
5255
5256     /**
5257      * Creates new Dom element(s) and inserts them before el
5258      * @param {String/HTMLElement/Element} el The context element
5259      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5260      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5261      * @return {HTMLElement/Roo.Element} The new node
5262      */
5263     insertBefore : function(el, o, returnElement){
5264         return this.doInsert(el, o, returnElement, "beforeBegin");
5265     },
5266
5267     /**
5268      * Creates new Dom element(s) and inserts them after el
5269      * @param {String/HTMLElement/Element} el The context element
5270      * @param {Object} o The Dom object spec (and children)
5271      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5272      * @return {HTMLElement/Roo.Element} The new node
5273      */
5274     insertAfter : function(el, o, returnElement){
5275         return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5276     },
5277
5278     /**
5279      * Creates new Dom element(s) and inserts them as the first child of el
5280      * @param {String/HTMLElement/Element} el The context element
5281      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5282      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5283      * @return {HTMLElement/Roo.Element} The new node
5284      */
5285     insertFirst : function(el, o, returnElement){
5286         return this.doInsert(el, o, returnElement, "afterBegin");
5287     },
5288
5289     // private
5290     doInsert : function(el, o, returnElement, pos, sibling){
5291         el = Roo.getDom(el);
5292         var newNode;
5293         if(this.useDom || o.ns){
5294             newNode = createDom(o, null);
5295             el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5296         }else{
5297             var html = createHtml(o);
5298             newNode = this.insertHtml(pos, el, html);
5299         }
5300         return returnElement ? Roo.get(newNode, true) : newNode;
5301     },
5302
5303     /**
5304      * Creates new Dom element(s) and appends them to el
5305      * @param {String/HTMLElement/Element} el The context element
5306      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5307      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5308      * @return {HTMLElement/Roo.Element} The new node
5309      */
5310     append : function(el, o, returnElement){
5311         el = Roo.getDom(el);
5312         var newNode;
5313         if(this.useDom || o.ns){
5314             newNode = createDom(o, null);
5315             el.appendChild(newNode);
5316         }else{
5317             var html = createHtml(o);
5318             newNode = this.insertHtml("beforeEnd", el, html);
5319         }
5320         return returnElement ? Roo.get(newNode, true) : newNode;
5321     },
5322
5323     /**
5324      * Creates new Dom element(s) and overwrites the contents of el with them
5325      * @param {String/HTMLElement/Element} el The context element
5326      * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5327      * @param {Boolean} returnElement (optional) true to return a Roo.Element
5328      * @return {HTMLElement/Roo.Element} The new node
5329      */
5330     overwrite : function(el, o, returnElement){
5331         el = Roo.getDom(el);
5332         if (o.ns) {
5333           
5334             while (el.childNodes.length) {
5335                 el.removeChild(el.firstChild);
5336             }
5337             createDom(o, el);
5338         } else {
5339             el.innerHTML = createHtml(o);   
5340         }
5341         
5342         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5343     },
5344
5345     /**
5346      * Creates a new Roo.DomHelper.Template from the Dom object spec
5347      * @param {Object} o The Dom object spec (and children)
5348      * @return {Roo.DomHelper.Template} The new template
5349      */
5350     createTemplate : function(o){
5351         var html = createHtml(o);
5352         return new Roo.Template(html);
5353     }
5354     };
5355 }();
5356 /*
5357  * Based on:
5358  * Ext JS Library 1.1.1
5359  * Copyright(c) 2006-2007, Ext JS, LLC.
5360  *
5361  * Originally Released Under LGPL - original licence link has changed is not relivant.
5362  *
5363  * Fork - LGPL
5364  * <script type="text/javascript">
5365  */
5366  
5367 /**
5368 * @class Roo.Template
5369 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5370 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5371 * Usage:
5372 <pre><code>
5373 var t = new Roo.Template({
5374     html :  '&lt;div name="{id}"&gt;' + 
5375         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5376         '&lt;/div&gt;',
5377     myformat: function (value, allValues) {
5378         return 'XX' + value;
5379     }
5380 });
5381 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5382 </code></pre>
5383 * For more information see this blog post with examples:
5384 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5385      - Create Elements using DOM, HTML fragments and Templates</a>. 
5386 * @constructor
5387 * @param {Object} cfg - Configuration object.
5388 */
5389 Roo.Template = function(cfg){
5390     // BC!
5391     if(cfg instanceof Array){
5392         cfg = cfg.join("");
5393     }else if(arguments.length > 1){
5394         cfg = Array.prototype.join.call(arguments, "");
5395     }
5396     
5397     
5398     if (typeof(cfg) == 'object') {
5399         Roo.apply(this,cfg)
5400     } else {
5401         // bc
5402         this.html = cfg;
5403     }
5404     if (this.url) {
5405         this.load();
5406     }
5407     
5408 };
5409 Roo.Template.prototype = {
5410     
5411     /**
5412      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5413      */
5414     onLoad : false,
5415     
5416     
5417     /**
5418      * @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..
5419      *                    it should be fixed so that template is observable...
5420      */
5421     url : false,
5422     /**
5423      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5424      */
5425     html : '',
5426     
5427     
5428     compiled : false,
5429     loaded : false,
5430     /**
5431      * Returns an HTML fragment of this template with the specified values applied.
5432      * @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'})
5433      * @return {String} The HTML fragment
5434      */
5435     
5436    
5437     
5438     applyTemplate : function(values){
5439         //Roo.log(["applyTemplate", values]);
5440         try {
5441            
5442             if(this.compiled){
5443                 return this.compiled(values);
5444             }
5445             var useF = this.disableFormats !== true;
5446             var fm = Roo.util.Format, tpl = this;
5447             var fn = function(m, name, format, args){
5448                 if(format && useF){
5449                     if(format.substr(0, 5) == "this."){
5450                         return tpl.call(format.substr(5), values[name], values);
5451                     }else{
5452                         if(args){
5453                             // quoted values are required for strings in compiled templates, 
5454                             // but for non compiled we need to strip them
5455                             // quoted reversed for jsmin
5456                             var re = /^\s*['"](.*)["']\s*$/;
5457                             args = args.split(',');
5458                             for(var i = 0, len = args.length; i < len; i++){
5459                                 args[i] = args[i].replace(re, "$1");
5460                             }
5461                             args = [values[name]].concat(args);
5462                         }else{
5463                             args = [values[name]];
5464                         }
5465                         return fm[format].apply(fm, args);
5466                     }
5467                 }else{
5468                     return values[name] !== undefined ? values[name] : "";
5469                 }
5470             };
5471             return this.html.replace(this.re, fn);
5472         } catch (e) {
5473             Roo.log(e);
5474             throw e;
5475         }
5476          
5477     },
5478     
5479     loading : false,
5480       
5481     load : function ()
5482     {
5483          
5484         if (this.loading) {
5485             return;
5486         }
5487         var _t = this;
5488         
5489         this.loading = true;
5490         this.compiled = false;
5491         
5492         var cx = new Roo.data.Connection();
5493         cx.request({
5494             url : this.url,
5495             method : 'GET',
5496             success : function (response) {
5497                 _t.loading = false;
5498                 _t.url = false;
5499                 
5500                 _t.set(response.responseText,true);
5501                 _t.loaded = true;
5502                 if (_t.onLoad) {
5503                     _t.onLoad();
5504                 }
5505              },
5506             failure : function(response) {
5507                 Roo.log("Template failed to load from " + _t.url);
5508                 _t.loading = false;
5509             }
5510         });
5511     },
5512
5513     /**
5514      * Sets the HTML used as the template and optionally compiles it.
5515      * @param {String} html
5516      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5517      * @return {Roo.Template} this
5518      */
5519     set : function(html, compile){
5520         this.html = html;
5521         this.compiled = false;
5522         if(compile){
5523             this.compile();
5524         }
5525         return this;
5526     },
5527     
5528     /**
5529      * True to disable format functions (defaults to false)
5530      * @type Boolean
5531      */
5532     disableFormats : false,
5533     
5534     /**
5535     * The regular expression used to match template variables 
5536     * @type RegExp
5537     * @property 
5538     */
5539     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5540     
5541     /**
5542      * Compiles the template into an internal function, eliminating the RegEx overhead.
5543      * @return {Roo.Template} this
5544      */
5545     compile : function(){
5546         var fm = Roo.util.Format;
5547         var useF = this.disableFormats !== true;
5548         var sep = Roo.isGecko ? "+" : ",";
5549         var fn = function(m, name, format, args){
5550             if(format && useF){
5551                 args = args ? ',' + args : "";
5552                 if(format.substr(0, 5) != "this."){
5553                     format = "fm." + format + '(';
5554                 }else{
5555                     format = 'this.call("'+ format.substr(5) + '", ';
5556                     args = ", values";
5557                 }
5558             }else{
5559                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5560             }
5561             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5562         };
5563         var body;
5564         // branched to use + in gecko and [].join() in others
5565         if(Roo.isGecko){
5566             body = "this.compiled = function(values){ return '" +
5567                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5568                     "';};";
5569         }else{
5570             body = ["this.compiled = function(values){ return ['"];
5571             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5572             body.push("'].join('');};");
5573             body = body.join('');
5574         }
5575         /**
5576          * eval:var:values
5577          * eval:var:fm
5578          */
5579         eval(body);
5580         return this;
5581     },
5582     
5583     // private function used to call members
5584     call : function(fnName, value, allValues){
5585         return this[fnName](value, allValues);
5586     },
5587     
5588     /**
5589      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5590      * @param {String/HTMLElement/Roo.Element} el The context element
5591      * @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'})
5592      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5593      * @return {HTMLElement/Roo.Element} The new node or Element
5594      */
5595     insertFirst: function(el, values, returnElement){
5596         return this.doInsert('afterBegin', el, values, returnElement);
5597     },
5598
5599     /**
5600      * Applies the supplied values to the template and inserts the new node(s) before el.
5601      * @param {String/HTMLElement/Roo.Element} el The context element
5602      * @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'})
5603      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5604      * @return {HTMLElement/Roo.Element} The new node or Element
5605      */
5606     insertBefore: function(el, values, returnElement){
5607         return this.doInsert('beforeBegin', el, values, returnElement);
5608     },
5609
5610     /**
5611      * Applies the supplied values to the template and inserts the new node(s) after el.
5612      * @param {String/HTMLElement/Roo.Element} el The context element
5613      * @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'})
5614      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5615      * @return {HTMLElement/Roo.Element} The new node or Element
5616      */
5617     insertAfter : function(el, values, returnElement){
5618         return this.doInsert('afterEnd', el, values, returnElement);
5619     },
5620     
5621     /**
5622      * Applies the supplied values to the template and appends the new node(s) to el.
5623      * @param {String/HTMLElement/Roo.Element} el The context element
5624      * @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'})
5625      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5626      * @return {HTMLElement/Roo.Element} The new node or Element
5627      */
5628     append : function(el, values, returnElement){
5629         return this.doInsert('beforeEnd', el, values, returnElement);
5630     },
5631
5632     doInsert : function(where, el, values, returnEl){
5633         el = Roo.getDom(el);
5634         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5635         return returnEl ? Roo.get(newNode, true) : newNode;
5636     },
5637
5638     /**
5639      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5640      * @param {String/HTMLElement/Roo.Element} el The context element
5641      * @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'})
5642      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5643      * @return {HTMLElement/Roo.Element} The new node or Element
5644      */
5645     overwrite : function(el, values, returnElement){
5646         el = Roo.getDom(el);
5647         el.innerHTML = this.applyTemplate(values);
5648         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5649     }
5650 };
5651 /**
5652  * Alias for {@link #applyTemplate}
5653  * @method
5654  */
5655 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5656
5657 // backwards compat
5658 Roo.DomHelper.Template = Roo.Template;
5659
5660 /**
5661  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5662  * @param {String/HTMLElement} el A DOM element or its id
5663  * @returns {Roo.Template} The created template
5664  * @static
5665  */
5666 Roo.Template.from = function(el){
5667     el = Roo.getDom(el);
5668     return new Roo.Template(el.value || el.innerHTML);
5669 };/*
5670  * Based on:
5671  * Ext JS Library 1.1.1
5672  * Copyright(c) 2006-2007, Ext JS, LLC.
5673  *
5674  * Originally Released Under LGPL - original licence link has changed is not relivant.
5675  *
5676  * Fork - LGPL
5677  * <script type="text/javascript">
5678  */
5679  
5680
5681 /*
5682  * This is code is also distributed under MIT license for use
5683  * with jQuery and prototype JavaScript libraries.
5684  */
5685 /**
5686  * @class Roo.DomQuery
5687 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).
5688 <p>
5689 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>
5690
5691 <p>
5692 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.
5693 </p>
5694 <h4>Element Selectors:</h4>
5695 <ul class="list">
5696     <li> <b>*</b> any element</li>
5697     <li> <b>E</b> an element with the tag E</li>
5698     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5699     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5700     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5701     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5702 </ul>
5703 <h4>Attribute Selectors:</h4>
5704 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5705 <ul class="list">
5706     <li> <b>E[foo]</b> has an attribute "foo"</li>
5707     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5708     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5709     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5710     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5711     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5712     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5713 </ul>
5714 <h4>Pseudo Classes:</h4>
5715 <ul class="list">
5716     <li> <b>E:first-child</b> E is the first child of its parent</li>
5717     <li> <b>E:last-child</b> E is the last child of its parent</li>
5718     <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>
5719     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5720     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5721     <li> <b>E:only-child</b> E is the only child of its parent</li>
5722     <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>
5723     <li> <b>E:first</b> the first E in the resultset</li>
5724     <li> <b>E:last</b> the last E in the resultset</li>
5725     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5726     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5727     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5728     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5729     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5730     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5731     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5732     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5733     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5734 </ul>
5735 <h4>CSS Value Selectors:</h4>
5736 <ul class="list">
5737     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5738     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5739     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5740     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5741     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5742     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5743 </ul>
5744  * @static
5745  */
5746 Roo.DomQuery = function(){
5747     var cache = {}, simpleCache = {}, valueCache = {};
5748     var nonSpace = /\S/;
5749     var trimRe = /^\s+|\s+$/g;
5750     var tplRe = /\{(\d+)\}/g;
5751     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5752     var tagTokenRe = /^(#)?([\w-\*]+)/;
5753     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5754
5755     function child(p, index){
5756         var i = 0;
5757         var n = p.firstChild;
5758         while(n){
5759             if(n.nodeType == 1){
5760                if(++i == index){
5761                    return n;
5762                }
5763             }
5764             n = n.nextSibling;
5765         }
5766         return null;
5767     };
5768
5769     function next(n){
5770         while((n = n.nextSibling) && n.nodeType != 1);
5771         return n;
5772     };
5773
5774     function prev(n){
5775         while((n = n.previousSibling) && n.nodeType != 1);
5776         return n;
5777     };
5778
5779     function children(d){
5780         var n = d.firstChild, ni = -1;
5781             while(n){
5782                 var nx = n.nextSibling;
5783                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5784                     d.removeChild(n);
5785                 }else{
5786                     n.nodeIndex = ++ni;
5787                 }
5788                 n = nx;
5789             }
5790             return this;
5791         };
5792
5793     function byClassName(c, a, v){
5794         if(!v){
5795             return c;
5796         }
5797         var r = [], ri = -1, cn;
5798         for(var i = 0, ci; ci = c[i]; i++){
5799             
5800             
5801             if((' '+
5802                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5803                  +' ').indexOf(v) != -1){
5804                 r[++ri] = ci;
5805             }
5806         }
5807         return r;
5808     };
5809
5810     function attrValue(n, attr){
5811         if(!n.tagName && typeof n.length != "undefined"){
5812             n = n[0];
5813         }
5814         if(!n){
5815             return null;
5816         }
5817         if(attr == "for"){
5818             return n.htmlFor;
5819         }
5820         if(attr == "class" || attr == "className"){
5821             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5822         }
5823         return n.getAttribute(attr) || n[attr];
5824
5825     };
5826
5827     function getNodes(ns, mode, tagName){
5828         var result = [], ri = -1, cs;
5829         if(!ns){
5830             return result;
5831         }
5832         tagName = tagName || "*";
5833         if(typeof ns.getElementsByTagName != "undefined"){
5834             ns = [ns];
5835         }
5836         if(!mode){
5837             for(var i = 0, ni; ni = ns[i]; i++){
5838                 cs = ni.getElementsByTagName(tagName);
5839                 for(var j = 0, ci; ci = cs[j]; j++){
5840                     result[++ri] = ci;
5841                 }
5842             }
5843         }else if(mode == "/" || mode == ">"){
5844             var utag = tagName.toUpperCase();
5845             for(var i = 0, ni, cn; ni = ns[i]; i++){
5846                 cn = ni.children || ni.childNodes;
5847                 for(var j = 0, cj; cj = cn[j]; j++){
5848                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5849                         result[++ri] = cj;
5850                     }
5851                 }
5852             }
5853         }else if(mode == "+"){
5854             var utag = tagName.toUpperCase();
5855             for(var i = 0, n; n = ns[i]; i++){
5856                 while((n = n.nextSibling) && n.nodeType != 1);
5857                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5858                     result[++ri] = n;
5859                 }
5860             }
5861         }else if(mode == "~"){
5862             for(var i = 0, n; n = ns[i]; i++){
5863                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5864                 if(n){
5865                     result[++ri] = n;
5866                 }
5867             }
5868         }
5869         return result;
5870     };
5871
5872     function concat(a, b){
5873         if(b.slice){
5874             return a.concat(b);
5875         }
5876         for(var i = 0, l = b.length; i < l; i++){
5877             a[a.length] = b[i];
5878         }
5879         return a;
5880     }
5881
5882     function byTag(cs, tagName){
5883         if(cs.tagName || cs == document){
5884             cs = [cs];
5885         }
5886         if(!tagName){
5887             return cs;
5888         }
5889         var r = [], ri = -1;
5890         tagName = tagName.toLowerCase();
5891         for(var i = 0, ci; ci = cs[i]; i++){
5892             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5893                 r[++ri] = ci;
5894             }
5895         }
5896         return r;
5897     };
5898
5899     function byId(cs, attr, id){
5900         if(cs.tagName || cs == document){
5901             cs = [cs];
5902         }
5903         if(!id){
5904             return cs;
5905         }
5906         var r = [], ri = -1;
5907         for(var i = 0,ci; ci = cs[i]; i++){
5908             if(ci && ci.id == id){
5909                 r[++ri] = ci;
5910                 return r;
5911             }
5912         }
5913         return r;
5914     };
5915
5916     function byAttribute(cs, attr, value, op, custom){
5917         var r = [], ri = -1, st = custom=="{";
5918         var f = Roo.DomQuery.operators[op];
5919         for(var i = 0, ci; ci = cs[i]; i++){
5920             var a;
5921             if(st){
5922                 a = Roo.DomQuery.getStyle(ci, attr);
5923             }
5924             else if(attr == "class" || attr == "className"){
5925                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
5926             }else if(attr == "for"){
5927                 a = ci.htmlFor;
5928             }else if(attr == "href"){
5929                 a = ci.getAttribute("href", 2);
5930             }else{
5931                 a = ci.getAttribute(attr);
5932             }
5933             if((f && f(a, value)) || (!f && a)){
5934                 r[++ri] = ci;
5935             }
5936         }
5937         return r;
5938     };
5939
5940     function byPseudo(cs, name, value){
5941         return Roo.DomQuery.pseudos[name](cs, value);
5942     };
5943
5944     // This is for IE MSXML which does not support expandos.
5945     // IE runs the same speed using setAttribute, however FF slows way down
5946     // and Safari completely fails so they need to continue to use expandos.
5947     var isIE = window.ActiveXObject ? true : false;
5948
5949     // this eval is stop the compressor from
5950     // renaming the variable to something shorter
5951     
5952     /** eval:var:batch */
5953     var batch = 30803; 
5954
5955     var key = 30803;
5956
5957     function nodupIEXml(cs){
5958         var d = ++key;
5959         cs[0].setAttribute("_nodup", d);
5960         var r = [cs[0]];
5961         for(var i = 1, len = cs.length; i < len; i++){
5962             var c = cs[i];
5963             if(!c.getAttribute("_nodup") != d){
5964                 c.setAttribute("_nodup", d);
5965                 r[r.length] = c;
5966             }
5967         }
5968         for(var i = 0, len = cs.length; i < len; i++){
5969             cs[i].removeAttribute("_nodup");
5970         }
5971         return r;
5972     }
5973
5974     function nodup(cs){
5975         if(!cs){
5976             return [];
5977         }
5978         var len = cs.length, c, i, r = cs, cj, ri = -1;
5979         if(!len || typeof cs.nodeType != "undefined" || len == 1){
5980             return cs;
5981         }
5982         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
5983             return nodupIEXml(cs);
5984         }
5985         var d = ++key;
5986         cs[0]._nodup = d;
5987         for(i = 1; c = cs[i]; i++){
5988             if(c._nodup != d){
5989                 c._nodup = d;
5990             }else{
5991                 r = [];
5992                 for(var j = 0; j < i; j++){
5993                     r[++ri] = cs[j];
5994                 }
5995                 for(j = i+1; cj = cs[j]; j++){
5996                     if(cj._nodup != d){
5997                         cj._nodup = d;
5998                         r[++ri] = cj;
5999                     }
6000                 }
6001                 return r;
6002             }
6003         }
6004         return r;
6005     }
6006
6007     function quickDiffIEXml(c1, c2){
6008         var d = ++key;
6009         for(var i = 0, len = c1.length; i < len; i++){
6010             c1[i].setAttribute("_qdiff", d);
6011         }
6012         var r = [];
6013         for(var i = 0, len = c2.length; i < len; i++){
6014             if(c2[i].getAttribute("_qdiff") != d){
6015                 r[r.length] = c2[i];
6016             }
6017         }
6018         for(var i = 0, len = c1.length; i < len; i++){
6019            c1[i].removeAttribute("_qdiff");
6020         }
6021         return r;
6022     }
6023
6024     function quickDiff(c1, c2){
6025         var len1 = c1.length;
6026         if(!len1){
6027             return c2;
6028         }
6029         if(isIE && c1[0].selectSingleNode){
6030             return quickDiffIEXml(c1, c2);
6031         }
6032         var d = ++key;
6033         for(var i = 0; i < len1; i++){
6034             c1[i]._qdiff = d;
6035         }
6036         var r = [];
6037         for(var i = 0, len = c2.length; i < len; i++){
6038             if(c2[i]._qdiff != d){
6039                 r[r.length] = c2[i];
6040             }
6041         }
6042         return r;
6043     }
6044
6045     function quickId(ns, mode, root, id){
6046         if(ns == root){
6047            var d = root.ownerDocument || root;
6048            return d.getElementById(id);
6049         }
6050         ns = getNodes(ns, mode, "*");
6051         return byId(ns, null, id);
6052     }
6053
6054     return {
6055         getStyle : function(el, name){
6056             return Roo.fly(el).getStyle(name);
6057         },
6058         /**
6059          * Compiles a selector/xpath query into a reusable function. The returned function
6060          * takes one parameter "root" (optional), which is the context node from where the query should start.
6061          * @param {String} selector The selector/xpath query
6062          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6063          * @return {Function}
6064          */
6065         compile : function(path, type){
6066             type = type || "select";
6067             
6068             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6069             var q = path, mode, lq;
6070             var tk = Roo.DomQuery.matchers;
6071             var tklen = tk.length;
6072             var mm;
6073
6074             // accept leading mode switch
6075             var lmode = q.match(modeRe);
6076             if(lmode && lmode[1]){
6077                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6078                 q = q.replace(lmode[1], "");
6079             }
6080             // strip leading slashes
6081             while(path.substr(0, 1)=="/"){
6082                 path = path.substr(1);
6083             }
6084
6085             while(q && lq != q){
6086                 lq = q;
6087                 var tm = q.match(tagTokenRe);
6088                 if(type == "select"){
6089                     if(tm){
6090                         if(tm[1] == "#"){
6091                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6092                         }else{
6093                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6094                         }
6095                         q = q.replace(tm[0], "");
6096                     }else if(q.substr(0, 1) != '@'){
6097                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6098                     }
6099                 }else{
6100                     if(tm){
6101                         if(tm[1] == "#"){
6102                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6103                         }else{
6104                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6105                         }
6106                         q = q.replace(tm[0], "");
6107                     }
6108                 }
6109                 while(!(mm = q.match(modeRe))){
6110                     var matched = false;
6111                     for(var j = 0; j < tklen; j++){
6112                         var t = tk[j];
6113                         var m = q.match(t.re);
6114                         if(m){
6115                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6116                                                     return m[i];
6117                                                 });
6118                             q = q.replace(m[0], "");
6119                             matched = true;
6120                             break;
6121                         }
6122                     }
6123                     // prevent infinite loop on bad selector
6124                     if(!matched){
6125                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6126                     }
6127                 }
6128                 if(mm[1]){
6129                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6130                     q = q.replace(mm[1], "");
6131                 }
6132             }
6133             fn[fn.length] = "return nodup(n);\n}";
6134             
6135              /** 
6136               * list of variables that need from compression as they are used by eval.
6137              *  eval:var:batch 
6138              *  eval:var:nodup
6139              *  eval:var:byTag
6140              *  eval:var:ById
6141              *  eval:var:getNodes
6142              *  eval:var:quickId
6143              *  eval:var:mode
6144              *  eval:var:root
6145              *  eval:var:n
6146              *  eval:var:byClassName
6147              *  eval:var:byPseudo
6148              *  eval:var:byAttribute
6149              *  eval:var:attrValue
6150              * 
6151              **/ 
6152             eval(fn.join(""));
6153             return f;
6154         },
6155
6156         /**
6157          * Selects a group of elements.
6158          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6159          * @param {Node} root (optional) The start of the query (defaults to document).
6160          * @return {Array}
6161          */
6162         select : function(path, root, type){
6163             if(!root || root == document){
6164                 root = document;
6165             }
6166             if(typeof root == "string"){
6167                 root = document.getElementById(root);
6168             }
6169             var paths = path.split(",");
6170             var results = [];
6171             for(var i = 0, len = paths.length; i < len; i++){
6172                 var p = paths[i].replace(trimRe, "");
6173                 if(!cache[p]){
6174                     cache[p] = Roo.DomQuery.compile(p);
6175                     if(!cache[p]){
6176                         throw p + " is not a valid selector";
6177                     }
6178                 }
6179                 var result = cache[p](root);
6180                 if(result && result != document){
6181                     results = results.concat(result);
6182                 }
6183             }
6184             if(paths.length > 1){
6185                 return nodup(results);
6186             }
6187             return results;
6188         },
6189
6190         /**
6191          * Selects a single element.
6192          * @param {String} selector The selector/xpath query
6193          * @param {Node} root (optional) The start of the query (defaults to document).
6194          * @return {Element}
6195          */
6196         selectNode : function(path, root){
6197             return Roo.DomQuery.select(path, root)[0];
6198         },
6199
6200         /**
6201          * Selects the value of a node, optionally replacing null with the defaultValue.
6202          * @param {String} selector The selector/xpath query
6203          * @param {Node} root (optional) The start of the query (defaults to document).
6204          * @param {String} defaultValue
6205          */
6206         selectValue : function(path, root, defaultValue){
6207             path = path.replace(trimRe, "");
6208             if(!valueCache[path]){
6209                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6210             }
6211             var n = valueCache[path](root);
6212             n = n[0] ? n[0] : n;
6213             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6214             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6215         },
6216
6217         /**
6218          * Selects the value of a node, parsing integers and floats.
6219          * @param {String} selector The selector/xpath query
6220          * @param {Node} root (optional) The start of the query (defaults to document).
6221          * @param {Number} defaultValue
6222          * @return {Number}
6223          */
6224         selectNumber : function(path, root, defaultValue){
6225             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6226             return parseFloat(v);
6227         },
6228
6229         /**
6230          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6231          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6232          * @param {String} selector The simple selector to test
6233          * @return {Boolean}
6234          */
6235         is : function(el, ss){
6236             if(typeof el == "string"){
6237                 el = document.getElementById(el);
6238             }
6239             var isArray = (el instanceof Array);
6240             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6241             return isArray ? (result.length == el.length) : (result.length > 0);
6242         },
6243
6244         /**
6245          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6246          * @param {Array} el An array of elements to filter
6247          * @param {String} selector The simple selector to test
6248          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6249          * the selector instead of the ones that match
6250          * @return {Array}
6251          */
6252         filter : function(els, ss, nonMatches){
6253             ss = ss.replace(trimRe, "");
6254             if(!simpleCache[ss]){
6255                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6256             }
6257             var result = simpleCache[ss](els);
6258             return nonMatches ? quickDiff(result, els) : result;
6259         },
6260
6261         /**
6262          * Collection of matching regular expressions and code snippets.
6263          */
6264         matchers : [{
6265                 re: /^\.([\w-]+)/,
6266                 select: 'n = byClassName(n, null, " {1} ");'
6267             }, {
6268                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6269                 select: 'n = byPseudo(n, "{1}", "{2}");'
6270             },{
6271                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6272                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6273             }, {
6274                 re: /^#([\w-]+)/,
6275                 select: 'n = byId(n, null, "{1}");'
6276             },{
6277                 re: /^@([\w-]+)/,
6278                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6279             }
6280         ],
6281
6282         /**
6283          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6284          * 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;.
6285          */
6286         operators : {
6287             "=" : function(a, v){
6288                 return a == v;
6289             },
6290             "!=" : function(a, v){
6291                 return a != v;
6292             },
6293             "^=" : function(a, v){
6294                 return a && a.substr(0, v.length) == v;
6295             },
6296             "$=" : function(a, v){
6297                 return a && a.substr(a.length-v.length) == v;
6298             },
6299             "*=" : function(a, v){
6300                 return a && a.indexOf(v) !== -1;
6301             },
6302             "%=" : function(a, v){
6303                 return (a % v) == 0;
6304             },
6305             "|=" : function(a, v){
6306                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6307             },
6308             "~=" : function(a, v){
6309                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6310             }
6311         },
6312
6313         /**
6314          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6315          * and the argument (if any) supplied in the selector.
6316          */
6317         pseudos : {
6318             "first-child" : function(c){
6319                 var r = [], ri = -1, n;
6320                 for(var i = 0, ci; ci = n = c[i]; i++){
6321                     while((n = n.previousSibling) && n.nodeType != 1);
6322                     if(!n){
6323                         r[++ri] = ci;
6324                     }
6325                 }
6326                 return r;
6327             },
6328
6329             "last-child" : function(c){
6330                 var r = [], ri = -1, n;
6331                 for(var i = 0, ci; ci = n = c[i]; i++){
6332                     while((n = n.nextSibling) && n.nodeType != 1);
6333                     if(!n){
6334                         r[++ri] = ci;
6335                     }
6336                 }
6337                 return r;
6338             },
6339
6340             "nth-child" : function(c, a) {
6341                 var r = [], ri = -1;
6342                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6343                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6344                 for(var i = 0, n; n = c[i]; i++){
6345                     var pn = n.parentNode;
6346                     if (batch != pn._batch) {
6347                         var j = 0;
6348                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6349                             if(cn.nodeType == 1){
6350                                cn.nodeIndex = ++j;
6351                             }
6352                         }
6353                         pn._batch = batch;
6354                     }
6355                     if (f == 1) {
6356                         if (l == 0 || n.nodeIndex == l){
6357                             r[++ri] = n;
6358                         }
6359                     } else if ((n.nodeIndex + l) % f == 0){
6360                         r[++ri] = n;
6361                     }
6362                 }
6363
6364                 return r;
6365             },
6366
6367             "only-child" : function(c){
6368                 var r = [], ri = -1;;
6369                 for(var i = 0, ci; ci = c[i]; i++){
6370                     if(!prev(ci) && !next(ci)){
6371                         r[++ri] = ci;
6372                     }
6373                 }
6374                 return r;
6375             },
6376
6377             "empty" : function(c){
6378                 var r = [], ri = -1;
6379                 for(var i = 0, ci; ci = c[i]; i++){
6380                     var cns = ci.childNodes, j = 0, cn, empty = true;
6381                     while(cn = cns[j]){
6382                         ++j;
6383                         if(cn.nodeType == 1 || cn.nodeType == 3){
6384                             empty = false;
6385                             break;
6386                         }
6387                     }
6388                     if(empty){
6389                         r[++ri] = ci;
6390                     }
6391                 }
6392                 return r;
6393             },
6394
6395             "contains" : function(c, v){
6396                 var r = [], ri = -1;
6397                 for(var i = 0, ci; ci = c[i]; i++){
6398                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6399                         r[++ri] = ci;
6400                     }
6401                 }
6402                 return r;
6403             },
6404
6405             "nodeValue" : function(c, v){
6406                 var r = [], ri = -1;
6407                 for(var i = 0, ci; ci = c[i]; i++){
6408                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6409                         r[++ri] = ci;
6410                     }
6411                 }
6412                 return r;
6413             },
6414
6415             "checked" : function(c){
6416                 var r = [], ri = -1;
6417                 for(var i = 0, ci; ci = c[i]; i++){
6418                     if(ci.checked == true){
6419                         r[++ri] = ci;
6420                     }
6421                 }
6422                 return r;
6423             },
6424
6425             "not" : function(c, ss){
6426                 return Roo.DomQuery.filter(c, ss, true);
6427             },
6428
6429             "odd" : function(c){
6430                 return this["nth-child"](c, "odd");
6431             },
6432
6433             "even" : function(c){
6434                 return this["nth-child"](c, "even");
6435             },
6436
6437             "nth" : function(c, a){
6438                 return c[a-1] || [];
6439             },
6440
6441             "first" : function(c){
6442                 return c[0] || [];
6443             },
6444
6445             "last" : function(c){
6446                 return c[c.length-1] || [];
6447             },
6448
6449             "has" : function(c, ss){
6450                 var s = Roo.DomQuery.select;
6451                 var r = [], ri = -1;
6452                 for(var i = 0, ci; ci = c[i]; i++){
6453                     if(s(ss, ci).length > 0){
6454                         r[++ri] = ci;
6455                     }
6456                 }
6457                 return r;
6458             },
6459
6460             "next" : function(c, ss){
6461                 var is = Roo.DomQuery.is;
6462                 var r = [], ri = -1;
6463                 for(var i = 0, ci; ci = c[i]; i++){
6464                     var n = next(ci);
6465                     if(n && is(n, ss)){
6466                         r[++ri] = ci;
6467                     }
6468                 }
6469                 return r;
6470             },
6471
6472             "prev" : function(c, ss){
6473                 var is = Roo.DomQuery.is;
6474                 var r = [], ri = -1;
6475                 for(var i = 0, ci; ci = c[i]; i++){
6476                     var n = prev(ci);
6477                     if(n && is(n, ss)){
6478                         r[++ri] = ci;
6479                     }
6480                 }
6481                 return r;
6482             }
6483         }
6484     };
6485 }();
6486
6487 /**
6488  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6489  * @param {String} path The selector/xpath query
6490  * @param {Node} root (optional) The start of the query (defaults to document).
6491  * @return {Array}
6492  * @member Roo
6493  * @method query
6494  */
6495 Roo.query = Roo.DomQuery.select;
6496 /*
6497  * Based on:
6498  * Ext JS Library 1.1.1
6499  * Copyright(c) 2006-2007, Ext JS, LLC.
6500  *
6501  * Originally Released Under LGPL - original licence link has changed is not relivant.
6502  *
6503  * Fork - LGPL
6504  * <script type="text/javascript">
6505  */
6506
6507 /**
6508  * @class Roo.util.Observable
6509  * Base class that provides a common interface for publishing events. Subclasses are expected to
6510  * to have a property "events" with all the events defined.<br>
6511  * For example:
6512  * <pre><code>
6513  Employee = function(name){
6514     this.name = name;
6515     this.addEvents({
6516         "fired" : true,
6517         "quit" : true
6518     });
6519  }
6520  Roo.extend(Employee, Roo.util.Observable);
6521 </code></pre>
6522  * @param {Object} config properties to use (incuding events / listeners)
6523  */
6524
6525 Roo.util.Observable = function(cfg){
6526     
6527     cfg = cfg|| {};
6528     this.addEvents(cfg.events || {});
6529     if (cfg.events) {
6530         delete cfg.events; // make sure
6531     }
6532      
6533     Roo.apply(this, cfg);
6534     
6535     if(this.listeners){
6536         this.on(this.listeners);
6537         delete this.listeners;
6538     }
6539 };
6540 Roo.util.Observable.prototype = {
6541     /** 
6542  * @cfg {Object} listeners  list of events and functions to call for this object, 
6543  * For example :
6544  * <pre><code>
6545     listeners :  { 
6546        'click' : function(e) {
6547            ..... 
6548         } ,
6549         .... 
6550     } 
6551   </code></pre>
6552  */
6553     
6554     
6555     /**
6556      * Fires the specified event with the passed parameters (minus the event name).
6557      * @param {String} eventName
6558      * @param {Object...} args Variable number of parameters are passed to handlers
6559      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6560      */
6561     fireEvent : function(){
6562         var ce = this.events[arguments[0].toLowerCase()];
6563         if(typeof ce == "object"){
6564             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6565         }else{
6566             return true;
6567         }
6568     },
6569
6570     // private
6571     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6572
6573     /**
6574      * Appends an event handler to this component
6575      * @param {String}   eventName The type of event to listen for
6576      * @param {Function} handler The method the event invokes
6577      * @param {Object}   scope (optional) The scope in which to execute the handler
6578      * function. The handler function's "this" context.
6579      * @param {Object}   options (optional) An object containing handler configuration
6580      * properties. This may contain any of the following properties:<ul>
6581      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6582      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6583      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6584      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6585      * by the specified number of milliseconds. If the event fires again within that time, the original
6586      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6587      * </ul><br>
6588      * <p>
6589      * <b>Combining Options</b><br>
6590      * Using the options argument, it is possible to combine different types of listeners:<br>
6591      * <br>
6592      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6593                 <pre><code>
6594                 el.on('click', this.onClick, this, {
6595                         single: true,
6596                 delay: 100,
6597                 forumId: 4
6598                 });
6599                 </code></pre>
6600      * <p>
6601      * <b>Attaching multiple handlers in 1 call</b><br>
6602      * The method also allows for a single argument to be passed which is a config object containing properties
6603      * which specify multiple handlers.
6604      * <pre><code>
6605                 el.on({
6606                         'click': {
6607                         fn: this.onClick,
6608                         scope: this,
6609                         delay: 100
6610                 }, 
6611                 'mouseover': {
6612                         fn: this.onMouseOver,
6613                         scope: this
6614                 },
6615                 'mouseout': {
6616                         fn: this.onMouseOut,
6617                         scope: this
6618                 }
6619                 });
6620                 </code></pre>
6621      * <p>
6622      * Or a shorthand syntax which passes the same scope object to all handlers:
6623         <pre><code>
6624                 el.on({
6625                         'click': this.onClick,
6626                 'mouseover': this.onMouseOver,
6627                 'mouseout': this.onMouseOut,
6628                 scope: this
6629                 });
6630                 </code></pre>
6631      */
6632     addListener : function(eventName, fn, scope, o){
6633         if(typeof eventName == "object"){
6634             o = eventName;
6635             for(var e in o){
6636                 if(this.filterOptRe.test(e)){
6637                     continue;
6638                 }
6639                 if(typeof o[e] == "function"){
6640                     // shared options
6641                     this.addListener(e, o[e], o.scope,  o);
6642                 }else{
6643                     // individual options
6644                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6645                 }
6646             }
6647             return;
6648         }
6649         o = (!o || typeof o == "boolean") ? {} : o;
6650         eventName = eventName.toLowerCase();
6651         var ce = this.events[eventName] || true;
6652         if(typeof ce == "boolean"){
6653             ce = new Roo.util.Event(this, eventName);
6654             this.events[eventName] = ce;
6655         }
6656         ce.addListener(fn, scope, o);
6657     },
6658
6659     /**
6660      * Removes a listener
6661      * @param {String}   eventName     The type of event to listen for
6662      * @param {Function} handler        The handler to remove
6663      * @param {Object}   scope  (optional) The scope (this object) for the handler
6664      */
6665     removeListener : function(eventName, fn, scope){
6666         var ce = this.events[eventName.toLowerCase()];
6667         if(typeof ce == "object"){
6668             ce.removeListener(fn, scope);
6669         }
6670     },
6671
6672     /**
6673      * Removes all listeners for this object
6674      */
6675     purgeListeners : function(){
6676         for(var evt in this.events){
6677             if(typeof this.events[evt] == "object"){
6678                  this.events[evt].clearListeners();
6679             }
6680         }
6681     },
6682
6683     relayEvents : function(o, events){
6684         var createHandler = function(ename){
6685             return function(){
6686                  
6687                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6688             };
6689         };
6690         for(var i = 0, len = events.length; i < len; i++){
6691             var ename = events[i];
6692             if(!this.events[ename]){
6693                 this.events[ename] = true;
6694             };
6695             o.on(ename, createHandler(ename), this);
6696         }
6697     },
6698
6699     /**
6700      * Used to define events on this Observable
6701      * @param {Object} object The object with the events defined
6702      */
6703     addEvents : function(o){
6704         if(!this.events){
6705             this.events = {};
6706         }
6707         Roo.applyIf(this.events, o);
6708     },
6709
6710     /**
6711      * Checks to see if this object has any listeners for a specified event
6712      * @param {String} eventName The name of the event to check for
6713      * @return {Boolean} True if the event is being listened for, else false
6714      */
6715     hasListener : function(eventName){
6716         var e = this.events[eventName];
6717         return typeof e == "object" && e.listeners.length > 0;
6718     }
6719 };
6720 /**
6721  * Appends an event handler to this element (shorthand for addListener)
6722  * @param {String}   eventName     The type of event to listen for
6723  * @param {Function} handler        The method the event invokes
6724  * @param {Object}   scope (optional) The scope in which to execute the handler
6725  * function. The handler function's "this" context.
6726  * @param {Object}   options  (optional)
6727  * @method
6728  */
6729 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6730 /**
6731  * Removes a listener (shorthand for removeListener)
6732  * @param {String}   eventName     The type of event to listen for
6733  * @param {Function} handler        The handler to remove
6734  * @param {Object}   scope  (optional) The scope (this object) for the handler
6735  * @method
6736  */
6737 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6738
6739 /**
6740  * Starts capture on the specified Observable. All events will be passed
6741  * to the supplied function with the event name + standard signature of the event
6742  * <b>before</b> the event is fired. If the supplied function returns false,
6743  * the event will not fire.
6744  * @param {Observable} o The Observable to capture
6745  * @param {Function} fn The function to call
6746  * @param {Object} scope (optional) The scope (this object) for the fn
6747  * @static
6748  */
6749 Roo.util.Observable.capture = function(o, fn, scope){
6750     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6751 };
6752
6753 /**
6754  * Removes <b>all</b> added captures from the Observable.
6755  * @param {Observable} o The Observable to release
6756  * @static
6757  */
6758 Roo.util.Observable.releaseCapture = function(o){
6759     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6760 };
6761
6762 (function(){
6763
6764     var createBuffered = function(h, o, scope){
6765         var task = new Roo.util.DelayedTask();
6766         return function(){
6767             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6768         };
6769     };
6770
6771     var createSingle = function(h, e, fn, scope){
6772         return function(){
6773             e.removeListener(fn, scope);
6774             return h.apply(scope, arguments);
6775         };
6776     };
6777
6778     var createDelayed = function(h, o, scope){
6779         return function(){
6780             var args = Array.prototype.slice.call(arguments, 0);
6781             setTimeout(function(){
6782                 h.apply(scope, args);
6783             }, o.delay || 10);
6784         };
6785     };
6786
6787     Roo.util.Event = function(obj, name){
6788         this.name = name;
6789         this.obj = obj;
6790         this.listeners = [];
6791     };
6792
6793     Roo.util.Event.prototype = {
6794         addListener : function(fn, scope, options){
6795             var o = options || {};
6796             scope = scope || this.obj;
6797             if(!this.isListening(fn, scope)){
6798                 var l = {fn: fn, scope: scope, options: o};
6799                 var h = fn;
6800                 if(o.delay){
6801                     h = createDelayed(h, o, scope);
6802                 }
6803                 if(o.single){
6804                     h = createSingle(h, this, fn, scope);
6805                 }
6806                 if(o.buffer){
6807                     h = createBuffered(h, o, scope);
6808                 }
6809                 l.fireFn = h;
6810                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6811                     this.listeners.push(l);
6812                 }else{
6813                     this.listeners = this.listeners.slice(0);
6814                     this.listeners.push(l);
6815                 }
6816             }
6817         },
6818
6819         findListener : function(fn, scope){
6820             scope = scope || this.obj;
6821             var ls = this.listeners;
6822             for(var i = 0, len = ls.length; i < len; i++){
6823                 var l = ls[i];
6824                 if(l.fn == fn && l.scope == scope){
6825                     return i;
6826                 }
6827             }
6828             return -1;
6829         },
6830
6831         isListening : function(fn, scope){
6832             return this.findListener(fn, scope) != -1;
6833         },
6834
6835         removeListener : function(fn, scope){
6836             var index;
6837             if((index = this.findListener(fn, scope)) != -1){
6838                 if(!this.firing){
6839                     this.listeners.splice(index, 1);
6840                 }else{
6841                     this.listeners = this.listeners.slice(0);
6842                     this.listeners.splice(index, 1);
6843                 }
6844                 return true;
6845             }
6846             return false;
6847         },
6848
6849         clearListeners : function(){
6850             this.listeners = [];
6851         },
6852
6853         fire : function(){
6854             var ls = this.listeners, scope, len = ls.length;
6855             if(len > 0){
6856                 this.firing = true;
6857                 var args = Array.prototype.slice.call(arguments, 0);                
6858                 for(var i = 0; i < len; i++){
6859                     var l = ls[i];
6860                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6861                         this.firing = false;
6862                         return false;
6863                     }
6864                 }
6865                 this.firing = false;
6866             }
6867             return true;
6868         }
6869     };
6870 })();/*
6871  * RooJS Library 
6872  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6873  *
6874  * Licence LGPL 
6875  *
6876  */
6877  
6878 /**
6879  * @class Roo.Document
6880  * @extends Roo.util.Observable
6881  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6882  * 
6883  * @param {Object} config the methods and properties of the 'base' class for the application.
6884  * 
6885  *  Generic Page handler - implement this to start your app..
6886  * 
6887  * eg.
6888  *  MyProject = new Roo.Document({
6889         events : {
6890             'load' : true // your events..
6891         },
6892         listeners : {
6893             'ready' : function() {
6894                 // fired on Roo.onReady()
6895             }
6896         }
6897  * 
6898  */
6899 Roo.Document = function(cfg) {
6900      
6901     this.addEvents({ 
6902         'ready' : true
6903     });
6904     Roo.util.Observable.call(this,cfg);
6905     
6906     var _this = this;
6907     
6908     Roo.onReady(function() {
6909         _this.fireEvent('ready');
6910     },null,false);
6911     
6912     
6913 }
6914
6915 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6916  * Based on:
6917  * Ext JS Library 1.1.1
6918  * Copyright(c) 2006-2007, Ext JS, LLC.
6919  *
6920  * Originally Released Under LGPL - original licence link has changed is not relivant.
6921  *
6922  * Fork - LGPL
6923  * <script type="text/javascript">
6924  */
6925
6926 /**
6927  * @class Roo.EventManager
6928  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
6929  * several useful events directly.
6930  * See {@link Roo.EventObject} for more details on normalized event objects.
6931  * @static
6932  */
6933 Roo.EventManager = function(){
6934     var docReadyEvent, docReadyProcId, docReadyState = false;
6935     var resizeEvent, resizeTask, textEvent, textSize;
6936     var E = Roo.lib.Event;
6937     var D = Roo.lib.Dom;
6938
6939     
6940     
6941
6942     var fireDocReady = function(){
6943         if(!docReadyState){
6944             docReadyState = true;
6945             Roo.isReady = true;
6946             if(docReadyProcId){
6947                 clearInterval(docReadyProcId);
6948             }
6949             if(Roo.isGecko || Roo.isOpera) {
6950                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
6951             }
6952             if(Roo.isIE){
6953                 var defer = document.getElementById("ie-deferred-loader");
6954                 if(defer){
6955                     defer.onreadystatechange = null;
6956                     defer.parentNode.removeChild(defer);
6957                 }
6958             }
6959             if(docReadyEvent){
6960                 docReadyEvent.fire();
6961                 docReadyEvent.clearListeners();
6962             }
6963         }
6964     };
6965     
6966     var initDocReady = function(){
6967         docReadyEvent = new Roo.util.Event();
6968         if(Roo.isGecko || Roo.isOpera) {
6969             document.addEventListener("DOMContentLoaded", fireDocReady, false);
6970         }else if(Roo.isIE){
6971             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
6972             var defer = document.getElementById("ie-deferred-loader");
6973             defer.onreadystatechange = function(){
6974                 if(this.readyState == "complete"){
6975                     fireDocReady();
6976                 }
6977             };
6978         }else if(Roo.isSafari){ 
6979             docReadyProcId = setInterval(function(){
6980                 var rs = document.readyState;
6981                 if(rs == "complete") {
6982                     fireDocReady();     
6983                  }
6984             }, 10);
6985         }
6986         // no matter what, make sure it fires on load
6987         E.on(window, "load", fireDocReady);
6988     };
6989
6990     var createBuffered = function(h, o){
6991         var task = new Roo.util.DelayedTask(h);
6992         return function(e){
6993             // create new event object impl so new events don't wipe out properties
6994             e = new Roo.EventObjectImpl(e);
6995             task.delay(o.buffer, h, null, [e]);
6996         };
6997     };
6998
6999     var createSingle = function(h, el, ename, fn){
7000         return function(e){
7001             Roo.EventManager.removeListener(el, ename, fn);
7002             h(e);
7003         };
7004     };
7005
7006     var createDelayed = function(h, o){
7007         return function(e){
7008             // create new event object impl so new events don't wipe out properties
7009             e = new Roo.EventObjectImpl(e);
7010             setTimeout(function(){
7011                 h(e);
7012             }, o.delay || 10);
7013         };
7014     };
7015     var transitionEndVal = false;
7016     
7017     var transitionEnd = function()
7018     {
7019         if (transitionEndVal) {
7020             return transitionEndVal;
7021         }
7022         var el = document.createElement('div');
7023
7024         var transEndEventNames = {
7025             WebkitTransition : 'webkitTransitionEnd',
7026             MozTransition    : 'transitionend',
7027             OTransition      : 'oTransitionEnd otransitionend',
7028             transition       : 'transitionend'
7029         };
7030     
7031         for (var name in transEndEventNames) {
7032             if (el.style[name] !== undefined) {
7033                 transitionEndVal = transEndEventNames[name];
7034                 return  transitionEndVal ;
7035             }
7036         }
7037     }
7038     
7039   
7040
7041     var listen = function(element, ename, opt, fn, scope)
7042     {
7043         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7044         fn = fn || o.fn; scope = scope || o.scope;
7045         var el = Roo.getDom(element);
7046         
7047         
7048         if(!el){
7049             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7050         }
7051         
7052         if (ename == 'transitionend') {
7053             ename = transitionEnd();
7054         }
7055         var h = function(e){
7056             e = Roo.EventObject.setEvent(e);
7057             var t;
7058             if(o.delegate){
7059                 t = e.getTarget(o.delegate, el);
7060                 if(!t){
7061                     return;
7062                 }
7063             }else{
7064                 t = e.target;
7065             }
7066             if(o.stopEvent === true){
7067                 e.stopEvent();
7068             }
7069             if(o.preventDefault === true){
7070                e.preventDefault();
7071             }
7072             if(o.stopPropagation === true){
7073                 e.stopPropagation();
7074             }
7075
7076             if(o.normalized === false){
7077                 e = e.browserEvent;
7078             }
7079
7080             fn.call(scope || el, e, t, o);
7081         };
7082         if(o.delay){
7083             h = createDelayed(h, o);
7084         }
7085         if(o.single){
7086             h = createSingle(h, el, ename, fn);
7087         }
7088         if(o.buffer){
7089             h = createBuffered(h, o);
7090         }
7091         
7092         fn._handlers = fn._handlers || [];
7093         
7094         
7095         fn._handlers.push([Roo.id(el), ename, h]);
7096         
7097         
7098          
7099         E.on(el, ename, h); // this adds the actuall listener to the object..
7100         
7101         
7102         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7103             el.addEventListener("DOMMouseScroll", h, false);
7104             E.on(window, 'unload', function(){
7105                 el.removeEventListener("DOMMouseScroll", h, false);
7106             });
7107         }
7108         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7109             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7110         }
7111         return h;
7112     };
7113
7114     var stopListening = function(el, ename, fn){
7115         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7116         if(hds){
7117             for(var i = 0, len = hds.length; i < len; i++){
7118                 var h = hds[i];
7119                 if(h[0] == id && h[1] == ename){
7120                     hd = h[2];
7121                     hds.splice(i, 1);
7122                     break;
7123                 }
7124             }
7125         }
7126         E.un(el, ename, hd);
7127         el = Roo.getDom(el);
7128         if(ename == "mousewheel" && el.addEventListener){
7129             el.removeEventListener("DOMMouseScroll", hd, false);
7130         }
7131         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7132             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7133         }
7134     };
7135
7136     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7137     
7138     var pub = {
7139         
7140         
7141         /** 
7142          * Fix for doc tools
7143          * @scope Roo.EventManager
7144          */
7145         
7146         
7147         /** 
7148          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7149          * object with a Roo.EventObject
7150          * @param {Function} fn        The method the event invokes
7151          * @param {Object}   scope    An object that becomes the scope of the handler
7152          * @param {boolean}  override If true, the obj passed in becomes
7153          *                             the execution scope of the listener
7154          * @return {Function} The wrapped function
7155          * @deprecated
7156          */
7157         wrap : function(fn, scope, override){
7158             return function(e){
7159                 Roo.EventObject.setEvent(e);
7160                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7161             };
7162         },
7163         
7164         /**
7165      * Appends an event handler to an element (shorthand for addListener)
7166      * @param {String/HTMLElement}   element        The html element or id to assign the
7167      * @param {String}   eventName The type of event to listen for
7168      * @param {Function} handler The method the event invokes
7169      * @param {Object}   scope (optional) The scope in which to execute the handler
7170      * function. The handler function's "this" context.
7171      * @param {Object}   options (optional) An object containing handler configuration
7172      * properties. This may contain any of the following properties:<ul>
7173      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7174      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7175      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7176      * <li>preventDefault {Boolean} True to prevent the default action</li>
7177      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7178      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7179      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7180      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7181      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7182      * by the specified number of milliseconds. If the event fires again within that time, the original
7183      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7184      * </ul><br>
7185      * <p>
7186      * <b>Combining Options</b><br>
7187      * Using the options argument, it is possible to combine different types of listeners:<br>
7188      * <br>
7189      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7190      * Code:<pre><code>
7191 el.on('click', this.onClick, this, {
7192     single: true,
7193     delay: 100,
7194     stopEvent : true,
7195     forumId: 4
7196 });</code></pre>
7197      * <p>
7198      * <b>Attaching multiple handlers in 1 call</b><br>
7199       * The method also allows for a single argument to be passed which is a config object containing properties
7200      * which specify multiple handlers.
7201      * <p>
7202      * Code:<pre><code>
7203 el.on({
7204     'click' : {
7205         fn: this.onClick
7206         scope: this,
7207         delay: 100
7208     },
7209     'mouseover' : {
7210         fn: this.onMouseOver
7211         scope: this
7212     },
7213     'mouseout' : {
7214         fn: this.onMouseOut
7215         scope: this
7216     }
7217 });</code></pre>
7218      * <p>
7219      * Or a shorthand syntax:<br>
7220      * Code:<pre><code>
7221 el.on({
7222     'click' : this.onClick,
7223     'mouseover' : this.onMouseOver,
7224     'mouseout' : this.onMouseOut
7225     scope: this
7226 });</code></pre>
7227      */
7228         addListener : function(element, eventName, fn, scope, options){
7229             if(typeof eventName == "object"){
7230                 var o = eventName;
7231                 for(var e in o){
7232                     if(propRe.test(e)){
7233                         continue;
7234                     }
7235                     if(typeof o[e] == "function"){
7236                         // shared options
7237                         listen(element, e, o, o[e], o.scope);
7238                     }else{
7239                         // individual options
7240                         listen(element, e, o[e]);
7241                     }
7242                 }
7243                 return;
7244             }
7245             return listen(element, eventName, options, fn, scope);
7246         },
7247         
7248         /**
7249          * Removes an event handler
7250          *
7251          * @param {String/HTMLElement}   element        The id or html element to remove the 
7252          *                             event from
7253          * @param {String}   eventName     The type of event
7254          * @param {Function} fn
7255          * @return {Boolean} True if a listener was actually removed
7256          */
7257         removeListener : function(element, eventName, fn){
7258             return stopListening(element, eventName, fn);
7259         },
7260         
7261         /**
7262          * Fires when the document is ready (before onload and before images are loaded). Can be 
7263          * accessed shorthanded Roo.onReady().
7264          * @param {Function} fn        The method the event invokes
7265          * @param {Object}   scope    An  object that becomes the scope of the handler
7266          * @param {boolean}  options
7267          */
7268         onDocumentReady : function(fn, scope, options){
7269             if(docReadyState){ // if it already fired
7270                 docReadyEvent.addListener(fn, scope, options);
7271                 docReadyEvent.fire();
7272                 docReadyEvent.clearListeners();
7273                 return;
7274             }
7275             if(!docReadyEvent){
7276                 initDocReady();
7277             }
7278             docReadyEvent.addListener(fn, scope, options);
7279         },
7280         
7281         /**
7282          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7283          * @param {Function} fn        The method the event invokes
7284          * @param {Object}   scope    An object that becomes the scope of the handler
7285          * @param {boolean}  options
7286          */
7287         onWindowResize : function(fn, scope, options)
7288         {
7289             if(!resizeEvent){
7290                 resizeEvent = new Roo.util.Event();
7291                 resizeTask = new Roo.util.DelayedTask(function(){
7292                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7293                 });
7294                 E.on(window, "resize", function()
7295                 {
7296                     if (Roo.isIE) {
7297                         resizeTask.delay(50);
7298                     } else {
7299                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7300                     }
7301                 });
7302             }
7303             resizeEvent.addListener(fn, scope, options);
7304         },
7305
7306         /**
7307          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7308          * @param {Function} fn        The method the event invokes
7309          * @param {Object}   scope    An object that becomes the scope of the handler
7310          * @param {boolean}  options
7311          */
7312         onTextResize : function(fn, scope, options){
7313             if(!textEvent){
7314                 textEvent = new Roo.util.Event();
7315                 var textEl = new Roo.Element(document.createElement('div'));
7316                 textEl.dom.className = 'x-text-resize';
7317                 textEl.dom.innerHTML = 'X';
7318                 textEl.appendTo(document.body);
7319                 textSize = textEl.dom.offsetHeight;
7320                 setInterval(function(){
7321                     if(textEl.dom.offsetHeight != textSize){
7322                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7323                     }
7324                 }, this.textResizeInterval);
7325             }
7326             textEvent.addListener(fn, scope, options);
7327         },
7328
7329         /**
7330          * Removes the passed window resize listener.
7331          * @param {Function} fn        The method the event invokes
7332          * @param {Object}   scope    The scope of handler
7333          */
7334         removeResizeListener : function(fn, scope){
7335             if(resizeEvent){
7336                 resizeEvent.removeListener(fn, scope);
7337             }
7338         },
7339
7340         // private
7341         fireResize : function(){
7342             if(resizeEvent){
7343                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7344             }   
7345         },
7346         /**
7347          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7348          */
7349         ieDeferSrc : false,
7350         /**
7351          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7352          */
7353         textResizeInterval : 50
7354     };
7355     
7356     /**
7357      * Fix for doc tools
7358      * @scopeAlias pub=Roo.EventManager
7359      */
7360     
7361      /**
7362      * Appends an event handler to an element (shorthand for addListener)
7363      * @param {String/HTMLElement}   element        The html element or id to assign the
7364      * @param {String}   eventName The type of event to listen for
7365      * @param {Function} handler The method the event invokes
7366      * @param {Object}   scope (optional) The scope in which to execute the handler
7367      * function. The handler function's "this" context.
7368      * @param {Object}   options (optional) An object containing handler configuration
7369      * properties. This may contain any of the following properties:<ul>
7370      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7371      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7372      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7373      * <li>preventDefault {Boolean} True to prevent the default action</li>
7374      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7375      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7376      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7377      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7378      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7379      * by the specified number of milliseconds. If the event fires again within that time, the original
7380      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7381      * </ul><br>
7382      * <p>
7383      * <b>Combining Options</b><br>
7384      * Using the options argument, it is possible to combine different types of listeners:<br>
7385      * <br>
7386      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7387      * Code:<pre><code>
7388 el.on('click', this.onClick, this, {
7389     single: true,
7390     delay: 100,
7391     stopEvent : true,
7392     forumId: 4
7393 });</code></pre>
7394      * <p>
7395      * <b>Attaching multiple handlers in 1 call</b><br>
7396       * The method also allows for a single argument to be passed which is a config object containing properties
7397      * which specify multiple handlers.
7398      * <p>
7399      * Code:<pre><code>
7400 el.on({
7401     'click' : {
7402         fn: this.onClick
7403         scope: this,
7404         delay: 100
7405     },
7406     'mouseover' : {
7407         fn: this.onMouseOver
7408         scope: this
7409     },
7410     'mouseout' : {
7411         fn: this.onMouseOut
7412         scope: this
7413     }
7414 });</code></pre>
7415      * <p>
7416      * Or a shorthand syntax:<br>
7417      * Code:<pre><code>
7418 el.on({
7419     'click' : this.onClick,
7420     'mouseover' : this.onMouseOver,
7421     'mouseout' : this.onMouseOut
7422     scope: this
7423 });</code></pre>
7424      */
7425     pub.on = pub.addListener;
7426     pub.un = pub.removeListener;
7427
7428     pub.stoppedMouseDownEvent = new Roo.util.Event();
7429     return pub;
7430 }();
7431 /**
7432   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7433   * @param {Function} fn        The method the event invokes
7434   * @param {Object}   scope    An  object that becomes the scope of the handler
7435   * @param {boolean}  override If true, the obj passed in becomes
7436   *                             the execution scope of the listener
7437   * @member Roo
7438   * @method onReady
7439  */
7440 Roo.onReady = Roo.EventManager.onDocumentReady;
7441
7442 Roo.onReady(function(){
7443     var bd = Roo.get(document.body);
7444     if(!bd){ return; }
7445
7446     var cls = [
7447             Roo.isIE ? "roo-ie"
7448             : Roo.isIE11 ? "roo-ie11"
7449             : Roo.isEdge ? "roo-edge"
7450             : Roo.isGecko ? "roo-gecko"
7451             : Roo.isOpera ? "roo-opera"
7452             : Roo.isSafari ? "roo-safari" : ""];
7453
7454     if(Roo.isMac){
7455         cls.push("roo-mac");
7456     }
7457     if(Roo.isLinux){
7458         cls.push("roo-linux");
7459     }
7460     if(Roo.isIOS){
7461         cls.push("roo-ios");
7462     }
7463     if(Roo.isTouch){
7464         cls.push("roo-touch");
7465     }
7466     if(Roo.isBorderBox){
7467         cls.push('roo-border-box');
7468     }
7469     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7470         var p = bd.dom.parentNode;
7471         if(p){
7472             p.className += ' roo-strict';
7473         }
7474     }
7475     bd.addClass(cls.join(' '));
7476 });
7477
7478 /**
7479  * @class Roo.EventObject
7480  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7481  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7482  * Example:
7483  * <pre><code>
7484  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7485     e.preventDefault();
7486     var target = e.getTarget();
7487     ...
7488  }
7489  var myDiv = Roo.get("myDiv");
7490  myDiv.on("click", handleClick);
7491  //or
7492  Roo.EventManager.on("myDiv", 'click', handleClick);
7493  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7494  </code></pre>
7495  * @static
7496  */
7497 Roo.EventObject = function(){
7498     
7499     var E = Roo.lib.Event;
7500     
7501     // safari keypress events for special keys return bad keycodes
7502     var safariKeys = {
7503         63234 : 37, // left
7504         63235 : 39, // right
7505         63232 : 38, // up
7506         63233 : 40, // down
7507         63276 : 33, // page up
7508         63277 : 34, // page down
7509         63272 : 46, // delete
7510         63273 : 36, // home
7511         63275 : 35  // end
7512     };
7513
7514     // normalize button clicks
7515     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7516                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7517
7518     Roo.EventObjectImpl = function(e){
7519         if(e){
7520             this.setEvent(e.browserEvent || e);
7521         }
7522     };
7523     Roo.EventObjectImpl.prototype = {
7524         /**
7525          * Used to fix doc tools.
7526          * @scope Roo.EventObject.prototype
7527          */
7528             
7529
7530         
7531         
7532         /** The normal browser event */
7533         browserEvent : null,
7534         /** The button pressed in a mouse event */
7535         button : -1,
7536         /** True if the shift key was down during the event */
7537         shiftKey : false,
7538         /** True if the control key was down during the event */
7539         ctrlKey : false,
7540         /** True if the alt key was down during the event */
7541         altKey : false,
7542
7543         /** Key constant 
7544         * @type Number */
7545         BACKSPACE : 8,
7546         /** Key constant 
7547         * @type Number */
7548         TAB : 9,
7549         /** Key constant 
7550         * @type Number */
7551         RETURN : 13,
7552         /** Key constant 
7553         * @type Number */
7554         ENTER : 13,
7555         /** Key constant 
7556         * @type Number */
7557         SHIFT : 16,
7558         /** Key constant 
7559         * @type Number */
7560         CONTROL : 17,
7561         /** Key constant 
7562         * @type Number */
7563         ESC : 27,
7564         /** Key constant 
7565         * @type Number */
7566         SPACE : 32,
7567         /** Key constant 
7568         * @type Number */
7569         PAGEUP : 33,
7570         /** Key constant 
7571         * @type Number */
7572         PAGEDOWN : 34,
7573         /** Key constant 
7574         * @type Number */
7575         END : 35,
7576         /** Key constant 
7577         * @type Number */
7578         HOME : 36,
7579         /** Key constant 
7580         * @type Number */
7581         LEFT : 37,
7582         /** Key constant 
7583         * @type Number */
7584         UP : 38,
7585         /** Key constant 
7586         * @type Number */
7587         RIGHT : 39,
7588         /** Key constant 
7589         * @type Number */
7590         DOWN : 40,
7591         /** Key constant 
7592         * @type Number */
7593         DELETE : 46,
7594         /** Key constant 
7595         * @type Number */
7596         F5 : 116,
7597
7598            /** @private */
7599         setEvent : function(e){
7600             if(e == this || (e && e.browserEvent)){ // already wrapped
7601                 return e;
7602             }
7603             this.browserEvent = e;
7604             if(e){
7605                 // normalize buttons
7606                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7607                 if(e.type == 'click' && this.button == -1){
7608                     this.button = 0;
7609                 }
7610                 this.type = e.type;
7611                 this.shiftKey = e.shiftKey;
7612                 // mac metaKey behaves like ctrlKey
7613                 this.ctrlKey = e.ctrlKey || e.metaKey;
7614                 this.altKey = e.altKey;
7615                 // in getKey these will be normalized for the mac
7616                 this.keyCode = e.keyCode;
7617                 // keyup warnings on firefox.
7618                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7619                 // cache the target for the delayed and or buffered events
7620                 this.target = E.getTarget(e);
7621                 // same for XY
7622                 this.xy = E.getXY(e);
7623             }else{
7624                 this.button = -1;
7625                 this.shiftKey = false;
7626                 this.ctrlKey = false;
7627                 this.altKey = false;
7628                 this.keyCode = 0;
7629                 this.charCode =0;
7630                 this.target = null;
7631                 this.xy = [0, 0];
7632             }
7633             return this;
7634         },
7635
7636         /**
7637          * Stop the event (preventDefault and stopPropagation)
7638          */
7639         stopEvent : function(){
7640             if(this.browserEvent){
7641                 if(this.browserEvent.type == 'mousedown'){
7642                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7643                 }
7644                 E.stopEvent(this.browserEvent);
7645             }
7646         },
7647
7648         /**
7649          * Prevents the browsers default handling of the event.
7650          */
7651         preventDefault : function(){
7652             if(this.browserEvent){
7653                 E.preventDefault(this.browserEvent);
7654             }
7655         },
7656
7657         /** @private */
7658         isNavKeyPress : function(){
7659             var k = this.keyCode;
7660             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7661             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7662         },
7663
7664         isSpecialKey : function(){
7665             var k = this.keyCode;
7666             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7667             (k == 16) || (k == 17) ||
7668             (k >= 18 && k <= 20) ||
7669             (k >= 33 && k <= 35) ||
7670             (k >= 36 && k <= 39) ||
7671             (k >= 44 && k <= 45);
7672         },
7673         /**
7674          * Cancels bubbling of the event.
7675          */
7676         stopPropagation : function(){
7677             if(this.browserEvent){
7678                 if(this.type == 'mousedown'){
7679                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7680                 }
7681                 E.stopPropagation(this.browserEvent);
7682             }
7683         },
7684
7685         /**
7686          * Gets the key code for the event.
7687          * @return {Number}
7688          */
7689         getCharCode : function(){
7690             return this.charCode || this.keyCode;
7691         },
7692
7693         /**
7694          * Returns a normalized keyCode for the event.
7695          * @return {Number} The key code
7696          */
7697         getKey : function(){
7698             var k = this.keyCode || this.charCode;
7699             return Roo.isSafari ? (safariKeys[k] || k) : k;
7700         },
7701
7702         /**
7703          * Gets the x coordinate of the event.
7704          * @return {Number}
7705          */
7706         getPageX : function(){
7707             return this.xy[0];
7708         },
7709
7710         /**
7711          * Gets the y coordinate of the event.
7712          * @return {Number}
7713          */
7714         getPageY : function(){
7715             return this.xy[1];
7716         },
7717
7718         /**
7719          * Gets the time of the event.
7720          * @return {Number}
7721          */
7722         getTime : function(){
7723             if(this.browserEvent){
7724                 return E.getTime(this.browserEvent);
7725             }
7726             return null;
7727         },
7728
7729         /**
7730          * Gets the page coordinates of the event.
7731          * @return {Array} The xy values like [x, y]
7732          */
7733         getXY : function(){
7734             return this.xy;
7735         },
7736
7737         /**
7738          * Gets the target for the event.
7739          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7740          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7741                 search as a number or element (defaults to 10 || document.body)
7742          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7743          * @return {HTMLelement}
7744          */
7745         getTarget : function(selector, maxDepth, returnEl){
7746             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7747         },
7748         /**
7749          * Gets the related target.
7750          * @return {HTMLElement}
7751          */
7752         getRelatedTarget : function(){
7753             if(this.browserEvent){
7754                 return E.getRelatedTarget(this.browserEvent);
7755             }
7756             return null;
7757         },
7758
7759         /**
7760          * Normalizes mouse wheel delta across browsers
7761          * @return {Number} The delta
7762          */
7763         getWheelDelta : function(){
7764             var e = this.browserEvent;
7765             var delta = 0;
7766             if(e.wheelDelta){ /* IE/Opera. */
7767                 delta = e.wheelDelta/120;
7768             }else if(e.detail){ /* Mozilla case. */
7769                 delta = -e.detail/3;
7770             }
7771             return delta;
7772         },
7773
7774         /**
7775          * Returns true if the control, meta, shift or alt key was pressed during this event.
7776          * @return {Boolean}
7777          */
7778         hasModifier : function(){
7779             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7780         },
7781
7782         /**
7783          * Returns true if the target of this event equals el or is a child of el
7784          * @param {String/HTMLElement/Element} el
7785          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7786          * @return {Boolean}
7787          */
7788         within : function(el, related){
7789             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7790             return t && Roo.fly(el).contains(t);
7791         },
7792
7793         getPoint : function(){
7794             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7795         }
7796     };
7797
7798     return new Roo.EventObjectImpl();
7799 }();
7800             
7801     /*
7802  * Based on:
7803  * Ext JS Library 1.1.1
7804  * Copyright(c) 2006-2007, Ext JS, LLC.
7805  *
7806  * Originally Released Under LGPL - original licence link has changed is not relivant.
7807  *
7808  * Fork - LGPL
7809  * <script type="text/javascript">
7810  */
7811
7812  
7813 // was in Composite Element!??!?!
7814  
7815 (function(){
7816     var D = Roo.lib.Dom;
7817     var E = Roo.lib.Event;
7818     var A = Roo.lib.Anim;
7819
7820     // local style camelizing for speed
7821     var propCache = {};
7822     var camelRe = /(-[a-z])/gi;
7823     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7824     var view = document.defaultView;
7825
7826 /**
7827  * @class Roo.Element
7828  * Represents an Element in the DOM.<br><br>
7829  * Usage:<br>
7830 <pre><code>
7831 var el = Roo.get("my-div");
7832
7833 // or with getEl
7834 var el = getEl("my-div");
7835
7836 // or with a DOM element
7837 var el = Roo.get(myDivElement);
7838 </code></pre>
7839  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7840  * each call instead of constructing a new one.<br><br>
7841  * <b>Animations</b><br />
7842  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7843  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7844 <pre>
7845 Option    Default   Description
7846 --------- --------  ---------------------------------------------
7847 duration  .35       The duration of the animation in seconds
7848 easing    easeOut   The YUI easing method
7849 callback  none      A function to execute when the anim completes
7850 scope     this      The scope (this) of the callback function
7851 </pre>
7852 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7853 * manipulate the animation. Here's an example:
7854 <pre><code>
7855 var el = Roo.get("my-div");
7856
7857 // no animation
7858 el.setWidth(100);
7859
7860 // default animation
7861 el.setWidth(100, true);
7862
7863 // animation with some options set
7864 el.setWidth(100, {
7865     duration: 1,
7866     callback: this.foo,
7867     scope: this
7868 });
7869
7870 // using the "anim" property to get the Anim object
7871 var opt = {
7872     duration: 1,
7873     callback: this.foo,
7874     scope: this
7875 };
7876 el.setWidth(100, opt);
7877 ...
7878 if(opt.anim.isAnimated()){
7879     opt.anim.stop();
7880 }
7881 </code></pre>
7882 * <b> Composite (Collections of) Elements</b><br />
7883  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7884  * @constructor Create a new Element directly.
7885  * @param {String/HTMLElement} element
7886  * @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).
7887  */
7888     Roo.Element = function(element, forceNew)
7889     {
7890         var dom = typeof element == "string" ?
7891                 document.getElementById(element) : element;
7892         
7893         this.listeners = {};
7894         
7895         if(!dom){ // invalid id/element
7896             return null;
7897         }
7898         var id = dom.id;
7899         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7900             return Roo.Element.cache[id];
7901         }
7902
7903         /**
7904          * The DOM element
7905          * @type HTMLElement
7906          */
7907         this.dom = dom;
7908
7909         /**
7910          * The DOM element ID
7911          * @type String
7912          */
7913         this.id = id || Roo.id(dom);
7914         
7915         return this; // assumed for cctor?
7916     };
7917
7918     var El = Roo.Element;
7919
7920     El.prototype = {
7921         /**
7922          * The element's default display mode  (defaults to "") 
7923          * @type String
7924          */
7925         originalDisplay : "",
7926
7927         
7928         // note this is overridden in BS version..
7929         visibilityMode : 1, 
7930         /**
7931          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
7932          * @type String
7933          */
7934         defaultUnit : "px",
7935         
7936         /**
7937          * Sets the element's visibility mode. When setVisible() is called it
7938          * will use this to determine whether to set the visibility or the display property.
7939          * @param visMode Element.VISIBILITY or Element.DISPLAY
7940          * @return {Roo.Element} this
7941          */
7942         setVisibilityMode : function(visMode){
7943             this.visibilityMode = visMode;
7944             return this;
7945         },
7946         /**
7947          * Convenience method for setVisibilityMode(Element.DISPLAY)
7948          * @param {String} display (optional) What to set display to when visible
7949          * @return {Roo.Element} this
7950          */
7951         enableDisplayMode : function(display){
7952             this.setVisibilityMode(El.DISPLAY);
7953             if(typeof display != "undefined") { this.originalDisplay = display; }
7954             return this;
7955         },
7956
7957         /**
7958          * 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)
7959          * @param {String} selector The simple selector to test
7960          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7961                 search as a number or element (defaults to 10 || document.body)
7962          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7963          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7964          */
7965         findParent : function(simpleSelector, maxDepth, returnEl){
7966             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
7967             maxDepth = maxDepth || 50;
7968             if(typeof maxDepth != "number"){
7969                 stopEl = Roo.getDom(maxDepth);
7970                 maxDepth = 10;
7971             }
7972             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
7973                 if(dq.is(p, simpleSelector)){
7974                     return returnEl ? Roo.get(p) : p;
7975                 }
7976                 depth++;
7977                 p = p.parentNode;
7978             }
7979             return null;
7980         },
7981
7982
7983         /**
7984          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
7985          * @param {String} selector The simple selector to test
7986          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7987                 search as a number or element (defaults to 10 || document.body)
7988          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7989          * @return {HTMLElement} The matching DOM node (or null if no match was found)
7990          */
7991         findParentNode : function(simpleSelector, maxDepth, returnEl){
7992             var p = Roo.fly(this.dom.parentNode, '_internal');
7993             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
7994         },
7995         
7996         /**
7997          * Looks at  the scrollable parent element
7998          */
7999         findScrollableParent : function()
8000         {
8001             var overflowRegex = /(auto|scroll)/;
8002             
8003             if(this.getStyle('position') === 'fixed'){
8004                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8005             }
8006             
8007             var excludeStaticParent = this.getStyle('position') === "absolute";
8008             
8009             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8010                 
8011                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8012                     continue;
8013                 }
8014                 
8015                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8016                     return parent;
8017                 }
8018                 
8019                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8020                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8021                 }
8022             }
8023             
8024             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8025         },
8026
8027         /**
8028          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8029          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8030          * @param {String} selector The simple selector to test
8031          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8032                 search as a number or element (defaults to 10 || document.body)
8033          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8034          */
8035         up : function(simpleSelector, maxDepth){
8036             return this.findParentNode(simpleSelector, maxDepth, true);
8037         },
8038
8039
8040
8041         /**
8042          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8043          * @param {String} selector The simple selector to test
8044          * @return {Boolean} True if this element matches the selector, else false
8045          */
8046         is : function(simpleSelector){
8047             return Roo.DomQuery.is(this.dom, simpleSelector);
8048         },
8049
8050         /**
8051          * Perform animation on this element.
8052          * @param {Object} args The YUI animation control args
8053          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8054          * @param {Function} onComplete (optional) Function to call when animation completes
8055          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8056          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8057          * @return {Roo.Element} this
8058          */
8059         animate : function(args, duration, onComplete, easing, animType){
8060             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8061             return this;
8062         },
8063
8064         /*
8065          * @private Internal animation call
8066          */
8067         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8068             animType = animType || 'run';
8069             opt = opt || {};
8070             var anim = Roo.lib.Anim[animType](
8071                 this.dom, args,
8072                 (opt.duration || defaultDur) || .35,
8073                 (opt.easing || defaultEase) || 'easeOut',
8074                 function(){
8075                     Roo.callback(cb, this);
8076                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8077                 },
8078                 this
8079             );
8080             opt.anim = anim;
8081             return anim;
8082         },
8083
8084         // private legacy anim prep
8085         preanim : function(a, i){
8086             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8087         },
8088
8089         /**
8090          * Removes worthless text nodes
8091          * @param {Boolean} forceReclean (optional) By default the element
8092          * keeps track if it has been cleaned already so
8093          * you can call this over and over. However, if you update the element and
8094          * need to force a reclean, you can pass true.
8095          */
8096         clean : function(forceReclean){
8097             if(this.isCleaned && forceReclean !== true){
8098                 return this;
8099             }
8100             var ns = /\S/;
8101             var d = this.dom, n = d.firstChild, ni = -1;
8102             while(n){
8103                 var nx = n.nextSibling;
8104                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8105                     d.removeChild(n);
8106                 }else{
8107                     n.nodeIndex = ++ni;
8108                 }
8109                 n = nx;
8110             }
8111             this.isCleaned = true;
8112             return this;
8113         },
8114
8115         // private
8116         calcOffsetsTo : function(el){
8117             el = Roo.get(el);
8118             var d = el.dom;
8119             var restorePos = false;
8120             if(el.getStyle('position') == 'static'){
8121                 el.position('relative');
8122                 restorePos = true;
8123             }
8124             var x = 0, y =0;
8125             var op = this.dom;
8126             while(op && op != d && op.tagName != 'HTML'){
8127                 x+= op.offsetLeft;
8128                 y+= op.offsetTop;
8129                 op = op.offsetParent;
8130             }
8131             if(restorePos){
8132                 el.position('static');
8133             }
8134             return [x, y];
8135         },
8136
8137         /**
8138          * Scrolls this element into view within the passed container.
8139          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8140          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8141          * @return {Roo.Element} this
8142          */
8143         scrollIntoView : function(container, hscroll){
8144             var c = Roo.getDom(container) || document.body;
8145             var el = this.dom;
8146
8147             var o = this.calcOffsetsTo(c),
8148                 l = o[0],
8149                 t = o[1],
8150                 b = t+el.offsetHeight,
8151                 r = l+el.offsetWidth;
8152
8153             var ch = c.clientHeight;
8154             var ct = parseInt(c.scrollTop, 10);
8155             var cl = parseInt(c.scrollLeft, 10);
8156             var cb = ct + ch;
8157             var cr = cl + c.clientWidth;
8158
8159             if(t < ct){
8160                 c.scrollTop = t;
8161             }else if(b > cb){
8162                 c.scrollTop = b-ch;
8163             }
8164
8165             if(hscroll !== false){
8166                 if(l < cl){
8167                     c.scrollLeft = l;
8168                 }else if(r > cr){
8169                     c.scrollLeft = r-c.clientWidth;
8170                 }
8171             }
8172             return this;
8173         },
8174
8175         // private
8176         scrollChildIntoView : function(child, hscroll){
8177             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8178         },
8179
8180         /**
8181          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8182          * the new height may not be available immediately.
8183          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8184          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8185          * @param {Function} onComplete (optional) Function to call when animation completes
8186          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8187          * @return {Roo.Element} this
8188          */
8189         autoHeight : function(animate, duration, onComplete, easing){
8190             var oldHeight = this.getHeight();
8191             this.clip();
8192             this.setHeight(1); // force clipping
8193             setTimeout(function(){
8194                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8195                 if(!animate){
8196                     this.setHeight(height);
8197                     this.unclip();
8198                     if(typeof onComplete == "function"){
8199                         onComplete();
8200                     }
8201                 }else{
8202                     this.setHeight(oldHeight); // restore original height
8203                     this.setHeight(height, animate, duration, function(){
8204                         this.unclip();
8205                         if(typeof onComplete == "function") { onComplete(); }
8206                     }.createDelegate(this), easing);
8207                 }
8208             }.createDelegate(this), 0);
8209             return this;
8210         },
8211
8212         /**
8213          * Returns true if this element is an ancestor of the passed element
8214          * @param {HTMLElement/String} el The element to check
8215          * @return {Boolean} True if this element is an ancestor of el, else false
8216          */
8217         contains : function(el){
8218             if(!el){return false;}
8219             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8220         },
8221
8222         /**
8223          * Checks whether the element is currently visible using both visibility and display properties.
8224          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8225          * @return {Boolean} True if the element is currently visible, else false
8226          */
8227         isVisible : function(deep) {
8228             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8229             if(deep !== true || !vis){
8230                 return vis;
8231             }
8232             var p = this.dom.parentNode;
8233             while(p && p.tagName.toLowerCase() != "body"){
8234                 if(!Roo.fly(p, '_isVisible').isVisible()){
8235                     return false;
8236                 }
8237                 p = p.parentNode;
8238             }
8239             return true;
8240         },
8241
8242         /**
8243          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8244          * @param {String} selector The CSS selector
8245          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8246          * @return {CompositeElement/CompositeElementLite} The composite element
8247          */
8248         select : function(selector, unique){
8249             return El.select(selector, unique, this.dom);
8250         },
8251
8252         /**
8253          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8254          * @param {String} selector The CSS selector
8255          * @return {Array} An array of the matched nodes
8256          */
8257         query : function(selector, unique){
8258             return Roo.DomQuery.select(selector, this.dom);
8259         },
8260
8261         /**
8262          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8263          * @param {String} selector The CSS selector
8264          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8265          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8266          */
8267         child : function(selector, returnDom){
8268             var n = Roo.DomQuery.selectNode(selector, this.dom);
8269             return returnDom ? n : Roo.get(n);
8270         },
8271
8272         /**
8273          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8274          * @param {String} selector The CSS selector
8275          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8276          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8277          */
8278         down : function(selector, returnDom){
8279             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8280             return returnDom ? n : Roo.get(n);
8281         },
8282
8283         /**
8284          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8285          * @param {String} group The group the DD object is member of
8286          * @param {Object} config The DD config object
8287          * @param {Object} overrides An object containing methods to override/implement on the DD object
8288          * @return {Roo.dd.DD} The DD object
8289          */
8290         initDD : function(group, config, overrides){
8291             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8292             return Roo.apply(dd, overrides);
8293         },
8294
8295         /**
8296          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8297          * @param {String} group The group the DDProxy object is member of
8298          * @param {Object} config The DDProxy config object
8299          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8300          * @return {Roo.dd.DDProxy} The DDProxy object
8301          */
8302         initDDProxy : function(group, config, overrides){
8303             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8304             return Roo.apply(dd, overrides);
8305         },
8306
8307         /**
8308          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8309          * @param {String} group The group the DDTarget object is member of
8310          * @param {Object} config The DDTarget config object
8311          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8312          * @return {Roo.dd.DDTarget} The DDTarget object
8313          */
8314         initDDTarget : function(group, config, overrides){
8315             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8316             return Roo.apply(dd, overrides);
8317         },
8318
8319         /**
8320          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8321          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8322          * @param {Boolean} visible Whether the element is visible
8323          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8324          * @return {Roo.Element} this
8325          */
8326          setVisible : function(visible, animate){
8327             if(!animate || !A){
8328                 if(this.visibilityMode == El.DISPLAY){
8329                     this.setDisplayed(visible);
8330                 }else{
8331                     this.fixDisplay();
8332                     this.dom.style.visibility = visible ? "visible" : "hidden";
8333                 }
8334             }else{
8335                 // closure for composites
8336                 var dom = this.dom;
8337                 var visMode = this.visibilityMode;
8338                 if(visible){
8339                     this.setOpacity(.01);
8340                     this.setVisible(true);
8341                 }
8342                 this.anim({opacity: { to: (visible?1:0) }},
8343                       this.preanim(arguments, 1),
8344                       null, .35, 'easeIn', function(){
8345                          if(!visible){
8346                              if(visMode == El.DISPLAY){
8347                                  dom.style.display = "none";
8348                              }else{
8349                                  dom.style.visibility = "hidden";
8350                              }
8351                              Roo.get(dom).setOpacity(1);
8352                          }
8353                      });
8354             }
8355             return this;
8356         },
8357
8358         /**
8359          * Returns true if display is not "none"
8360          * @return {Boolean}
8361          */
8362         isDisplayed : function() {
8363             return this.getStyle("display") != "none";
8364         },
8365
8366         /**
8367          * Toggles the element's visibility or display, depending on visibility mode.
8368          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8369          * @return {Roo.Element} this
8370          */
8371         toggle : function(animate){
8372             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8373             return this;
8374         },
8375
8376         /**
8377          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8378          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8379          * @return {Roo.Element} this
8380          */
8381         setDisplayed : function(value) {
8382             if(typeof value == "boolean"){
8383                value = value ? this.originalDisplay : "none";
8384             }
8385             this.setStyle("display", value);
8386             return this;
8387         },
8388
8389         /**
8390          * Tries to focus the element. Any exceptions are caught and ignored.
8391          * @return {Roo.Element} this
8392          */
8393         focus : function() {
8394             try{
8395                 this.dom.focus();
8396             }catch(e){}
8397             return this;
8398         },
8399
8400         /**
8401          * Tries to blur the element. Any exceptions are caught and ignored.
8402          * @return {Roo.Element} this
8403          */
8404         blur : function() {
8405             try{
8406                 this.dom.blur();
8407             }catch(e){}
8408             return this;
8409         },
8410
8411         /**
8412          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8413          * @param {String/Array} className The CSS class to add, or an array of classes
8414          * @return {Roo.Element} this
8415          */
8416         addClass : function(className){
8417             if(className instanceof Array){
8418                 for(var i = 0, len = className.length; i < len; i++) {
8419                     this.addClass(className[i]);
8420                 }
8421             }else{
8422                 if(className && !this.hasClass(className)){
8423                     if (this.dom instanceof SVGElement) {
8424                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8425                     } else {
8426                         this.dom.className = this.dom.className + " " + className;
8427                     }
8428                 }
8429             }
8430             return this;
8431         },
8432
8433         /**
8434          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8435          * @param {String/Array} className The CSS class to add, or an array of classes
8436          * @return {Roo.Element} this
8437          */
8438         radioClass : function(className){
8439             var siblings = this.dom.parentNode.childNodes;
8440             for(var i = 0; i < siblings.length; i++) {
8441                 var s = siblings[i];
8442                 if(s.nodeType == 1){
8443                     Roo.get(s).removeClass(className);
8444                 }
8445             }
8446             this.addClass(className);
8447             return this;
8448         },
8449
8450         /**
8451          * Removes one or more CSS classes from the element.
8452          * @param {String/Array} className The CSS class to remove, or an array of classes
8453          * @return {Roo.Element} this
8454          */
8455         removeClass : function(className){
8456             
8457             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8458             if(!className || !cn){
8459                 return this;
8460             }
8461             if(className instanceof Array){
8462                 for(var i = 0, len = className.length; i < len; i++) {
8463                     this.removeClass(className[i]);
8464                 }
8465             }else{
8466                 if(this.hasClass(className)){
8467                     var re = this.classReCache[className];
8468                     if (!re) {
8469                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8470                        this.classReCache[className] = re;
8471                     }
8472                     if (this.dom instanceof SVGElement) {
8473                         this.dom.className.baseVal = cn.replace(re, " ");
8474                     } else {
8475                         this.dom.className = cn.replace(re, " ");
8476                     }
8477                 }
8478             }
8479             return this;
8480         },
8481
8482         // private
8483         classReCache: {},
8484
8485         /**
8486          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8487          * @param {String} className The CSS class to toggle
8488          * @return {Roo.Element} this
8489          */
8490         toggleClass : function(className){
8491             if(this.hasClass(className)){
8492                 this.removeClass(className);
8493             }else{
8494                 this.addClass(className);
8495             }
8496             return this;
8497         },
8498
8499         /**
8500          * Checks if the specified CSS class exists on this element's DOM node.
8501          * @param {String} className The CSS class to check for
8502          * @return {Boolean} True if the class exists, else false
8503          */
8504         hasClass : function(className){
8505             if (this.dom instanceof SVGElement) {
8506                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8507             } 
8508             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8509         },
8510
8511         /**
8512          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8513          * @param {String} oldClassName The CSS class to replace
8514          * @param {String} newClassName The replacement CSS class
8515          * @return {Roo.Element} this
8516          */
8517         replaceClass : function(oldClassName, newClassName){
8518             this.removeClass(oldClassName);
8519             this.addClass(newClassName);
8520             return this;
8521         },
8522
8523         /**
8524          * Returns an object with properties matching the styles requested.
8525          * For example, el.getStyles('color', 'font-size', 'width') might return
8526          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8527          * @param {String} style1 A style name
8528          * @param {String} style2 A style name
8529          * @param {String} etc.
8530          * @return {Object} The style object
8531          */
8532         getStyles : function(){
8533             var a = arguments, len = a.length, r = {};
8534             for(var i = 0; i < len; i++){
8535                 r[a[i]] = this.getStyle(a[i]);
8536             }
8537             return r;
8538         },
8539
8540         /**
8541          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8542          * @param {String} property The style property whose value is returned.
8543          * @return {String} The current value of the style property for this element.
8544          */
8545         getStyle : function(){
8546             return view && view.getComputedStyle ?
8547                 function(prop){
8548                     var el = this.dom, v, cs, camel;
8549                     if(prop == 'float'){
8550                         prop = "cssFloat";
8551                     }
8552                     if(el.style && (v = el.style[prop])){
8553                         return v;
8554                     }
8555                     if(cs = view.getComputedStyle(el, "")){
8556                         if(!(camel = propCache[prop])){
8557                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8558                         }
8559                         return cs[camel];
8560                     }
8561                     return null;
8562                 } :
8563                 function(prop){
8564                     var el = this.dom, v, cs, camel;
8565                     if(prop == 'opacity'){
8566                         if(typeof el.style.filter == 'string'){
8567                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8568                             if(m){
8569                                 var fv = parseFloat(m[1]);
8570                                 if(!isNaN(fv)){
8571                                     return fv ? fv / 100 : 0;
8572                                 }
8573                             }
8574                         }
8575                         return 1;
8576                     }else if(prop == 'float'){
8577                         prop = "styleFloat";
8578                     }
8579                     if(!(camel = propCache[prop])){
8580                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8581                     }
8582                     if(v = el.style[camel]){
8583                         return v;
8584                     }
8585                     if(cs = el.currentStyle){
8586                         return cs[camel];
8587                     }
8588                     return null;
8589                 };
8590         }(),
8591
8592         /**
8593          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8594          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8595          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8596          * @return {Roo.Element} this
8597          */
8598         setStyle : function(prop, value){
8599             if(typeof prop == "string"){
8600                 
8601                 if (prop == 'float') {
8602                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8603                     return this;
8604                 }
8605                 
8606                 var camel;
8607                 if(!(camel = propCache[prop])){
8608                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8609                 }
8610                 
8611                 if(camel == 'opacity') {
8612                     this.setOpacity(value);
8613                 }else{
8614                     this.dom.style[camel] = value;
8615                 }
8616             }else{
8617                 for(var style in prop){
8618                     if(typeof prop[style] != "function"){
8619                        this.setStyle(style, prop[style]);
8620                     }
8621                 }
8622             }
8623             return this;
8624         },
8625
8626         /**
8627          * More flexible version of {@link #setStyle} for setting style properties.
8628          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8629          * a function which returns such a specification.
8630          * @return {Roo.Element} this
8631          */
8632         applyStyles : function(style){
8633             Roo.DomHelper.applyStyles(this.dom, style);
8634             return this;
8635         },
8636
8637         /**
8638           * 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).
8639           * @return {Number} The X position of the element
8640           */
8641         getX : function(){
8642             return D.getX(this.dom);
8643         },
8644
8645         /**
8646           * 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).
8647           * @return {Number} The Y position of the element
8648           */
8649         getY : function(){
8650             return D.getY(this.dom);
8651         },
8652
8653         /**
8654           * 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).
8655           * @return {Array} The XY position of the element
8656           */
8657         getXY : function(){
8658             return D.getXY(this.dom);
8659         },
8660
8661         /**
8662          * 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).
8663          * @param {Number} The X position of the element
8664          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8665          * @return {Roo.Element} this
8666          */
8667         setX : function(x, animate){
8668             if(!animate || !A){
8669                 D.setX(this.dom, x);
8670             }else{
8671                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8672             }
8673             return this;
8674         },
8675
8676         /**
8677          * 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).
8678          * @param {Number} The Y position of the element
8679          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8680          * @return {Roo.Element} this
8681          */
8682         setY : function(y, animate){
8683             if(!animate || !A){
8684                 D.setY(this.dom, y);
8685             }else{
8686                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8687             }
8688             return this;
8689         },
8690
8691         /**
8692          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8693          * @param {String} left The left CSS property value
8694          * @return {Roo.Element} this
8695          */
8696         setLeft : function(left){
8697             this.setStyle("left", this.addUnits(left));
8698             return this;
8699         },
8700
8701         /**
8702          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8703          * @param {String} top The top CSS property value
8704          * @return {Roo.Element} this
8705          */
8706         setTop : function(top){
8707             this.setStyle("top", this.addUnits(top));
8708             return this;
8709         },
8710
8711         /**
8712          * Sets the element's CSS right style.
8713          * @param {String} right The right CSS property value
8714          * @return {Roo.Element} this
8715          */
8716         setRight : function(right){
8717             this.setStyle("right", this.addUnits(right));
8718             return this;
8719         },
8720
8721         /**
8722          * Sets the element's CSS bottom style.
8723          * @param {String} bottom The bottom CSS property value
8724          * @return {Roo.Element} this
8725          */
8726         setBottom : function(bottom){
8727             this.setStyle("bottom", this.addUnits(bottom));
8728             return this;
8729         },
8730
8731         /**
8732          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8733          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8734          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8735          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8736          * @return {Roo.Element} this
8737          */
8738         setXY : function(pos, animate){
8739             if(!animate || !A){
8740                 D.setXY(this.dom, pos);
8741             }else{
8742                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8743             }
8744             return this;
8745         },
8746
8747         /**
8748          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8749          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8750          * @param {Number} x X value for new position (coordinates are page-based)
8751          * @param {Number} y Y value for new position (coordinates are page-based)
8752          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8753          * @return {Roo.Element} this
8754          */
8755         setLocation : function(x, y, animate){
8756             this.setXY([x, y], this.preanim(arguments, 2));
8757             return this;
8758         },
8759
8760         /**
8761          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8762          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8763          * @param {Number} x X value for new position (coordinates are page-based)
8764          * @param {Number} y Y value for new position (coordinates are page-based)
8765          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8766          * @return {Roo.Element} this
8767          */
8768         moveTo : function(x, y, animate){
8769             this.setXY([x, y], this.preanim(arguments, 2));
8770             return this;
8771         },
8772
8773         /**
8774          * Returns the region of the given element.
8775          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8776          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8777          */
8778         getRegion : function(){
8779             return D.getRegion(this.dom);
8780         },
8781
8782         /**
8783          * Returns the offset height of the element
8784          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8785          * @return {Number} The element's height
8786          */
8787         getHeight : function(contentHeight){
8788             var h = this.dom.offsetHeight || 0;
8789             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8790         },
8791
8792         /**
8793          * Returns the offset width of the element
8794          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8795          * @return {Number} The element's width
8796          */
8797         getWidth : function(contentWidth){
8798             var w = this.dom.offsetWidth || 0;
8799             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8800         },
8801
8802         /**
8803          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8804          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8805          * if a height has not been set using CSS.
8806          * @return {Number}
8807          */
8808         getComputedHeight : function(){
8809             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8810             if(!h){
8811                 h = parseInt(this.getStyle('height'), 10) || 0;
8812                 if(!this.isBorderBox()){
8813                     h += this.getFrameWidth('tb');
8814                 }
8815             }
8816             return h;
8817         },
8818
8819         /**
8820          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8821          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8822          * if a width has not been set using CSS.
8823          * @return {Number}
8824          */
8825         getComputedWidth : function(){
8826             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8827             if(!w){
8828                 w = parseInt(this.getStyle('width'), 10) || 0;
8829                 if(!this.isBorderBox()){
8830                     w += this.getFrameWidth('lr');
8831                 }
8832             }
8833             return w;
8834         },
8835
8836         /**
8837          * Returns the size of the element.
8838          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8839          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8840          */
8841         getSize : function(contentSize){
8842             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8843         },
8844
8845         /**
8846          * Returns the width and height of the viewport.
8847          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8848          */
8849         getViewSize : function(){
8850             var d = this.dom, doc = document, aw = 0, ah = 0;
8851             if(d == doc || d == doc.body){
8852                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8853             }else{
8854                 return {
8855                     width : d.clientWidth,
8856                     height: d.clientHeight
8857                 };
8858             }
8859         },
8860
8861         /**
8862          * Returns the value of the "value" attribute
8863          * @param {Boolean} asNumber true to parse the value as a number
8864          * @return {String/Number}
8865          */
8866         getValue : function(asNumber){
8867             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8868         },
8869
8870         // private
8871         adjustWidth : function(width){
8872             if(typeof width == "number"){
8873                 if(this.autoBoxAdjust && !this.isBorderBox()){
8874                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8875                 }
8876                 if(width < 0){
8877                     width = 0;
8878                 }
8879             }
8880             return width;
8881         },
8882
8883         // private
8884         adjustHeight : function(height){
8885             if(typeof height == "number"){
8886                if(this.autoBoxAdjust && !this.isBorderBox()){
8887                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8888                }
8889                if(height < 0){
8890                    height = 0;
8891                }
8892             }
8893             return height;
8894         },
8895
8896         /**
8897          * Set the width of the element
8898          * @param {Number} width The new width
8899          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8900          * @return {Roo.Element} this
8901          */
8902         setWidth : function(width, animate){
8903             width = this.adjustWidth(width);
8904             if(!animate || !A){
8905                 this.dom.style.width = this.addUnits(width);
8906             }else{
8907                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8908             }
8909             return this;
8910         },
8911
8912         /**
8913          * Set the height of the element
8914          * @param {Number} height The new height
8915          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8916          * @return {Roo.Element} this
8917          */
8918          setHeight : function(height, animate){
8919             height = this.adjustHeight(height);
8920             if(!animate || !A){
8921                 this.dom.style.height = this.addUnits(height);
8922             }else{
8923                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
8924             }
8925             return this;
8926         },
8927
8928         /**
8929          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
8930          * @param {Number} width The new width
8931          * @param {Number} height The new height
8932          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8933          * @return {Roo.Element} this
8934          */
8935          setSize : function(width, height, animate){
8936             if(typeof width == "object"){ // in case of object from getSize()
8937                 height = width.height; width = width.width;
8938             }
8939             width = this.adjustWidth(width); height = this.adjustHeight(height);
8940             if(!animate || !A){
8941                 this.dom.style.width = this.addUnits(width);
8942                 this.dom.style.height = this.addUnits(height);
8943             }else{
8944                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
8945             }
8946             return this;
8947         },
8948
8949         /**
8950          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
8951          * @param {Number} x X value for new position (coordinates are page-based)
8952          * @param {Number} y Y value for new position (coordinates are page-based)
8953          * @param {Number} width The new width
8954          * @param {Number} height The new height
8955          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8956          * @return {Roo.Element} this
8957          */
8958         setBounds : function(x, y, width, height, animate){
8959             if(!animate || !A){
8960                 this.setSize(width, height);
8961                 this.setLocation(x, y);
8962             }else{
8963                 width = this.adjustWidth(width); height = this.adjustHeight(height);
8964                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
8965                               this.preanim(arguments, 4), 'motion');
8966             }
8967             return this;
8968         },
8969
8970         /**
8971          * 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.
8972          * @param {Roo.lib.Region} region The region to fill
8973          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8974          * @return {Roo.Element} this
8975          */
8976         setRegion : function(region, animate){
8977             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
8978             return this;
8979         },
8980
8981         /**
8982          * Appends an event handler
8983          *
8984          * @param {String}   eventName     The type of event to append
8985          * @param {Function} fn        The method the event invokes
8986          * @param {Object} scope       (optional) The scope (this object) of the fn
8987          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
8988          */
8989         addListener : function(eventName, fn, scope, options)
8990         {
8991             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
8992                 this.addListener('touchstart', this.onTapHandler, this);
8993             }
8994             
8995             // we need to handle a special case where dom element is a svg element.
8996             // in this case we do not actua
8997             if (!this.dom) {
8998                 return;
8999             }
9000             
9001             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9002                 if (typeof(this.listeners[eventName]) == 'undefined') {
9003                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9004                 }
9005                 this.listeners[eventName].addListener(fn, scope, options);
9006                 return;
9007             }
9008             
9009                 
9010             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9011             
9012             
9013         },
9014         tapedTwice : false,
9015         onTapHandler : function(event)
9016         {
9017             if(!this.tapedTwice) {
9018                 this.tapedTwice = true;
9019                 var s = this;
9020                 setTimeout( function() {
9021                     s.tapedTwice = false;
9022                 }, 300 );
9023                 return;
9024             }
9025             event.preventDefault();
9026             var revent = new MouseEvent('dblclick',  {
9027                 view: window,
9028                 bubbles: true,
9029                 cancelable: true
9030             });
9031              
9032             this.dom.dispatchEvent(revent);
9033             //action on double tap goes below
9034              
9035         }, 
9036  
9037         /**
9038          * Removes an event handler from this element
9039          * @param {String} eventName the type of event to remove
9040          * @param {Function} fn the method the event invokes
9041          * @param {Function} scope (needed for svg fake listeners)
9042          * @return {Roo.Element} this
9043          */
9044         removeListener : function(eventName, fn, scope){
9045             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9046             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9047                 return this;
9048             }
9049             this.listeners[eventName].removeListener(fn, scope);
9050             return this;
9051         },
9052
9053         /**
9054          * Removes all previous added listeners from this element
9055          * @return {Roo.Element} this
9056          */
9057         removeAllListeners : function(){
9058             E.purgeElement(this.dom);
9059             this.listeners = {};
9060             return this;
9061         },
9062
9063         relayEvent : function(eventName, observable){
9064             this.on(eventName, function(e){
9065                 observable.fireEvent(eventName, e);
9066             });
9067         },
9068
9069         
9070         /**
9071          * Set the opacity of the element
9072          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9073          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9074          * @return {Roo.Element} this
9075          */
9076          setOpacity : function(opacity, animate){
9077             if(!animate || !A){
9078                 var s = this.dom.style;
9079                 if(Roo.isIE){
9080                     s.zoom = 1;
9081                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9082                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9083                 }else{
9084                     s.opacity = opacity;
9085                 }
9086             }else{
9087                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9088             }
9089             return this;
9090         },
9091
9092         /**
9093          * Gets the left X coordinate
9094          * @param {Boolean} local True to get the local css position instead of page coordinate
9095          * @return {Number}
9096          */
9097         getLeft : function(local){
9098             if(!local){
9099                 return this.getX();
9100             }else{
9101                 return parseInt(this.getStyle("left"), 10) || 0;
9102             }
9103         },
9104
9105         /**
9106          * Gets the right X coordinate of the element (element X position + element width)
9107          * @param {Boolean} local True to get the local css position instead of page coordinate
9108          * @return {Number}
9109          */
9110         getRight : function(local){
9111             if(!local){
9112                 return this.getX() + this.getWidth();
9113             }else{
9114                 return (this.getLeft(true) + this.getWidth()) || 0;
9115             }
9116         },
9117
9118         /**
9119          * Gets the top Y coordinate
9120          * @param {Boolean} local True to get the local css position instead of page coordinate
9121          * @return {Number}
9122          */
9123         getTop : function(local) {
9124             if(!local){
9125                 return this.getY();
9126             }else{
9127                 return parseInt(this.getStyle("top"), 10) || 0;
9128             }
9129         },
9130
9131         /**
9132          * Gets the bottom Y coordinate of the element (element Y position + element height)
9133          * @param {Boolean} local True to get the local css position instead of page coordinate
9134          * @return {Number}
9135          */
9136         getBottom : function(local){
9137             if(!local){
9138                 return this.getY() + this.getHeight();
9139             }else{
9140                 return (this.getTop(true) + this.getHeight()) || 0;
9141             }
9142         },
9143
9144         /**
9145         * Initializes positioning on this element. If a desired position is not passed, it will make the
9146         * the element positioned relative IF it is not already positioned.
9147         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9148         * @param {Number} zIndex (optional) The zIndex to apply
9149         * @param {Number} x (optional) Set the page X position
9150         * @param {Number} y (optional) Set the page Y position
9151         */
9152         position : function(pos, zIndex, x, y){
9153             if(!pos){
9154                if(this.getStyle('position') == 'static'){
9155                    this.setStyle('position', 'relative');
9156                }
9157             }else{
9158                 this.setStyle("position", pos);
9159             }
9160             if(zIndex){
9161                 this.setStyle("z-index", zIndex);
9162             }
9163             if(x !== undefined && y !== undefined){
9164                 this.setXY([x, y]);
9165             }else if(x !== undefined){
9166                 this.setX(x);
9167             }else if(y !== undefined){
9168                 this.setY(y);
9169             }
9170         },
9171
9172         /**
9173         * Clear positioning back to the default when the document was loaded
9174         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9175         * @return {Roo.Element} this
9176          */
9177         clearPositioning : function(value){
9178             value = value ||'';
9179             this.setStyle({
9180                 "left": value,
9181                 "right": value,
9182                 "top": value,
9183                 "bottom": value,
9184                 "z-index": "",
9185                 "position" : "static"
9186             });
9187             return this;
9188         },
9189
9190         /**
9191         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9192         * snapshot before performing an update and then restoring the element.
9193         * @return {Object}
9194         */
9195         getPositioning : function(){
9196             var l = this.getStyle("left");
9197             var t = this.getStyle("top");
9198             return {
9199                 "position" : this.getStyle("position"),
9200                 "left" : l,
9201                 "right" : l ? "" : this.getStyle("right"),
9202                 "top" : t,
9203                 "bottom" : t ? "" : this.getStyle("bottom"),
9204                 "z-index" : this.getStyle("z-index")
9205             };
9206         },
9207
9208         /**
9209          * Gets the width of the border(s) for the specified side(s)
9210          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9211          * passing lr would get the border (l)eft width + the border (r)ight width.
9212          * @return {Number} The width of the sides passed added together
9213          */
9214         getBorderWidth : function(side){
9215             return this.addStyles(side, El.borders);
9216         },
9217
9218         /**
9219          * Gets the width of the padding(s) for the specified side(s)
9220          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9221          * passing lr would get the padding (l)eft + the padding (r)ight.
9222          * @return {Number} The padding of the sides passed added together
9223          */
9224         getPadding : function(side){
9225             return this.addStyles(side, El.paddings);
9226         },
9227
9228         /**
9229         * Set positioning with an object returned by getPositioning().
9230         * @param {Object} posCfg
9231         * @return {Roo.Element} this
9232          */
9233         setPositioning : function(pc){
9234             this.applyStyles(pc);
9235             if(pc.right == "auto"){
9236                 this.dom.style.right = "";
9237             }
9238             if(pc.bottom == "auto"){
9239                 this.dom.style.bottom = "";
9240             }
9241             return this;
9242         },
9243
9244         // private
9245         fixDisplay : function(){
9246             if(this.getStyle("display") == "none"){
9247                 this.setStyle("visibility", "hidden");
9248                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9249                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9250                     this.setStyle("display", "block");
9251                 }
9252             }
9253         },
9254
9255         /**
9256          * Quick set left and top adding default units
9257          * @param {String} left The left CSS property value
9258          * @param {String} top The top CSS property value
9259          * @return {Roo.Element} this
9260          */
9261          setLeftTop : function(left, top){
9262             this.dom.style.left = this.addUnits(left);
9263             this.dom.style.top = this.addUnits(top);
9264             return this;
9265         },
9266
9267         /**
9268          * Move this element relative to its current position.
9269          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9270          * @param {Number} distance How far to move the element in pixels
9271          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9272          * @return {Roo.Element} this
9273          */
9274          move : function(direction, distance, animate){
9275             var xy = this.getXY();
9276             direction = direction.toLowerCase();
9277             switch(direction){
9278                 case "l":
9279                 case "left":
9280                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9281                     break;
9282                case "r":
9283                case "right":
9284                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9285                     break;
9286                case "t":
9287                case "top":
9288                case "up":
9289                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9290                     break;
9291                case "b":
9292                case "bottom":
9293                case "down":
9294                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9295                     break;
9296             }
9297             return this;
9298         },
9299
9300         /**
9301          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9302          * @return {Roo.Element} this
9303          */
9304         clip : function(){
9305             if(!this.isClipped){
9306                this.isClipped = true;
9307                this.originalClip = {
9308                    "o": this.getStyle("overflow"),
9309                    "x": this.getStyle("overflow-x"),
9310                    "y": this.getStyle("overflow-y")
9311                };
9312                this.setStyle("overflow", "hidden");
9313                this.setStyle("overflow-x", "hidden");
9314                this.setStyle("overflow-y", "hidden");
9315             }
9316             return this;
9317         },
9318
9319         /**
9320          *  Return clipping (overflow) to original clipping before clip() was called
9321          * @return {Roo.Element} this
9322          */
9323         unclip : function(){
9324             if(this.isClipped){
9325                 this.isClipped = false;
9326                 var o = this.originalClip;
9327                 if(o.o){this.setStyle("overflow", o.o);}
9328                 if(o.x){this.setStyle("overflow-x", o.x);}
9329                 if(o.y){this.setStyle("overflow-y", o.y);}
9330             }
9331             return this;
9332         },
9333
9334
9335         /**
9336          * Gets the x,y coordinates specified by the anchor position on the element.
9337          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9338          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9339          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9340          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9341          * @return {Array} [x, y] An array containing the element's x and y coordinates
9342          */
9343         getAnchorXY : function(anchor, local, s){
9344             //Passing a different size is useful for pre-calculating anchors,
9345             //especially for anchored animations that change the el size.
9346
9347             var w, h, vp = false;
9348             if(!s){
9349                 var d = this.dom;
9350                 if(d == document.body || d == document){
9351                     vp = true;
9352                     w = D.getViewWidth(); h = D.getViewHeight();
9353                 }else{
9354                     w = this.getWidth(); h = this.getHeight();
9355                 }
9356             }else{
9357                 w = s.width;  h = s.height;
9358             }
9359             var x = 0, y = 0, r = Math.round;
9360             switch((anchor || "tl").toLowerCase()){
9361                 case "c":
9362                     x = r(w*.5);
9363                     y = r(h*.5);
9364                 break;
9365                 case "t":
9366                     x = r(w*.5);
9367                     y = 0;
9368                 break;
9369                 case "l":
9370                     x = 0;
9371                     y = r(h*.5);
9372                 break;
9373                 case "r":
9374                     x = w;
9375                     y = r(h*.5);
9376                 break;
9377                 case "b":
9378                     x = r(w*.5);
9379                     y = h;
9380                 break;
9381                 case "tl":
9382                     x = 0;
9383                     y = 0;
9384                 break;
9385                 case "bl":
9386                     x = 0;
9387                     y = h;
9388                 break;
9389                 case "br":
9390                     x = w;
9391                     y = h;
9392                 break;
9393                 case "tr":
9394                     x = w;
9395                     y = 0;
9396                 break;
9397             }
9398             if(local === true){
9399                 return [x, y];
9400             }
9401             if(vp){
9402                 var sc = this.getScroll();
9403                 return [x + sc.left, y + sc.top];
9404             }
9405             //Add the element's offset xy
9406             var o = this.getXY();
9407             return [x+o[0], y+o[1]];
9408         },
9409
9410         /**
9411          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9412          * supported position values.
9413          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9414          * @param {String} position The position to align to.
9415          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9416          * @return {Array} [x, y]
9417          */
9418         getAlignToXY : function(el, p, o)
9419         {
9420             el = Roo.get(el);
9421             var d = this.dom;
9422             if(!el.dom){
9423                 throw "Element.alignTo with an element that doesn't exist";
9424             }
9425             var c = false; //constrain to viewport
9426             var p1 = "", p2 = "";
9427             o = o || [0,0];
9428
9429             if(!p){
9430                 p = "tl-bl";
9431             }else if(p == "?"){
9432                 p = "tl-bl?";
9433             }else if(p.indexOf("-") == -1){
9434                 p = "tl-" + p;
9435             }
9436             p = p.toLowerCase();
9437             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9438             if(!m){
9439                throw "Element.alignTo with an invalid alignment " + p;
9440             }
9441             p1 = m[1]; p2 = m[2]; c = !!m[3];
9442
9443             //Subtract the aligned el's internal xy from the target's offset xy
9444             //plus custom offset to get the aligned el's new offset xy
9445             var a1 = this.getAnchorXY(p1, true);
9446             var a2 = el.getAnchorXY(p2, false);
9447             var x = a2[0] - a1[0] + o[0];
9448             var y = a2[1] - a1[1] + o[1];
9449             if(c){
9450                 //constrain the aligned el to viewport if necessary
9451                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9452                 // 5px of margin for ie
9453                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9454
9455                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9456                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9457                 //otherwise swap the aligned el to the opposite border of the target.
9458                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9459                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9460                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9461                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9462
9463                var doc = document;
9464                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9465                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9466
9467                if((x+w) > dw + scrollX){
9468                     x = swapX ? r.left-w : dw+scrollX-w;
9469                 }
9470                if(x < scrollX){
9471                    x = swapX ? r.right : scrollX;
9472                }
9473                if((y+h) > dh + scrollY){
9474                     y = swapY ? r.top-h : dh+scrollY-h;
9475                 }
9476                if (y < scrollY){
9477                    y = swapY ? r.bottom : scrollY;
9478                }
9479             }
9480             return [x,y];
9481         },
9482
9483         // private
9484         getConstrainToXY : function(){
9485             var os = {top:0, left:0, bottom:0, right: 0};
9486
9487             return function(el, local, offsets, proposedXY){
9488                 el = Roo.get(el);
9489                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9490
9491                 var vw, vh, vx = 0, vy = 0;
9492                 if(el.dom == document.body || el.dom == document){
9493                     vw = Roo.lib.Dom.getViewWidth();
9494                     vh = Roo.lib.Dom.getViewHeight();
9495                 }else{
9496                     vw = el.dom.clientWidth;
9497                     vh = el.dom.clientHeight;
9498                     if(!local){
9499                         var vxy = el.getXY();
9500                         vx = vxy[0];
9501                         vy = vxy[1];
9502                     }
9503                 }
9504
9505                 var s = el.getScroll();
9506
9507                 vx += offsets.left + s.left;
9508                 vy += offsets.top + s.top;
9509
9510                 vw -= offsets.right;
9511                 vh -= offsets.bottom;
9512
9513                 var vr = vx+vw;
9514                 var vb = vy+vh;
9515
9516                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9517                 var x = xy[0], y = xy[1];
9518                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9519
9520                 // only move it if it needs it
9521                 var moved = false;
9522
9523                 // first validate right/bottom
9524                 if((x + w) > vr){
9525                     x = vr - w;
9526                     moved = true;
9527                 }
9528                 if((y + h) > vb){
9529                     y = vb - h;
9530                     moved = true;
9531                 }
9532                 // then make sure top/left isn't negative
9533                 if(x < vx){
9534                     x = vx;
9535                     moved = true;
9536                 }
9537                 if(y < vy){
9538                     y = vy;
9539                     moved = true;
9540                 }
9541                 return moved ? [x, y] : false;
9542             };
9543         }(),
9544
9545         // private
9546         adjustForConstraints : function(xy, parent, offsets){
9547             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9548         },
9549
9550         /**
9551          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9552          * document it aligns it to the viewport.
9553          * The position parameter is optional, and can be specified in any one of the following formats:
9554          * <ul>
9555          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9556          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9557          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9558          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9559          *   <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
9560          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9561          * </ul>
9562          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9563          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9564          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9565          * that specified in order to enforce the viewport constraints.
9566          * Following are all of the supported anchor positions:
9567     <pre>
9568     Value  Description
9569     -----  -----------------------------
9570     tl     The top left corner (default)
9571     t      The center of the top edge
9572     tr     The top right corner
9573     l      The center of the left edge
9574     c      In the center of the element
9575     r      The center of the right edge
9576     bl     The bottom left corner
9577     b      The center of the bottom edge
9578     br     The bottom right corner
9579     </pre>
9580     Example Usage:
9581     <pre><code>
9582     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9583     el.alignTo("other-el");
9584
9585     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9586     el.alignTo("other-el", "tr?");
9587
9588     // align the bottom right corner of el with the center left edge of other-el
9589     el.alignTo("other-el", "br-l?");
9590
9591     // align the center of el with the bottom left corner of other-el and
9592     // adjust the x position by -6 pixels (and the y position by 0)
9593     el.alignTo("other-el", "c-bl", [-6, 0]);
9594     </code></pre>
9595          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9596          * @param {String} position The position to align to.
9597          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9598          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9599          * @return {Roo.Element} this
9600          */
9601         alignTo : function(element, position, offsets, animate){
9602             var xy = this.getAlignToXY(element, position, offsets);
9603             this.setXY(xy, this.preanim(arguments, 3));
9604             return this;
9605         },
9606
9607         /**
9608          * Anchors an element to another element and realigns it when the window is resized.
9609          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9610          * @param {String} position The position to align to.
9611          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9612          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9613          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9614          * is a number, it is used as the buffer delay (defaults to 50ms).
9615          * @param {Function} callback The function to call after the animation finishes
9616          * @return {Roo.Element} this
9617          */
9618         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9619             var action = function(){
9620                 this.alignTo(el, alignment, offsets, animate);
9621                 Roo.callback(callback, this);
9622             };
9623             Roo.EventManager.onWindowResize(action, this);
9624             var tm = typeof monitorScroll;
9625             if(tm != 'undefined'){
9626                 Roo.EventManager.on(window, 'scroll', action, this,
9627                     {buffer: tm == 'number' ? monitorScroll : 50});
9628             }
9629             action.call(this); // align immediately
9630             return this;
9631         },
9632         /**
9633          * Clears any opacity settings from this element. Required in some cases for IE.
9634          * @return {Roo.Element} this
9635          */
9636         clearOpacity : function(){
9637             if (window.ActiveXObject) {
9638                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9639                     this.dom.style.filter = "";
9640                 }
9641             } else {
9642                 this.dom.style.opacity = "";
9643                 this.dom.style["-moz-opacity"] = "";
9644                 this.dom.style["-khtml-opacity"] = "";
9645             }
9646             return this;
9647         },
9648
9649         /**
9650          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9651          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9652          * @return {Roo.Element} this
9653          */
9654         hide : function(animate){
9655             this.setVisible(false, this.preanim(arguments, 0));
9656             return this;
9657         },
9658
9659         /**
9660         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9661         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9662          * @return {Roo.Element} this
9663          */
9664         show : function(animate){
9665             this.setVisible(true, this.preanim(arguments, 0));
9666             return this;
9667         },
9668
9669         /**
9670          * @private Test if size has a unit, otherwise appends the default
9671          */
9672         addUnits : function(size){
9673             return Roo.Element.addUnits(size, this.defaultUnit);
9674         },
9675
9676         /**
9677          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9678          * @return {Roo.Element} this
9679          */
9680         beginMeasure : function(){
9681             var el = this.dom;
9682             if(el.offsetWidth || el.offsetHeight){
9683                 return this; // offsets work already
9684             }
9685             var changed = [];
9686             var p = this.dom, b = document.body; // start with this element
9687             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9688                 var pe = Roo.get(p);
9689                 if(pe.getStyle('display') == 'none'){
9690                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9691                     p.style.visibility = "hidden";
9692                     p.style.display = "block";
9693                 }
9694                 p = p.parentNode;
9695             }
9696             this._measureChanged = changed;
9697             return this;
9698
9699         },
9700
9701         /**
9702          * Restores displays to before beginMeasure was called
9703          * @return {Roo.Element} this
9704          */
9705         endMeasure : function(){
9706             var changed = this._measureChanged;
9707             if(changed){
9708                 for(var i = 0, len = changed.length; i < len; i++) {
9709                     var r = changed[i];
9710                     r.el.style.visibility = r.visibility;
9711                     r.el.style.display = "none";
9712                 }
9713                 this._measureChanged = null;
9714             }
9715             return this;
9716         },
9717
9718         /**
9719         * Update the innerHTML of this element, optionally searching for and processing scripts
9720         * @param {String} html The new HTML
9721         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9722         * @param {Function} callback For async script loading you can be noticed when the update completes
9723         * @return {Roo.Element} this
9724          */
9725         update : function(html, loadScripts, callback){
9726             if(typeof html == "undefined"){
9727                 html = "";
9728             }
9729             if(loadScripts !== true){
9730                 this.dom.innerHTML = html;
9731                 if(typeof callback == "function"){
9732                     callback();
9733                 }
9734                 return this;
9735             }
9736             var id = Roo.id();
9737             var dom = this.dom;
9738
9739             html += '<span id="' + id + '"></span>';
9740
9741             E.onAvailable(id, function(){
9742                 var hd = document.getElementsByTagName("head")[0];
9743                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9744                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9745                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9746
9747                 var match;
9748                 while(match = re.exec(html)){
9749                     var attrs = match[1];
9750                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9751                     if(srcMatch && srcMatch[2]){
9752                        var s = document.createElement("script");
9753                        s.src = srcMatch[2];
9754                        var typeMatch = attrs.match(typeRe);
9755                        if(typeMatch && typeMatch[2]){
9756                            s.type = typeMatch[2];
9757                        }
9758                        hd.appendChild(s);
9759                     }else if(match[2] && match[2].length > 0){
9760                         if(window.execScript) {
9761                            window.execScript(match[2]);
9762                         } else {
9763                             /**
9764                              * eval:var:id
9765                              * eval:var:dom
9766                              * eval:var:html
9767                              * 
9768                              */
9769                            window.eval(match[2]);
9770                         }
9771                     }
9772                 }
9773                 var el = document.getElementById(id);
9774                 if(el){el.parentNode.removeChild(el);}
9775                 if(typeof callback == "function"){
9776                     callback();
9777                 }
9778             });
9779             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9780             return this;
9781         },
9782
9783         /**
9784          * Direct access to the UpdateManager update() method (takes the same parameters).
9785          * @param {String/Function} url The url for this request or a function to call to get the url
9786          * @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}
9787          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9788          * @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.
9789          * @return {Roo.Element} this
9790          */
9791         load : function(){
9792             var um = this.getUpdateManager();
9793             um.update.apply(um, arguments);
9794             return this;
9795         },
9796
9797         /**
9798         * Gets this element's UpdateManager
9799         * @return {Roo.UpdateManager} The UpdateManager
9800         */
9801         getUpdateManager : function(){
9802             if(!this.updateManager){
9803                 this.updateManager = new Roo.UpdateManager(this);
9804             }
9805             return this.updateManager;
9806         },
9807
9808         /**
9809          * Disables text selection for this element (normalized across browsers)
9810          * @return {Roo.Element} this
9811          */
9812         unselectable : function(){
9813             this.dom.unselectable = "on";
9814             this.swallowEvent("selectstart", true);
9815             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9816             this.addClass("x-unselectable");
9817             return this;
9818         },
9819
9820         /**
9821         * Calculates the x, y to center this element on the screen
9822         * @return {Array} The x, y values [x, y]
9823         */
9824         getCenterXY : function(){
9825             return this.getAlignToXY(document, 'c-c');
9826         },
9827
9828         /**
9829         * Centers the Element in either the viewport, or another Element.
9830         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9831         */
9832         center : function(centerIn){
9833             this.alignTo(centerIn || document, 'c-c');
9834             return this;
9835         },
9836
9837         /**
9838          * Tests various css rules/browsers to determine if this element uses a border box
9839          * @return {Boolean}
9840          */
9841         isBorderBox : function(){
9842             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9843         },
9844
9845         /**
9846          * Return a box {x, y, width, height} that can be used to set another elements
9847          * size/location to match this element.
9848          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9849          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9850          * @return {Object} box An object in the format {x, y, width, height}
9851          */
9852         getBox : function(contentBox, local){
9853             var xy;
9854             if(!local){
9855                 xy = this.getXY();
9856             }else{
9857                 var left = parseInt(this.getStyle("left"), 10) || 0;
9858                 var top = parseInt(this.getStyle("top"), 10) || 0;
9859                 xy = [left, top];
9860             }
9861             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9862             if(!contentBox){
9863                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9864             }else{
9865                 var l = this.getBorderWidth("l")+this.getPadding("l");
9866                 var r = this.getBorderWidth("r")+this.getPadding("r");
9867                 var t = this.getBorderWidth("t")+this.getPadding("t");
9868                 var b = this.getBorderWidth("b")+this.getPadding("b");
9869                 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)};
9870             }
9871             bx.right = bx.x + bx.width;
9872             bx.bottom = bx.y + bx.height;
9873             return bx;
9874         },
9875
9876         /**
9877          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9878          for more information about the sides.
9879          * @param {String} sides
9880          * @return {Number}
9881          */
9882         getFrameWidth : function(sides, onlyContentBox){
9883             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9884         },
9885
9886         /**
9887          * 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.
9888          * @param {Object} box The box to fill {x, y, width, height}
9889          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9890          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9891          * @return {Roo.Element} this
9892          */
9893         setBox : function(box, adjust, animate){
9894             var w = box.width, h = box.height;
9895             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9896                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9897                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9898             }
9899             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9900             return this;
9901         },
9902
9903         /**
9904          * Forces the browser to repaint this element
9905          * @return {Roo.Element} this
9906          */
9907          repaint : function(){
9908             var dom = this.dom;
9909             this.addClass("x-repaint");
9910             setTimeout(function(){
9911                 Roo.get(dom).removeClass("x-repaint");
9912             }, 1);
9913             return this;
9914         },
9915
9916         /**
9917          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9918          * then it returns the calculated width of the sides (see getPadding)
9919          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9920          * @return {Object/Number}
9921          */
9922         getMargins : function(side){
9923             if(!side){
9924                 return {
9925                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
9926                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
9927                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
9928                     right: parseInt(this.getStyle("margin-right"), 10) || 0
9929                 };
9930             }else{
9931                 return this.addStyles(side, El.margins);
9932              }
9933         },
9934
9935         // private
9936         addStyles : function(sides, styles){
9937             var val = 0, v, w;
9938             for(var i = 0, len = sides.length; i < len; i++){
9939                 v = this.getStyle(styles[sides.charAt(i)]);
9940                 if(v){
9941                      w = parseInt(v, 10);
9942                      if(w){ val += w; }
9943                 }
9944             }
9945             return val;
9946         },
9947
9948         /**
9949          * Creates a proxy element of this element
9950          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
9951          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
9952          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
9953          * @return {Roo.Element} The new proxy element
9954          */
9955         createProxy : function(config, renderTo, matchBox){
9956             if(renderTo){
9957                 renderTo = Roo.getDom(renderTo);
9958             }else{
9959                 renderTo = document.body;
9960             }
9961             config = typeof config == "object" ?
9962                 config : {tag : "div", cls: config};
9963             var proxy = Roo.DomHelper.append(renderTo, config, true);
9964             if(matchBox){
9965                proxy.setBox(this.getBox());
9966             }
9967             return proxy;
9968         },
9969
9970         /**
9971          * Puts a mask over this element to disable user interaction. Requires core.css.
9972          * This method can only be applied to elements which accept child nodes.
9973          * @param {String} msg (optional) A message to display in the mask
9974          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
9975          * @return {Element} The mask  element
9976          */
9977         mask : function(msg, msgCls)
9978         {
9979             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
9980                 this.setStyle("position", "relative");
9981             }
9982             if(!this._mask){
9983                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
9984             }
9985             
9986             this.addClass("x-masked");
9987             this._mask.setDisplayed(true);
9988             
9989             // we wander
9990             var z = 0;
9991             var dom = this.dom;
9992             while (dom && dom.style) {
9993                 if (!isNaN(parseInt(dom.style.zIndex))) {
9994                     z = Math.max(z, parseInt(dom.style.zIndex));
9995                 }
9996                 dom = dom.parentNode;
9997             }
9998             // if we are masking the body - then it hides everything..
9999             if (this.dom == document.body) {
10000                 z = 1000000;
10001                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10002                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10003             }
10004            
10005             if(typeof msg == 'string'){
10006                 if(!this._maskMsg){
10007                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10008                         cls: "roo-el-mask-msg", 
10009                         cn: [
10010                             {
10011                                 tag: 'i',
10012                                 cls: 'fa fa-spinner fa-spin'
10013                             },
10014                             {
10015                                 tag: 'div'
10016                             }   
10017                         ]
10018                     }, true);
10019                 }
10020                 var mm = this._maskMsg;
10021                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10022                 if (mm.dom.lastChild) { // weird IE issue?
10023                     mm.dom.lastChild.innerHTML = msg;
10024                 }
10025                 mm.setDisplayed(true);
10026                 mm.center(this);
10027                 mm.setStyle('z-index', z + 102);
10028             }
10029             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10030                 this._mask.setHeight(this.getHeight());
10031             }
10032             this._mask.setStyle('z-index', z + 100);
10033             
10034             return this._mask;
10035         },
10036
10037         /**
10038          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10039          * it is cached for reuse.
10040          */
10041         unmask : function(removeEl){
10042             if(this._mask){
10043                 if(removeEl === true){
10044                     this._mask.remove();
10045                     delete this._mask;
10046                     if(this._maskMsg){
10047                         this._maskMsg.remove();
10048                         delete this._maskMsg;
10049                     }
10050                 }else{
10051                     this._mask.setDisplayed(false);
10052                     if(this._maskMsg){
10053                         this._maskMsg.setDisplayed(false);
10054                     }
10055                 }
10056             }
10057             this.removeClass("x-masked");
10058         },
10059
10060         /**
10061          * Returns true if this element is masked
10062          * @return {Boolean}
10063          */
10064         isMasked : function(){
10065             return this._mask && this._mask.isVisible();
10066         },
10067
10068         /**
10069          * Creates an iframe shim for this element to keep selects and other windowed objects from
10070          * showing through.
10071          * @return {Roo.Element} The new shim element
10072          */
10073         createShim : function(){
10074             var el = document.createElement('iframe');
10075             el.frameBorder = 'no';
10076             el.className = 'roo-shim';
10077             if(Roo.isIE && Roo.isSecure){
10078                 el.src = Roo.SSL_SECURE_URL;
10079             }
10080             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10081             shim.autoBoxAdjust = false;
10082             return shim;
10083         },
10084
10085         /**
10086          * Removes this element from the DOM and deletes it from the cache
10087          */
10088         remove : function(){
10089             if(this.dom.parentNode){
10090                 this.dom.parentNode.removeChild(this.dom);
10091             }
10092             delete El.cache[this.dom.id];
10093         },
10094
10095         /**
10096          * Sets up event handlers to add and remove a css class when the mouse is over this element
10097          * @param {String} className
10098          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10099          * mouseout events for children elements
10100          * @return {Roo.Element} this
10101          */
10102         addClassOnOver : function(className, preventFlicker){
10103             this.on("mouseover", function(){
10104                 Roo.fly(this, '_internal').addClass(className);
10105             }, this.dom);
10106             var removeFn = function(e){
10107                 if(preventFlicker !== true || !e.within(this, true)){
10108                     Roo.fly(this, '_internal').removeClass(className);
10109                 }
10110             };
10111             this.on("mouseout", removeFn, this.dom);
10112             return this;
10113         },
10114
10115         /**
10116          * Sets up event handlers to add and remove a css class when this element has the focus
10117          * @param {String} className
10118          * @return {Roo.Element} this
10119          */
10120         addClassOnFocus : function(className){
10121             this.on("focus", function(){
10122                 Roo.fly(this, '_internal').addClass(className);
10123             }, this.dom);
10124             this.on("blur", function(){
10125                 Roo.fly(this, '_internal').removeClass(className);
10126             }, this.dom);
10127             return this;
10128         },
10129         /**
10130          * 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)
10131          * @param {String} className
10132          * @return {Roo.Element} this
10133          */
10134         addClassOnClick : function(className){
10135             var dom = this.dom;
10136             this.on("mousedown", function(){
10137                 Roo.fly(dom, '_internal').addClass(className);
10138                 var d = Roo.get(document);
10139                 var fn = function(){
10140                     Roo.fly(dom, '_internal').removeClass(className);
10141                     d.removeListener("mouseup", fn);
10142                 };
10143                 d.on("mouseup", fn);
10144             });
10145             return this;
10146         },
10147
10148         /**
10149          * Stops the specified event from bubbling and optionally prevents the default action
10150          * @param {String} eventName
10151          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10152          * @return {Roo.Element} this
10153          */
10154         swallowEvent : function(eventName, preventDefault){
10155             var fn = function(e){
10156                 e.stopPropagation();
10157                 if(preventDefault){
10158                     e.preventDefault();
10159                 }
10160             };
10161             if(eventName instanceof Array){
10162                 for(var i = 0, len = eventName.length; i < len; i++){
10163                      this.on(eventName[i], fn);
10164                 }
10165                 return this;
10166             }
10167             this.on(eventName, fn);
10168             return this;
10169         },
10170
10171         /**
10172          * @private
10173          */
10174         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10175
10176         /**
10177          * Sizes this element to its parent element's dimensions performing
10178          * neccessary box adjustments.
10179          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10180          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10181          * @return {Roo.Element} this
10182          */
10183         fitToParent : function(monitorResize, targetParent) {
10184           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10185           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10186           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10187             return this;
10188           }
10189           var p = Roo.get(targetParent || this.dom.parentNode);
10190           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10191           if (monitorResize === true) {
10192             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10193             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10194           }
10195           return this;
10196         },
10197
10198         /**
10199          * Gets the next sibling, skipping text nodes
10200          * @return {HTMLElement} The next sibling or null
10201          */
10202         getNextSibling : function(){
10203             var n = this.dom.nextSibling;
10204             while(n && n.nodeType != 1){
10205                 n = n.nextSibling;
10206             }
10207             return n;
10208         },
10209
10210         /**
10211          * Gets the previous sibling, skipping text nodes
10212          * @return {HTMLElement} The previous sibling or null
10213          */
10214         getPrevSibling : function(){
10215             var n = this.dom.previousSibling;
10216             while(n && n.nodeType != 1){
10217                 n = n.previousSibling;
10218             }
10219             return n;
10220         },
10221
10222
10223         /**
10224          * Appends the passed element(s) to this element
10225          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10226          * @return {Roo.Element} this
10227          */
10228         appendChild: function(el){
10229             el = Roo.get(el);
10230             el.appendTo(this);
10231             return this;
10232         },
10233
10234         /**
10235          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10236          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10237          * automatically generated with the specified attributes.
10238          * @param {HTMLElement} insertBefore (optional) a child element of this element
10239          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10240          * @return {Roo.Element} The new child element
10241          */
10242         createChild: function(config, insertBefore, returnDom){
10243             config = config || {tag:'div'};
10244             if(insertBefore){
10245                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10246             }
10247             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10248         },
10249
10250         /**
10251          * Appends this element to the passed element
10252          * @param {String/HTMLElement/Element} el The new parent element
10253          * @return {Roo.Element} this
10254          */
10255         appendTo: function(el){
10256             el = Roo.getDom(el);
10257             el.appendChild(this.dom);
10258             return this;
10259         },
10260
10261         /**
10262          * Inserts this element before the passed element in the DOM
10263          * @param {String/HTMLElement/Element} el The element to insert before
10264          * @return {Roo.Element} this
10265          */
10266         insertBefore: function(el){
10267             el = Roo.getDom(el);
10268             el.parentNode.insertBefore(this.dom, el);
10269             return this;
10270         },
10271
10272         /**
10273          * Inserts this element after the passed element in the DOM
10274          * @param {String/HTMLElement/Element} el The element to insert after
10275          * @return {Roo.Element} this
10276          */
10277         insertAfter: function(el){
10278             el = Roo.getDom(el);
10279             el.parentNode.insertBefore(this.dom, el.nextSibling);
10280             return this;
10281         },
10282
10283         /**
10284          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10285          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10286          * @return {Roo.Element} The new child
10287          */
10288         insertFirst: function(el, returnDom){
10289             el = el || {};
10290             if(typeof el == 'object' && !el.nodeType){ // dh config
10291                 return this.createChild(el, this.dom.firstChild, returnDom);
10292             }else{
10293                 el = Roo.getDom(el);
10294                 this.dom.insertBefore(el, this.dom.firstChild);
10295                 return !returnDom ? Roo.get(el) : el;
10296             }
10297         },
10298
10299         /**
10300          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10301          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10302          * @param {String} where (optional) 'before' or 'after' defaults to before
10303          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10304          * @return {Roo.Element} the inserted Element
10305          */
10306         insertSibling: function(el, where, returnDom){
10307             where = where ? where.toLowerCase() : 'before';
10308             el = el || {};
10309             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10310
10311             if(typeof el == 'object' && !el.nodeType){ // dh config
10312                 if(where == 'after' && !this.dom.nextSibling){
10313                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10314                 }else{
10315                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10316                 }
10317
10318             }else{
10319                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10320                             where == 'before' ? this.dom : this.dom.nextSibling);
10321                 if(!returnDom){
10322                     rt = Roo.get(rt);
10323                 }
10324             }
10325             return rt;
10326         },
10327
10328         /**
10329          * Creates and wraps this element with another element
10330          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10331          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10332          * @return {HTMLElement/Element} The newly created wrapper element
10333          */
10334         wrap: function(config, returnDom){
10335             if(!config){
10336                 config = {tag: "div"};
10337             }
10338             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10339             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10340             return newEl;
10341         },
10342
10343         /**
10344          * Replaces the passed element with this element
10345          * @param {String/HTMLElement/Element} el The element to replace
10346          * @return {Roo.Element} this
10347          */
10348         replace: function(el){
10349             el = Roo.get(el);
10350             this.insertBefore(el);
10351             el.remove();
10352             return this;
10353         },
10354
10355         /**
10356          * Inserts an html fragment into this element
10357          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10358          * @param {String} html The HTML fragment
10359          * @param {Boolean} returnEl True to return an Roo.Element
10360          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10361          */
10362         insertHtml : function(where, html, returnEl){
10363             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10364             return returnEl ? Roo.get(el) : el;
10365         },
10366
10367         /**
10368          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10369          * @param {Object} o The object with the attributes
10370          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10371          * @return {Roo.Element} this
10372          */
10373         set : function(o, useSet){
10374             var el = this.dom;
10375             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10376             for(var attr in o){
10377                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10378                 if(attr=="cls"){
10379                     el.className = o["cls"];
10380                 }else{
10381                     if(useSet) {
10382                         el.setAttribute(attr, o[attr]);
10383                     } else {
10384                         el[attr] = o[attr];
10385                     }
10386                 }
10387             }
10388             if(o.style){
10389                 Roo.DomHelper.applyStyles(el, o.style);
10390             }
10391             return this;
10392         },
10393
10394         /**
10395          * Convenience method for constructing a KeyMap
10396          * @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:
10397          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10398          * @param {Function} fn The function to call
10399          * @param {Object} scope (optional) The scope of the function
10400          * @return {Roo.KeyMap} The KeyMap created
10401          */
10402         addKeyListener : function(key, fn, scope){
10403             var config;
10404             if(typeof key != "object" || key instanceof Array){
10405                 config = {
10406                     key: key,
10407                     fn: fn,
10408                     scope: scope
10409                 };
10410             }else{
10411                 config = {
10412                     key : key.key,
10413                     shift : key.shift,
10414                     ctrl : key.ctrl,
10415                     alt : key.alt,
10416                     fn: fn,
10417                     scope: scope
10418                 };
10419             }
10420             return new Roo.KeyMap(this, config);
10421         },
10422
10423         /**
10424          * Creates a KeyMap for this element
10425          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10426          * @return {Roo.KeyMap} The KeyMap created
10427          */
10428         addKeyMap : function(config){
10429             return new Roo.KeyMap(this, config);
10430         },
10431
10432         /**
10433          * Returns true if this element is scrollable.
10434          * @return {Boolean}
10435          */
10436          isScrollable : function(){
10437             var dom = this.dom;
10438             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10439         },
10440
10441         /**
10442          * 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().
10443          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10444          * @param {Number} value The new scroll value
10445          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10446          * @return {Element} this
10447          */
10448
10449         scrollTo : function(side, value, animate){
10450             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10451             if(!animate || !A){
10452                 this.dom[prop] = value;
10453             }else{
10454                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10455                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10456             }
10457             return this;
10458         },
10459
10460         /**
10461          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10462          * within this element's scrollable range.
10463          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10464          * @param {Number} distance How far to scroll the element in pixels
10465          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10466          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10467          * was scrolled as far as it could go.
10468          */
10469          scroll : function(direction, distance, animate){
10470              if(!this.isScrollable()){
10471                  return;
10472              }
10473              var el = this.dom;
10474              var l = el.scrollLeft, t = el.scrollTop;
10475              var w = el.scrollWidth, h = el.scrollHeight;
10476              var cw = el.clientWidth, ch = el.clientHeight;
10477              direction = direction.toLowerCase();
10478              var scrolled = false;
10479              var a = this.preanim(arguments, 2);
10480              switch(direction){
10481                  case "l":
10482                  case "left":
10483                      if(w - l > cw){
10484                          var v = Math.min(l + distance, w-cw);
10485                          this.scrollTo("left", v, a);
10486                          scrolled = true;
10487                      }
10488                      break;
10489                 case "r":
10490                 case "right":
10491                      if(l > 0){
10492                          var v = Math.max(l - distance, 0);
10493                          this.scrollTo("left", v, a);
10494                          scrolled = true;
10495                      }
10496                      break;
10497                 case "t":
10498                 case "top":
10499                 case "up":
10500                      if(t > 0){
10501                          var v = Math.max(t - distance, 0);
10502                          this.scrollTo("top", v, a);
10503                          scrolled = true;
10504                      }
10505                      break;
10506                 case "b":
10507                 case "bottom":
10508                 case "down":
10509                      if(h - t > ch){
10510                          var v = Math.min(t + distance, h-ch);
10511                          this.scrollTo("top", v, a);
10512                          scrolled = true;
10513                      }
10514                      break;
10515              }
10516              return scrolled;
10517         },
10518
10519         /**
10520          * Translates the passed page coordinates into left/top css values for this element
10521          * @param {Number/Array} x The page x or an array containing [x, y]
10522          * @param {Number} y The page y
10523          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10524          */
10525         translatePoints : function(x, y){
10526             if(typeof x == 'object' || x instanceof Array){
10527                 y = x[1]; x = x[0];
10528             }
10529             var p = this.getStyle('position');
10530             var o = this.getXY();
10531
10532             var l = parseInt(this.getStyle('left'), 10);
10533             var t = parseInt(this.getStyle('top'), 10);
10534
10535             if(isNaN(l)){
10536                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10537             }
10538             if(isNaN(t)){
10539                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10540             }
10541
10542             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10543         },
10544
10545         /**
10546          * Returns the current scroll position of the element.
10547          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10548          */
10549         getScroll : function(){
10550             var d = this.dom, doc = document;
10551             if(d == doc || d == doc.body){
10552                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10553                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10554                 return {left: l, top: t};
10555             }else{
10556                 return {left: d.scrollLeft, top: d.scrollTop};
10557             }
10558         },
10559
10560         /**
10561          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10562          * are convert to standard 6 digit hex color.
10563          * @param {String} attr The css attribute
10564          * @param {String} defaultValue The default value to use when a valid color isn't found
10565          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10566          * YUI color anims.
10567          */
10568         getColor : function(attr, defaultValue, prefix){
10569             var v = this.getStyle(attr);
10570             if(!v || v == "transparent" || v == "inherit") {
10571                 return defaultValue;
10572             }
10573             var color = typeof prefix == "undefined" ? "#" : prefix;
10574             if(v.substr(0, 4) == "rgb("){
10575                 var rvs = v.slice(4, v.length -1).split(",");
10576                 for(var i = 0; i < 3; i++){
10577                     var h = parseInt(rvs[i]).toString(16);
10578                     if(h < 16){
10579                         h = "0" + h;
10580                     }
10581                     color += h;
10582                 }
10583             } else {
10584                 if(v.substr(0, 1) == "#"){
10585                     if(v.length == 4) {
10586                         for(var i = 1; i < 4; i++){
10587                             var c = v.charAt(i);
10588                             color +=  c + c;
10589                         }
10590                     }else if(v.length == 7){
10591                         color += v.substr(1);
10592                     }
10593                 }
10594             }
10595             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10596         },
10597
10598         /**
10599          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10600          * gradient background, rounded corners and a 4-way shadow.
10601          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10602          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10603          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10604          * @return {Roo.Element} this
10605          */
10606         boxWrap : function(cls){
10607             cls = cls || 'x-box';
10608             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10609             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10610             return el;
10611         },
10612
10613         /**
10614          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10615          * @param {String} namespace The namespace in which to look for the attribute
10616          * @param {String} name The attribute name
10617          * @return {String} The attribute value
10618          */
10619         getAttributeNS : Roo.isIE ? function(ns, name){
10620             var d = this.dom;
10621             var type = typeof d[ns+":"+name];
10622             if(type != 'undefined' && type != 'unknown'){
10623                 return d[ns+":"+name];
10624             }
10625             return d[name];
10626         } : function(ns, name){
10627             var d = this.dom;
10628             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10629         },
10630         
10631         
10632         /**
10633          * Sets or Returns the value the dom attribute value
10634          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10635          * @param {String} value (optional) The value to set the attribute to
10636          * @return {String} The attribute value
10637          */
10638         attr : function(name){
10639             if (arguments.length > 1) {
10640                 this.dom.setAttribute(name, arguments[1]);
10641                 return arguments[1];
10642             }
10643             if (typeof(name) == 'object') {
10644                 for(var i in name) {
10645                     this.attr(i, name[i]);
10646                 }
10647                 return name;
10648             }
10649             
10650             
10651             if (!this.dom.hasAttribute(name)) {
10652                 return undefined;
10653             }
10654             return this.dom.getAttribute(name);
10655         }
10656         
10657         
10658         
10659     };
10660
10661     var ep = El.prototype;
10662
10663     /**
10664      * Appends an event handler (Shorthand for addListener)
10665      * @param {String}   eventName     The type of event to append
10666      * @param {Function} fn        The method the event invokes
10667      * @param {Object} scope       (optional) The scope (this object) of the fn
10668      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10669      * @method
10670      */
10671     ep.on = ep.addListener;
10672         // backwards compat
10673     ep.mon = ep.addListener;
10674
10675     /**
10676      * Removes an event handler from this element (shorthand for removeListener)
10677      * @param {String} eventName the type of event to remove
10678      * @param {Function} fn the method the event invokes
10679      * @return {Roo.Element} this
10680      * @method
10681      */
10682     ep.un = ep.removeListener;
10683
10684     /**
10685      * true to automatically adjust width and height settings for box-model issues (default to true)
10686      */
10687     ep.autoBoxAdjust = true;
10688
10689     // private
10690     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10691
10692     // private
10693     El.addUnits = function(v, defaultUnit){
10694         if(v === "" || v == "auto"){
10695             return v;
10696         }
10697         if(v === undefined){
10698             return '';
10699         }
10700         if(typeof v == "number" || !El.unitPattern.test(v)){
10701             return v + (defaultUnit || 'px');
10702         }
10703         return v;
10704     };
10705
10706     // special markup used throughout Roo when box wrapping elements
10707     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>';
10708     /**
10709      * Visibility mode constant - Use visibility to hide element
10710      * @static
10711      * @type Number
10712      */
10713     El.VISIBILITY = 1;
10714     /**
10715      * Visibility mode constant - Use display to hide element
10716      * @static
10717      * @type Number
10718      */
10719     El.DISPLAY = 2;
10720
10721     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10722     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10723     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10724
10725
10726
10727     /**
10728      * @private
10729      */
10730     El.cache = {};
10731
10732     var docEl;
10733
10734     /**
10735      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10736      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10737      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10738      * @return {Element} The Element object
10739      * @static
10740      */
10741     El.get = function(el){
10742         var ex, elm, id;
10743         if(!el){ return null; }
10744         if(typeof el == "string"){ // element id
10745             if(!(elm = document.getElementById(el))){
10746                 return null;
10747             }
10748             if(ex = El.cache[el]){
10749                 ex.dom = elm;
10750             }else{
10751                 ex = El.cache[el] = new El(elm);
10752             }
10753             return ex;
10754         }else if(el.tagName){ // dom element
10755             if(!(id = el.id)){
10756                 id = Roo.id(el);
10757             }
10758             if(ex = El.cache[id]){
10759                 ex.dom = el;
10760             }else{
10761                 ex = El.cache[id] = new El(el);
10762             }
10763             return ex;
10764         }else if(el instanceof El){
10765             if(el != docEl){
10766                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10767                                                               // catch case where it hasn't been appended
10768                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10769             }
10770             return el;
10771         }else if(el.isComposite){
10772             return el;
10773         }else if(el instanceof Array){
10774             return El.select(el);
10775         }else if(el == document){
10776             // create a bogus element object representing the document object
10777             if(!docEl){
10778                 var f = function(){};
10779                 f.prototype = El.prototype;
10780                 docEl = new f();
10781                 docEl.dom = document;
10782             }
10783             return docEl;
10784         }
10785         return null;
10786     };
10787
10788     // private
10789     El.uncache = function(el){
10790         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10791             if(a[i]){
10792                 delete El.cache[a[i].id || a[i]];
10793             }
10794         }
10795     };
10796
10797     // private
10798     // Garbage collection - uncache elements/purge listeners on orphaned elements
10799     // so we don't hold a reference and cause the browser to retain them
10800     El.garbageCollect = function(){
10801         if(!Roo.enableGarbageCollector){
10802             clearInterval(El.collectorThread);
10803             return;
10804         }
10805         for(var eid in El.cache){
10806             var el = El.cache[eid], d = el.dom;
10807             // -------------------------------------------------------
10808             // Determining what is garbage:
10809             // -------------------------------------------------------
10810             // !d
10811             // dom node is null, definitely garbage
10812             // -------------------------------------------------------
10813             // !d.parentNode
10814             // no parentNode == direct orphan, definitely garbage
10815             // -------------------------------------------------------
10816             // !d.offsetParent && !document.getElementById(eid)
10817             // display none elements have no offsetParent so we will
10818             // also try to look it up by it's id. However, check
10819             // offsetParent first so we don't do unneeded lookups.
10820             // This enables collection of elements that are not orphans
10821             // directly, but somewhere up the line they have an orphan
10822             // parent.
10823             // -------------------------------------------------------
10824             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10825                 delete El.cache[eid];
10826                 if(d && Roo.enableListenerCollection){
10827                     E.purgeElement(d);
10828                 }
10829             }
10830         }
10831     }
10832     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10833
10834
10835     // dom is optional
10836     El.Flyweight = function(dom){
10837         this.dom = dom;
10838     };
10839     El.Flyweight.prototype = El.prototype;
10840
10841     El._flyweights = {};
10842     /**
10843      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10844      * the dom node can be overwritten by other code.
10845      * @param {String/HTMLElement} el The dom node or id
10846      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10847      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10848      * @static
10849      * @return {Element} The shared Element object
10850      */
10851     El.fly = function(el, named){
10852         named = named || '_global';
10853         el = Roo.getDom(el);
10854         if(!el){
10855             return null;
10856         }
10857         if(!El._flyweights[named]){
10858             El._flyweights[named] = new El.Flyweight();
10859         }
10860         El._flyweights[named].dom = el;
10861         return El._flyweights[named];
10862     };
10863
10864     /**
10865      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10866      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10867      * Shorthand of {@link Roo.Element#get}
10868      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10869      * @return {Element} The Element object
10870      * @member Roo
10871      * @method get
10872      */
10873     Roo.get = El.get;
10874     /**
10875      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10876      * the dom node can be overwritten by other code.
10877      * Shorthand of {@link Roo.Element#fly}
10878      * @param {String/HTMLElement} el The dom node or id
10879      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10880      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10881      * @static
10882      * @return {Element} The shared Element object
10883      * @member Roo
10884      * @method fly
10885      */
10886     Roo.fly = El.fly;
10887
10888     // speedy lookup for elements never to box adjust
10889     var noBoxAdjust = Roo.isStrict ? {
10890         select:1
10891     } : {
10892         input:1, select:1, textarea:1
10893     };
10894     if(Roo.isIE || Roo.isGecko){
10895         noBoxAdjust['button'] = 1;
10896     }
10897
10898
10899     Roo.EventManager.on(window, 'unload', function(){
10900         delete El.cache;
10901         delete El._flyweights;
10902     });
10903 })();
10904
10905
10906
10907
10908 if(Roo.DomQuery){
10909     Roo.Element.selectorFunction = Roo.DomQuery.select;
10910 }
10911
10912 Roo.Element.select = function(selector, unique, root){
10913     var els;
10914     if(typeof selector == "string"){
10915         els = Roo.Element.selectorFunction(selector, root);
10916     }else if(selector.length !== undefined){
10917         els = selector;
10918     }else{
10919         throw "Invalid selector";
10920     }
10921     if(unique === true){
10922         return new Roo.CompositeElement(els);
10923     }else{
10924         return new Roo.CompositeElementLite(els);
10925     }
10926 };
10927 /**
10928  * Selects elements based on the passed CSS selector to enable working on them as 1.
10929  * @param {String/Array} selector The CSS selector or an array of elements
10930  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
10931  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
10932  * @return {CompositeElementLite/CompositeElement}
10933  * @member Roo
10934  * @method select
10935  */
10936 Roo.select = Roo.Element.select;
10937
10938
10939
10940
10941
10942
10943
10944
10945
10946
10947
10948
10949
10950
10951 /*
10952  * Based on:
10953  * Ext JS Library 1.1.1
10954  * Copyright(c) 2006-2007, Ext JS, LLC.
10955  *
10956  * Originally Released Under LGPL - original licence link has changed is not relivant.
10957  *
10958  * Fork - LGPL
10959  * <script type="text/javascript">
10960  */
10961
10962
10963
10964 //Notifies Element that fx methods are available
10965 Roo.enableFx = true;
10966
10967 /**
10968  * @class Roo.Fx
10969  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
10970  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
10971  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
10972  * Element effects to work.</p><br/>
10973  *
10974  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
10975  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
10976  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
10977  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
10978  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
10979  * expected results and should be done with care.</p><br/>
10980  *
10981  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
10982  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
10983 <pre>
10984 Value  Description
10985 -----  -----------------------------
10986 tl     The top left corner
10987 t      The center of the top edge
10988 tr     The top right corner
10989 l      The center of the left edge
10990 r      The center of the right edge
10991 bl     The bottom left corner
10992 b      The center of the bottom edge
10993 br     The bottom right corner
10994 </pre>
10995  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
10996  * below are common options that can be passed to any Fx method.</b>
10997  * @cfg {Function} callback A function called when the effect is finished
10998  * @cfg {Object} scope The scope of the effect function
10999  * @cfg {String} easing A valid Easing value for the effect
11000  * @cfg {String} afterCls A css class to apply after the effect
11001  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11002  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11003  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11004  * effects that end with the element being visually hidden, ignored otherwise)
11005  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11006  * a function which returns such a specification that will be applied to the Element after the effect finishes
11007  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11008  * @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
11009  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11010  */
11011 Roo.Fx = {
11012         /**
11013          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11014          * origin for the slide effect.  This function automatically handles wrapping the element with
11015          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11016          * Usage:
11017          *<pre><code>
11018 // default: slide the element in from the top
11019 el.slideIn();
11020
11021 // custom: slide the element in from the right with a 2-second duration
11022 el.slideIn('r', { duration: 2 });
11023
11024 // common config options shown with default values
11025 el.slideIn('t', {
11026     easing: 'easeOut',
11027     duration: .5
11028 });
11029 </code></pre>
11030          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11031          * @param {Object} options (optional) Object literal with any of the Fx config options
11032          * @return {Roo.Element} The Element
11033          */
11034     slideIn : function(anchor, o){
11035         var el = this.getFxEl();
11036         o = o || {};
11037
11038         el.queueFx(o, function(){
11039
11040             anchor = anchor || "t";
11041
11042             // fix display to visibility
11043             this.fixDisplay();
11044
11045             // restore values after effect
11046             var r = this.getFxRestore();
11047             var b = this.getBox();
11048             // fixed size for slide
11049             this.setSize(b);
11050
11051             // wrap if needed
11052             var wrap = this.fxWrap(r.pos, o, "hidden");
11053
11054             var st = this.dom.style;
11055             st.visibility = "visible";
11056             st.position = "absolute";
11057
11058             // clear out temp styles after slide and unwrap
11059             var after = function(){
11060                 el.fxUnwrap(wrap, r.pos, o);
11061                 st.width = r.width;
11062                 st.height = r.height;
11063                 el.afterFx(o);
11064             };
11065             // time to calc the positions
11066             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11067
11068             switch(anchor.toLowerCase()){
11069                 case "t":
11070                     wrap.setSize(b.width, 0);
11071                     st.left = st.bottom = "0";
11072                     a = {height: bh};
11073                 break;
11074                 case "l":
11075                     wrap.setSize(0, b.height);
11076                     st.right = st.top = "0";
11077                     a = {width: bw};
11078                 break;
11079                 case "r":
11080                     wrap.setSize(0, b.height);
11081                     wrap.setX(b.right);
11082                     st.left = st.top = "0";
11083                     a = {width: bw, points: pt};
11084                 break;
11085                 case "b":
11086                     wrap.setSize(b.width, 0);
11087                     wrap.setY(b.bottom);
11088                     st.left = st.top = "0";
11089                     a = {height: bh, points: pt};
11090                 break;
11091                 case "tl":
11092                     wrap.setSize(0, 0);
11093                     st.right = st.bottom = "0";
11094                     a = {width: bw, height: bh};
11095                 break;
11096                 case "bl":
11097                     wrap.setSize(0, 0);
11098                     wrap.setY(b.y+b.height);
11099                     st.right = st.top = "0";
11100                     a = {width: bw, height: bh, points: pt};
11101                 break;
11102                 case "br":
11103                     wrap.setSize(0, 0);
11104                     wrap.setXY([b.right, b.bottom]);
11105                     st.left = st.top = "0";
11106                     a = {width: bw, height: bh, points: pt};
11107                 break;
11108                 case "tr":
11109                     wrap.setSize(0, 0);
11110                     wrap.setX(b.x+b.width);
11111                     st.left = st.bottom = "0";
11112                     a = {width: bw, height: bh, points: pt};
11113                 break;
11114             }
11115             this.dom.style.visibility = "visible";
11116             wrap.show();
11117
11118             arguments.callee.anim = wrap.fxanim(a,
11119                 o,
11120                 'motion',
11121                 .5,
11122                 'easeOut', after);
11123         });
11124         return this;
11125     },
11126     
11127         /**
11128          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11129          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11130          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11131          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11132          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11133          * Usage:
11134          *<pre><code>
11135 // default: slide the element out to the top
11136 el.slideOut();
11137
11138 // custom: slide the element out to the right with a 2-second duration
11139 el.slideOut('r', { duration: 2 });
11140
11141 // common config options shown with default values
11142 el.slideOut('t', {
11143     easing: 'easeOut',
11144     duration: .5,
11145     remove: false,
11146     useDisplay: false
11147 });
11148 </code></pre>
11149          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11150          * @param {Object} options (optional) Object literal with any of the Fx config options
11151          * @return {Roo.Element} The Element
11152          */
11153     slideOut : function(anchor, o){
11154         var el = this.getFxEl();
11155         o = o || {};
11156
11157         el.queueFx(o, function(){
11158
11159             anchor = anchor || "t";
11160
11161             // restore values after effect
11162             var r = this.getFxRestore();
11163             
11164             var b = this.getBox();
11165             // fixed size for slide
11166             this.setSize(b);
11167
11168             // wrap if needed
11169             var wrap = this.fxWrap(r.pos, o, "visible");
11170
11171             var st = this.dom.style;
11172             st.visibility = "visible";
11173             st.position = "absolute";
11174
11175             wrap.setSize(b);
11176
11177             var after = function(){
11178                 if(o.useDisplay){
11179                     el.setDisplayed(false);
11180                 }else{
11181                     el.hide();
11182                 }
11183
11184                 el.fxUnwrap(wrap, r.pos, o);
11185
11186                 st.width = r.width;
11187                 st.height = r.height;
11188
11189                 el.afterFx(o);
11190             };
11191
11192             var a, zero = {to: 0};
11193             switch(anchor.toLowerCase()){
11194                 case "t":
11195                     st.left = st.bottom = "0";
11196                     a = {height: zero};
11197                 break;
11198                 case "l":
11199                     st.right = st.top = "0";
11200                     a = {width: zero};
11201                 break;
11202                 case "r":
11203                     st.left = st.top = "0";
11204                     a = {width: zero, points: {to:[b.right, b.y]}};
11205                 break;
11206                 case "b":
11207                     st.left = st.top = "0";
11208                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11209                 break;
11210                 case "tl":
11211                     st.right = st.bottom = "0";
11212                     a = {width: zero, height: zero};
11213                 break;
11214                 case "bl":
11215                     st.right = st.top = "0";
11216                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11217                 break;
11218                 case "br":
11219                     st.left = st.top = "0";
11220                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11221                 break;
11222                 case "tr":
11223                     st.left = st.bottom = "0";
11224                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11225                 break;
11226             }
11227
11228             arguments.callee.anim = wrap.fxanim(a,
11229                 o,
11230                 'motion',
11231                 .5,
11232                 "easeOut", after);
11233         });
11234         return this;
11235     },
11236
11237         /**
11238          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11239          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11240          * The element must be removed from the DOM using the 'remove' config option if desired.
11241          * Usage:
11242          *<pre><code>
11243 // default
11244 el.puff();
11245
11246 // common config options shown with default values
11247 el.puff({
11248     easing: 'easeOut',
11249     duration: .5,
11250     remove: false,
11251     useDisplay: false
11252 });
11253 </code></pre>
11254          * @param {Object} options (optional) Object literal with any of the Fx config options
11255          * @return {Roo.Element} The Element
11256          */
11257     puff : function(o){
11258         var el = this.getFxEl();
11259         o = o || {};
11260
11261         el.queueFx(o, function(){
11262             this.clearOpacity();
11263             this.show();
11264
11265             // restore values after effect
11266             var r = this.getFxRestore();
11267             var st = this.dom.style;
11268
11269             var after = function(){
11270                 if(o.useDisplay){
11271                     el.setDisplayed(false);
11272                 }else{
11273                     el.hide();
11274                 }
11275
11276                 el.clearOpacity();
11277
11278                 el.setPositioning(r.pos);
11279                 st.width = r.width;
11280                 st.height = r.height;
11281                 st.fontSize = '';
11282                 el.afterFx(o);
11283             };
11284
11285             var width = this.getWidth();
11286             var height = this.getHeight();
11287
11288             arguments.callee.anim = this.fxanim({
11289                     width : {to: this.adjustWidth(width * 2)},
11290                     height : {to: this.adjustHeight(height * 2)},
11291                     points : {by: [-(width * .5), -(height * .5)]},
11292                     opacity : {to: 0},
11293                     fontSize: {to:200, unit: "%"}
11294                 },
11295                 o,
11296                 'motion',
11297                 .5,
11298                 "easeOut", after);
11299         });
11300         return this;
11301     },
11302
11303         /**
11304          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11305          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11306          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11307          * Usage:
11308          *<pre><code>
11309 // default
11310 el.switchOff();
11311
11312 // all config options shown with default values
11313 el.switchOff({
11314     easing: 'easeIn',
11315     duration: .3,
11316     remove: false,
11317     useDisplay: false
11318 });
11319 </code></pre>
11320          * @param {Object} options (optional) Object literal with any of the Fx config options
11321          * @return {Roo.Element} The Element
11322          */
11323     switchOff : function(o){
11324         var el = this.getFxEl();
11325         o = o || {};
11326
11327         el.queueFx(o, function(){
11328             this.clearOpacity();
11329             this.clip();
11330
11331             // restore values after effect
11332             var r = this.getFxRestore();
11333             var st = this.dom.style;
11334
11335             var after = function(){
11336                 if(o.useDisplay){
11337                     el.setDisplayed(false);
11338                 }else{
11339                     el.hide();
11340                 }
11341
11342                 el.clearOpacity();
11343                 el.setPositioning(r.pos);
11344                 st.width = r.width;
11345                 st.height = r.height;
11346
11347                 el.afterFx(o);
11348             };
11349
11350             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11351                 this.clearOpacity();
11352                 (function(){
11353                     this.fxanim({
11354                         height:{to:1},
11355                         points:{by:[0, this.getHeight() * .5]}
11356                     }, o, 'motion', 0.3, 'easeIn', after);
11357                 }).defer(100, this);
11358             });
11359         });
11360         return this;
11361     },
11362
11363     /**
11364      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11365      * changed using the "attr" config option) and then fading back to the original color. If no original
11366      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11367      * Usage:
11368 <pre><code>
11369 // default: highlight background to yellow
11370 el.highlight();
11371
11372 // custom: highlight foreground text to blue for 2 seconds
11373 el.highlight("0000ff", { attr: 'color', duration: 2 });
11374
11375 // common config options shown with default values
11376 el.highlight("ffff9c", {
11377     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11378     endColor: (current color) or "ffffff",
11379     easing: 'easeIn',
11380     duration: 1
11381 });
11382 </code></pre>
11383      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11384      * @param {Object} options (optional) Object literal with any of the Fx config options
11385      * @return {Roo.Element} The Element
11386      */ 
11387     highlight : function(color, o){
11388         var el = this.getFxEl();
11389         o = o || {};
11390
11391         el.queueFx(o, function(){
11392             color = color || "ffff9c";
11393             attr = o.attr || "backgroundColor";
11394
11395             this.clearOpacity();
11396             this.show();
11397
11398             var origColor = this.getColor(attr);
11399             var restoreColor = this.dom.style[attr];
11400             endColor = (o.endColor || origColor) || "ffffff";
11401
11402             var after = function(){
11403                 el.dom.style[attr] = restoreColor;
11404                 el.afterFx(o);
11405             };
11406
11407             var a = {};
11408             a[attr] = {from: color, to: endColor};
11409             arguments.callee.anim = this.fxanim(a,
11410                 o,
11411                 'color',
11412                 1,
11413                 'easeIn', after);
11414         });
11415         return this;
11416     },
11417
11418    /**
11419     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11420     * Usage:
11421 <pre><code>
11422 // default: a single light blue ripple
11423 el.frame();
11424
11425 // custom: 3 red ripples lasting 3 seconds total
11426 el.frame("ff0000", 3, { duration: 3 });
11427
11428 // common config options shown with default values
11429 el.frame("C3DAF9", 1, {
11430     duration: 1 //duration of entire animation (not each individual ripple)
11431     // Note: Easing is not configurable and will be ignored if included
11432 });
11433 </code></pre>
11434     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11435     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11436     * @param {Object} options (optional) Object literal with any of the Fx config options
11437     * @return {Roo.Element} The Element
11438     */
11439     frame : function(color, count, o){
11440         var el = this.getFxEl();
11441         o = o || {};
11442
11443         el.queueFx(o, function(){
11444             color = color || "#C3DAF9";
11445             if(color.length == 6){
11446                 color = "#" + color;
11447             }
11448             count = count || 1;
11449             duration = o.duration || 1;
11450             this.show();
11451
11452             var b = this.getBox();
11453             var animFn = function(){
11454                 var proxy = this.createProxy({
11455
11456                      style:{
11457                         visbility:"hidden",
11458                         position:"absolute",
11459                         "z-index":"35000", // yee haw
11460                         border:"0px solid " + color
11461                      }
11462                   });
11463                 var scale = Roo.isBorderBox ? 2 : 1;
11464                 proxy.animate({
11465                     top:{from:b.y, to:b.y - 20},
11466                     left:{from:b.x, to:b.x - 20},
11467                     borderWidth:{from:0, to:10},
11468                     opacity:{from:1, to:0},
11469                     height:{from:b.height, to:(b.height + (20*scale))},
11470                     width:{from:b.width, to:(b.width + (20*scale))}
11471                 }, duration, function(){
11472                     proxy.remove();
11473                 });
11474                 if(--count > 0){
11475                      animFn.defer((duration/2)*1000, this);
11476                 }else{
11477                     el.afterFx(o);
11478                 }
11479             };
11480             animFn.call(this);
11481         });
11482         return this;
11483     },
11484
11485    /**
11486     * Creates a pause before any subsequent queued effects begin.  If there are
11487     * no effects queued after the pause it will have no effect.
11488     * Usage:
11489 <pre><code>
11490 el.pause(1);
11491 </code></pre>
11492     * @param {Number} seconds The length of time to pause (in seconds)
11493     * @return {Roo.Element} The Element
11494     */
11495     pause : function(seconds){
11496         var el = this.getFxEl();
11497         var o = {};
11498
11499         el.queueFx(o, function(){
11500             setTimeout(function(){
11501                 el.afterFx(o);
11502             }, seconds * 1000);
11503         });
11504         return this;
11505     },
11506
11507    /**
11508     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11509     * using the "endOpacity" config option.
11510     * Usage:
11511 <pre><code>
11512 // default: fade in from opacity 0 to 100%
11513 el.fadeIn();
11514
11515 // custom: fade in from opacity 0 to 75% over 2 seconds
11516 el.fadeIn({ endOpacity: .75, duration: 2});
11517
11518 // common config options shown with default values
11519 el.fadeIn({
11520     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11521     easing: 'easeOut',
11522     duration: .5
11523 });
11524 </code></pre>
11525     * @param {Object} options (optional) Object literal with any of the Fx config options
11526     * @return {Roo.Element} The Element
11527     */
11528     fadeIn : function(o){
11529         var el = this.getFxEl();
11530         o = o || {};
11531         el.queueFx(o, function(){
11532             this.setOpacity(0);
11533             this.fixDisplay();
11534             this.dom.style.visibility = 'visible';
11535             var to = o.endOpacity || 1;
11536             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11537                 o, null, .5, "easeOut", function(){
11538                 if(to == 1){
11539                     this.clearOpacity();
11540                 }
11541                 el.afterFx(o);
11542             });
11543         });
11544         return this;
11545     },
11546
11547    /**
11548     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11549     * using the "endOpacity" config option.
11550     * Usage:
11551 <pre><code>
11552 // default: fade out from the element's current opacity to 0
11553 el.fadeOut();
11554
11555 // custom: fade out from the element's current opacity to 25% over 2 seconds
11556 el.fadeOut({ endOpacity: .25, duration: 2});
11557
11558 // common config options shown with default values
11559 el.fadeOut({
11560     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11561     easing: 'easeOut',
11562     duration: .5
11563     remove: false,
11564     useDisplay: false
11565 });
11566 </code></pre>
11567     * @param {Object} options (optional) Object literal with any of the Fx config options
11568     * @return {Roo.Element} The Element
11569     */
11570     fadeOut : function(o){
11571         var el = this.getFxEl();
11572         o = o || {};
11573         el.queueFx(o, function(){
11574             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11575                 o, null, .5, "easeOut", function(){
11576                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11577                      this.dom.style.display = "none";
11578                 }else{
11579                      this.dom.style.visibility = "hidden";
11580                 }
11581                 this.clearOpacity();
11582                 el.afterFx(o);
11583             });
11584         });
11585         return this;
11586     },
11587
11588    /**
11589     * Animates the transition of an element's dimensions from a starting height/width
11590     * to an ending height/width.
11591     * Usage:
11592 <pre><code>
11593 // change height and width to 100x100 pixels
11594 el.scale(100, 100);
11595
11596 // common config options shown with default values.  The height and width will default to
11597 // the element's existing values if passed as null.
11598 el.scale(
11599     [element's width],
11600     [element's height], {
11601     easing: 'easeOut',
11602     duration: .35
11603 });
11604 </code></pre>
11605     * @param {Number} width  The new width (pass undefined to keep the original width)
11606     * @param {Number} height  The new height (pass undefined to keep the original height)
11607     * @param {Object} options (optional) Object literal with any of the Fx config options
11608     * @return {Roo.Element} The Element
11609     */
11610     scale : function(w, h, o){
11611         this.shift(Roo.apply({}, o, {
11612             width: w,
11613             height: h
11614         }));
11615         return this;
11616     },
11617
11618    /**
11619     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11620     * Any of these properties not specified in the config object will not be changed.  This effect 
11621     * requires that at least one new dimension, position or opacity setting must be passed in on
11622     * the config object in order for the function to have any effect.
11623     * Usage:
11624 <pre><code>
11625 // slide the element horizontally to x position 200 while changing the height and opacity
11626 el.shift({ x: 200, height: 50, opacity: .8 });
11627
11628 // common config options shown with default values.
11629 el.shift({
11630     width: [element's width],
11631     height: [element's height],
11632     x: [element's x position],
11633     y: [element's y position],
11634     opacity: [element's opacity],
11635     easing: 'easeOut',
11636     duration: .35
11637 });
11638 </code></pre>
11639     * @param {Object} options  Object literal with any of the Fx config options
11640     * @return {Roo.Element} The Element
11641     */
11642     shift : function(o){
11643         var el = this.getFxEl();
11644         o = o || {};
11645         el.queueFx(o, function(){
11646             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11647             if(w !== undefined){
11648                 a.width = {to: this.adjustWidth(w)};
11649             }
11650             if(h !== undefined){
11651                 a.height = {to: this.adjustHeight(h)};
11652             }
11653             if(x !== undefined || y !== undefined){
11654                 a.points = {to: [
11655                     x !== undefined ? x : this.getX(),
11656                     y !== undefined ? y : this.getY()
11657                 ]};
11658             }
11659             if(op !== undefined){
11660                 a.opacity = {to: op};
11661             }
11662             if(o.xy !== undefined){
11663                 a.points = {to: o.xy};
11664             }
11665             arguments.callee.anim = this.fxanim(a,
11666                 o, 'motion', .35, "easeOut", function(){
11667                 el.afterFx(o);
11668             });
11669         });
11670         return this;
11671     },
11672
11673         /**
11674          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11675          * ending point of the effect.
11676          * Usage:
11677          *<pre><code>
11678 // default: slide the element downward while fading out
11679 el.ghost();
11680
11681 // custom: slide the element out to the right with a 2-second duration
11682 el.ghost('r', { duration: 2 });
11683
11684 // common config options shown with default values
11685 el.ghost('b', {
11686     easing: 'easeOut',
11687     duration: .5
11688     remove: false,
11689     useDisplay: false
11690 });
11691 </code></pre>
11692          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11693          * @param {Object} options (optional) Object literal with any of the Fx config options
11694          * @return {Roo.Element} The Element
11695          */
11696     ghost : function(anchor, o){
11697         var el = this.getFxEl();
11698         o = o || {};
11699
11700         el.queueFx(o, function(){
11701             anchor = anchor || "b";
11702
11703             // restore values after effect
11704             var r = this.getFxRestore();
11705             var w = this.getWidth(),
11706                 h = this.getHeight();
11707
11708             var st = this.dom.style;
11709
11710             var after = function(){
11711                 if(o.useDisplay){
11712                     el.setDisplayed(false);
11713                 }else{
11714                     el.hide();
11715                 }
11716
11717                 el.clearOpacity();
11718                 el.setPositioning(r.pos);
11719                 st.width = r.width;
11720                 st.height = r.height;
11721
11722                 el.afterFx(o);
11723             };
11724
11725             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11726             switch(anchor.toLowerCase()){
11727                 case "t":
11728                     pt.by = [0, -h];
11729                 break;
11730                 case "l":
11731                     pt.by = [-w, 0];
11732                 break;
11733                 case "r":
11734                     pt.by = [w, 0];
11735                 break;
11736                 case "b":
11737                     pt.by = [0, h];
11738                 break;
11739                 case "tl":
11740                     pt.by = [-w, -h];
11741                 break;
11742                 case "bl":
11743                     pt.by = [-w, h];
11744                 break;
11745                 case "br":
11746                     pt.by = [w, h];
11747                 break;
11748                 case "tr":
11749                     pt.by = [w, -h];
11750                 break;
11751             }
11752
11753             arguments.callee.anim = this.fxanim(a,
11754                 o,
11755                 'motion',
11756                 .5,
11757                 "easeOut", after);
11758         });
11759         return this;
11760     },
11761
11762         /**
11763          * Ensures that all effects queued after syncFx is called on the element are
11764          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11765          * @return {Roo.Element} The Element
11766          */
11767     syncFx : function(){
11768         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11769             block : false,
11770             concurrent : true,
11771             stopFx : false
11772         });
11773         return this;
11774     },
11775
11776         /**
11777          * Ensures that all effects queued after sequenceFx is called on the element are
11778          * run in sequence.  This is the opposite of {@link #syncFx}.
11779          * @return {Roo.Element} The Element
11780          */
11781     sequenceFx : function(){
11782         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11783             block : false,
11784             concurrent : false,
11785             stopFx : false
11786         });
11787         return this;
11788     },
11789
11790         /* @private */
11791     nextFx : function(){
11792         var ef = this.fxQueue[0];
11793         if(ef){
11794             ef.call(this);
11795         }
11796     },
11797
11798         /**
11799          * Returns true if the element has any effects actively running or queued, else returns false.
11800          * @return {Boolean} True if element has active effects, else false
11801          */
11802     hasActiveFx : function(){
11803         return this.fxQueue && this.fxQueue[0];
11804     },
11805
11806         /**
11807          * Stops any running effects and clears the element's internal effects queue if it contains
11808          * any additional effects that haven't started yet.
11809          * @return {Roo.Element} The Element
11810          */
11811     stopFx : function(){
11812         if(this.hasActiveFx()){
11813             var cur = this.fxQueue[0];
11814             if(cur && cur.anim && cur.anim.isAnimated()){
11815                 this.fxQueue = [cur]; // clear out others
11816                 cur.anim.stop(true);
11817             }
11818         }
11819         return this;
11820     },
11821
11822         /* @private */
11823     beforeFx : function(o){
11824         if(this.hasActiveFx() && !o.concurrent){
11825            if(o.stopFx){
11826                this.stopFx();
11827                return true;
11828            }
11829            return false;
11830         }
11831         return true;
11832     },
11833
11834         /**
11835          * Returns true if the element is currently blocking so that no other effect can be queued
11836          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11837          * used to ensure that an effect initiated by a user action runs to completion prior to the
11838          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11839          * @return {Boolean} True if blocking, else false
11840          */
11841     hasFxBlock : function(){
11842         var q = this.fxQueue;
11843         return q && q[0] && q[0].block;
11844     },
11845
11846         /* @private */
11847     queueFx : function(o, fn){
11848         if(!this.fxQueue){
11849             this.fxQueue = [];
11850         }
11851         if(!this.hasFxBlock()){
11852             Roo.applyIf(o, this.fxDefaults);
11853             if(!o.concurrent){
11854                 var run = this.beforeFx(o);
11855                 fn.block = o.block;
11856                 this.fxQueue.push(fn);
11857                 if(run){
11858                     this.nextFx();
11859                 }
11860             }else{
11861                 fn.call(this);
11862             }
11863         }
11864         return this;
11865     },
11866
11867         /* @private */
11868     fxWrap : function(pos, o, vis){
11869         var wrap;
11870         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11871             var wrapXY;
11872             if(o.fixPosition){
11873                 wrapXY = this.getXY();
11874             }
11875             var div = document.createElement("div");
11876             div.style.visibility = vis;
11877             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11878             wrap.setPositioning(pos);
11879             if(wrap.getStyle("position") == "static"){
11880                 wrap.position("relative");
11881             }
11882             this.clearPositioning('auto');
11883             wrap.clip();
11884             wrap.dom.appendChild(this.dom);
11885             if(wrapXY){
11886                 wrap.setXY(wrapXY);
11887             }
11888         }
11889         return wrap;
11890     },
11891
11892         /* @private */
11893     fxUnwrap : function(wrap, pos, o){
11894         this.clearPositioning();
11895         this.setPositioning(pos);
11896         if(!o.wrap){
11897             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11898             wrap.remove();
11899         }
11900     },
11901
11902         /* @private */
11903     getFxRestore : function(){
11904         var st = this.dom.style;
11905         return {pos: this.getPositioning(), width: st.width, height : st.height};
11906     },
11907
11908         /* @private */
11909     afterFx : function(o){
11910         if(o.afterStyle){
11911             this.applyStyles(o.afterStyle);
11912         }
11913         if(o.afterCls){
11914             this.addClass(o.afterCls);
11915         }
11916         if(o.remove === true){
11917             this.remove();
11918         }
11919         Roo.callback(o.callback, o.scope, [this]);
11920         if(!o.concurrent){
11921             this.fxQueue.shift();
11922             this.nextFx();
11923         }
11924     },
11925
11926         /* @private */
11927     getFxEl : function(){ // support for composite element fx
11928         return Roo.get(this.dom);
11929     },
11930
11931         /* @private */
11932     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
11933         animType = animType || 'run';
11934         opt = opt || {};
11935         var anim = Roo.lib.Anim[animType](
11936             this.dom, args,
11937             (opt.duration || defaultDur) || .35,
11938             (opt.easing || defaultEase) || 'easeOut',
11939             function(){
11940                 Roo.callback(cb, this);
11941             },
11942             this
11943         );
11944         opt.anim = anim;
11945         return anim;
11946     }
11947 };
11948
11949 // backwords compat
11950 Roo.Fx.resize = Roo.Fx.scale;
11951
11952 //When included, Roo.Fx is automatically applied to Element so that all basic
11953 //effects are available directly via the Element API
11954 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
11955  * Based on:
11956  * Ext JS Library 1.1.1
11957  * Copyright(c) 2006-2007, Ext JS, LLC.
11958  *
11959  * Originally Released Under LGPL - original licence link has changed is not relivant.
11960  *
11961  * Fork - LGPL
11962  * <script type="text/javascript">
11963  */
11964
11965
11966 /**
11967  * @class Roo.CompositeElement
11968  * Standard composite class. Creates a Roo.Element for every element in the collection.
11969  * <br><br>
11970  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
11971  * actions will be performed on all the elements in this collection.</b>
11972  * <br><br>
11973  * All methods return <i>this</i> and can be chained.
11974  <pre><code>
11975  var els = Roo.select("#some-el div.some-class", true);
11976  // or select directly from an existing element
11977  var el = Roo.get('some-el');
11978  el.select('div.some-class', true);
11979
11980  els.setWidth(100); // all elements become 100 width
11981  els.hide(true); // all elements fade out and hide
11982  // or
11983  els.setWidth(100).hide(true);
11984  </code></pre>
11985  */
11986 Roo.CompositeElement = function(els){
11987     this.elements = [];
11988     this.addElements(els);
11989 };
11990 Roo.CompositeElement.prototype = {
11991     isComposite: true,
11992     addElements : function(els){
11993         if(!els) {
11994             return this;
11995         }
11996         if(typeof els == "string"){
11997             els = Roo.Element.selectorFunction(els);
11998         }
11999         var yels = this.elements;
12000         var index = yels.length-1;
12001         for(var i = 0, len = els.length; i < len; i++) {
12002                 yels[++index] = Roo.get(els[i]);
12003         }
12004         return this;
12005     },
12006
12007     /**
12008     * Clears this composite and adds the elements returned by the passed selector.
12009     * @param {String/Array} els A string CSS selector, an array of elements or an element
12010     * @return {CompositeElement} this
12011     */
12012     fill : function(els){
12013         this.elements = [];
12014         this.add(els);
12015         return this;
12016     },
12017
12018     /**
12019     * Filters this composite to only elements that match the passed selector.
12020     * @param {String} selector A string CSS selector
12021     * @param {Boolean} inverse return inverse filter (not matches)
12022     * @return {CompositeElement} this
12023     */
12024     filter : function(selector, inverse){
12025         var els = [];
12026         inverse = inverse || false;
12027         this.each(function(el){
12028             var match = inverse ? !el.is(selector) : el.is(selector);
12029             if(match){
12030                 els[els.length] = el.dom;
12031             }
12032         });
12033         this.fill(els);
12034         return this;
12035     },
12036
12037     invoke : function(fn, args){
12038         var els = this.elements;
12039         for(var i = 0, len = els.length; i < len; i++) {
12040                 Roo.Element.prototype[fn].apply(els[i], args);
12041         }
12042         return this;
12043     },
12044     /**
12045     * Adds elements to this composite.
12046     * @param {String/Array} els A string CSS selector, an array of elements or an element
12047     * @return {CompositeElement} this
12048     */
12049     add : function(els){
12050         if(typeof els == "string"){
12051             this.addElements(Roo.Element.selectorFunction(els));
12052         }else if(els.length !== undefined){
12053             this.addElements(els);
12054         }else{
12055             this.addElements([els]);
12056         }
12057         return this;
12058     },
12059     /**
12060     * Calls the passed function passing (el, this, index) for each element in this composite.
12061     * @param {Function} fn The function to call
12062     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12063     * @return {CompositeElement} this
12064     */
12065     each : function(fn, scope){
12066         var els = this.elements;
12067         for(var i = 0, len = els.length; i < len; i++){
12068             if(fn.call(scope || els[i], els[i], this, i) === false) {
12069                 break;
12070             }
12071         }
12072         return this;
12073     },
12074
12075     /**
12076      * Returns the Element object at the specified index
12077      * @param {Number} index
12078      * @return {Roo.Element}
12079      */
12080     item : function(index){
12081         return this.elements[index] || null;
12082     },
12083
12084     /**
12085      * Returns the first Element
12086      * @return {Roo.Element}
12087      */
12088     first : function(){
12089         return this.item(0);
12090     },
12091
12092     /**
12093      * Returns the last Element
12094      * @return {Roo.Element}
12095      */
12096     last : function(){
12097         return this.item(this.elements.length-1);
12098     },
12099
12100     /**
12101      * Returns the number of elements in this composite
12102      * @return Number
12103      */
12104     getCount : function(){
12105         return this.elements.length;
12106     },
12107
12108     /**
12109      * Returns true if this composite contains the passed element
12110      * @return Boolean
12111      */
12112     contains : function(el){
12113         return this.indexOf(el) !== -1;
12114     },
12115
12116     /**
12117      * Returns true if this composite contains the passed element
12118      * @return Boolean
12119      */
12120     indexOf : function(el){
12121         return this.elements.indexOf(Roo.get(el));
12122     },
12123
12124
12125     /**
12126     * Removes the specified element(s).
12127     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12128     * or an array of any of those.
12129     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12130     * @return {CompositeElement} this
12131     */
12132     removeElement : function(el, removeDom){
12133         if(el instanceof Array){
12134             for(var i = 0, len = el.length; i < len; i++){
12135                 this.removeElement(el[i]);
12136             }
12137             return this;
12138         }
12139         var index = typeof el == 'number' ? el : this.indexOf(el);
12140         if(index !== -1){
12141             if(removeDom){
12142                 var d = this.elements[index];
12143                 if(d.dom){
12144                     d.remove();
12145                 }else{
12146                     d.parentNode.removeChild(d);
12147                 }
12148             }
12149             this.elements.splice(index, 1);
12150         }
12151         return this;
12152     },
12153
12154     /**
12155     * Replaces the specified element with the passed element.
12156     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12157     * to replace.
12158     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12159     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12160     * @return {CompositeElement} this
12161     */
12162     replaceElement : function(el, replacement, domReplace){
12163         var index = typeof el == 'number' ? el : this.indexOf(el);
12164         if(index !== -1){
12165             if(domReplace){
12166                 this.elements[index].replaceWith(replacement);
12167             }else{
12168                 this.elements.splice(index, 1, Roo.get(replacement))
12169             }
12170         }
12171         return this;
12172     },
12173
12174     /**
12175      * Removes all elements.
12176      */
12177     clear : function(){
12178         this.elements = [];
12179     }
12180 };
12181 (function(){
12182     Roo.CompositeElement.createCall = function(proto, fnName){
12183         if(!proto[fnName]){
12184             proto[fnName] = function(){
12185                 return this.invoke(fnName, arguments);
12186             };
12187         }
12188     };
12189     for(var fnName in Roo.Element.prototype){
12190         if(typeof Roo.Element.prototype[fnName] == "function"){
12191             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12192         }
12193     };
12194 })();
12195 /*
12196  * Based on:
12197  * Ext JS Library 1.1.1
12198  * Copyright(c) 2006-2007, Ext JS, LLC.
12199  *
12200  * Originally Released Under LGPL - original licence link has changed is not relivant.
12201  *
12202  * Fork - LGPL
12203  * <script type="text/javascript">
12204  */
12205
12206 /**
12207  * @class Roo.CompositeElementLite
12208  * @extends Roo.CompositeElement
12209  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12210  <pre><code>
12211  var els = Roo.select("#some-el div.some-class");
12212  // or select directly from an existing element
12213  var el = Roo.get('some-el');
12214  el.select('div.some-class');
12215
12216  els.setWidth(100); // all elements become 100 width
12217  els.hide(true); // all elements fade out and hide
12218  // or
12219  els.setWidth(100).hide(true);
12220  </code></pre><br><br>
12221  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12222  * actions will be performed on all the elements in this collection.</b>
12223  */
12224 Roo.CompositeElementLite = function(els){
12225     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12226     this.el = new Roo.Element.Flyweight();
12227 };
12228 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12229     addElements : function(els){
12230         if(els){
12231             if(els instanceof Array){
12232                 this.elements = this.elements.concat(els);
12233             }else{
12234                 var yels = this.elements;
12235                 var index = yels.length-1;
12236                 for(var i = 0, len = els.length; i < len; i++) {
12237                     yels[++index] = els[i];
12238                 }
12239             }
12240         }
12241         return this;
12242     },
12243     invoke : function(fn, args){
12244         var els = this.elements;
12245         var el = this.el;
12246         for(var i = 0, len = els.length; i < len; i++) {
12247             el.dom = els[i];
12248                 Roo.Element.prototype[fn].apply(el, args);
12249         }
12250         return this;
12251     },
12252     /**
12253      * Returns a flyweight Element of the dom element object at the specified index
12254      * @param {Number} index
12255      * @return {Roo.Element}
12256      */
12257     item : function(index){
12258         if(!this.elements[index]){
12259             return null;
12260         }
12261         this.el.dom = this.elements[index];
12262         return this.el;
12263     },
12264
12265     // fixes scope with flyweight
12266     addListener : function(eventName, handler, scope, opt){
12267         var els = this.elements;
12268         for(var i = 0, len = els.length; i < len; i++) {
12269             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12270         }
12271         return this;
12272     },
12273
12274     /**
12275     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12276     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12277     * a reference to the dom node, use el.dom.</b>
12278     * @param {Function} fn The function to call
12279     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12280     * @return {CompositeElement} this
12281     */
12282     each : function(fn, scope){
12283         var els = this.elements;
12284         var el = this.el;
12285         for(var i = 0, len = els.length; i < len; i++){
12286             el.dom = els[i];
12287                 if(fn.call(scope || el, el, this, i) === false){
12288                 break;
12289             }
12290         }
12291         return this;
12292     },
12293
12294     indexOf : function(el){
12295         return this.elements.indexOf(Roo.getDom(el));
12296     },
12297
12298     replaceElement : function(el, replacement, domReplace){
12299         var index = typeof el == 'number' ? el : this.indexOf(el);
12300         if(index !== -1){
12301             replacement = Roo.getDom(replacement);
12302             if(domReplace){
12303                 var d = this.elements[index];
12304                 d.parentNode.insertBefore(replacement, d);
12305                 d.parentNode.removeChild(d);
12306             }
12307             this.elements.splice(index, 1, replacement);
12308         }
12309         return this;
12310     }
12311 });
12312 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12313
12314 /*
12315  * Based on:
12316  * Ext JS Library 1.1.1
12317  * Copyright(c) 2006-2007, Ext JS, LLC.
12318  *
12319  * Originally Released Under LGPL - original licence link has changed is not relivant.
12320  *
12321  * Fork - LGPL
12322  * <script type="text/javascript">
12323  */
12324
12325  
12326
12327 /**
12328  * @class Roo.data.Connection
12329  * @extends Roo.util.Observable
12330  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12331  * either to a configured URL, or to a URL specified at request time. 
12332  * 
12333  * Requests made by this class are asynchronous, and will return immediately. No data from
12334  * the server will be available to the statement immediately following the {@link #request} call.
12335  * To process returned data, use a callback in the request options object, or an event listener.
12336  * 
12337  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12338  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12339  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12340  * property and, if present, the IFRAME's XML document as the responseXML property.
12341  * 
12342  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12343  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12344  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12345  * standard DOM methods.
12346  * @constructor
12347  * @param {Object} config a configuration object.
12348  */
12349 Roo.data.Connection = function(config){
12350     Roo.apply(this, config);
12351     this.addEvents({
12352         /**
12353          * @event beforerequest
12354          * Fires before a network request is made to retrieve a data object.
12355          * @param {Connection} conn This Connection object.
12356          * @param {Object} options The options config object passed to the {@link #request} method.
12357          */
12358         "beforerequest" : true,
12359         /**
12360          * @event requestcomplete
12361          * Fires if the request was successfully completed.
12362          * @param {Connection} conn This Connection object.
12363          * @param {Object} response The XHR object containing the response data.
12364          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12365          * @param {Object} options The options config object passed to the {@link #request} method.
12366          */
12367         "requestcomplete" : true,
12368         /**
12369          * @event requestexception
12370          * Fires if an error HTTP status was returned from the server.
12371          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12372          * @param {Connection} conn This Connection object.
12373          * @param {Object} response The XHR object containing the response data.
12374          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12375          * @param {Object} options The options config object passed to the {@link #request} method.
12376          */
12377         "requestexception" : true
12378     });
12379     Roo.data.Connection.superclass.constructor.call(this);
12380 };
12381
12382 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12383     /**
12384      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12385      */
12386     /**
12387      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12388      * extra parameters to each request made by this object. (defaults to undefined)
12389      */
12390     /**
12391      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12392      *  to each request made by this object. (defaults to undefined)
12393      */
12394     /**
12395      * @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)
12396      */
12397     /**
12398      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12399      */
12400     timeout : 30000,
12401     /**
12402      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12403      * @type Boolean
12404      */
12405     autoAbort:false,
12406
12407     /**
12408      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12409      * @type Boolean
12410      */
12411     disableCaching: true,
12412
12413     /**
12414      * Sends an HTTP request to a remote server.
12415      * @param {Object} options An object which may contain the following properties:<ul>
12416      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12417      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12418      * request, a url encoded string or a function to call to get either.</li>
12419      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12420      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12421      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12422      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12423      * <li>options {Object} The parameter to the request call.</li>
12424      * <li>success {Boolean} True if the request succeeded.</li>
12425      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12426      * </ul></li>
12427      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12428      * The callback is passed the following parameters:<ul>
12429      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12430      * <li>options {Object} The parameter to the request call.</li>
12431      * </ul></li>
12432      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12433      * The callback is passed the following parameters:<ul>
12434      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12435      * <li>options {Object} The parameter to the request call.</li>
12436      * </ul></li>
12437      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12438      * for the callback function. Defaults to the browser window.</li>
12439      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12440      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12441      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12442      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12443      * params for the post data. Any params will be appended to the URL.</li>
12444      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12445      * </ul>
12446      * @return {Number} transactionId
12447      */
12448     request : function(o){
12449         if(this.fireEvent("beforerequest", this, o) !== false){
12450             var p = o.params;
12451
12452             if(typeof p == "function"){
12453                 p = p.call(o.scope||window, o);
12454             }
12455             if(typeof p == "object"){
12456                 p = Roo.urlEncode(o.params);
12457             }
12458             if(this.extraParams){
12459                 var extras = Roo.urlEncode(this.extraParams);
12460                 p = p ? (p + '&' + extras) : extras;
12461             }
12462
12463             var url = o.url || this.url;
12464             if(typeof url == 'function'){
12465                 url = url.call(o.scope||window, o);
12466             }
12467
12468             if(o.form){
12469                 var form = Roo.getDom(o.form);
12470                 url = url || form.action;
12471
12472                 var enctype = form.getAttribute("enctype");
12473                 
12474                 if (o.formData) {
12475                     return this.doFormDataUpload(o, url);
12476                 }
12477                 
12478                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12479                     return this.doFormUpload(o, p, url);
12480                 }
12481                 var f = Roo.lib.Ajax.serializeForm(form);
12482                 p = p ? (p + '&' + f) : f;
12483             }
12484             
12485             if (!o.form && o.formData) {
12486                 o.formData = o.formData === true ? new FormData() : o.formData;
12487                 for (var k in o.params) {
12488                     o.formData.append(k,o.params[k]);
12489                 }
12490                     
12491                 return this.doFormDataUpload(o, url);
12492             }
12493             
12494
12495             var hs = o.headers;
12496             if(this.defaultHeaders){
12497                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12498                 if(!o.headers){
12499                     o.headers = hs;
12500                 }
12501             }
12502
12503             var cb = {
12504                 success: this.handleResponse,
12505                 failure: this.handleFailure,
12506                 scope: this,
12507                 argument: {options: o},
12508                 timeout : o.timeout || this.timeout
12509             };
12510
12511             var method = o.method||this.method||(p ? "POST" : "GET");
12512
12513             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12514                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12515             }
12516
12517             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12518                 if(o.autoAbort){
12519                     this.abort();
12520                 }
12521             }else if(this.autoAbort !== false){
12522                 this.abort();
12523             }
12524
12525             if((method == 'GET' && p) || o.xmlData){
12526                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12527                 p = '';
12528             }
12529             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12530             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12531             Roo.lib.Ajax.useDefaultHeader == true;
12532             return this.transId;
12533         }else{
12534             Roo.callback(o.callback, o.scope, [o, null, null]);
12535             return null;
12536         }
12537     },
12538
12539     /**
12540      * Determine whether this object has a request outstanding.
12541      * @param {Number} transactionId (Optional) defaults to the last transaction
12542      * @return {Boolean} True if there is an outstanding request.
12543      */
12544     isLoading : function(transId){
12545         if(transId){
12546             return Roo.lib.Ajax.isCallInProgress(transId);
12547         }else{
12548             return this.transId ? true : false;
12549         }
12550     },
12551
12552     /**
12553      * Aborts any outstanding request.
12554      * @param {Number} transactionId (Optional) defaults to the last transaction
12555      */
12556     abort : function(transId){
12557         if(transId || this.isLoading()){
12558             Roo.lib.Ajax.abort(transId || this.transId);
12559         }
12560     },
12561
12562     // private
12563     handleResponse : function(response){
12564         this.transId = false;
12565         var options = response.argument.options;
12566         response.argument = options ? options.argument : null;
12567         this.fireEvent("requestcomplete", this, response, options);
12568         Roo.callback(options.success, options.scope, [response, options]);
12569         Roo.callback(options.callback, options.scope, [options, true, response]);
12570     },
12571
12572     // private
12573     handleFailure : function(response, e){
12574         this.transId = false;
12575         var options = response.argument.options;
12576         response.argument = options ? options.argument : null;
12577         this.fireEvent("requestexception", this, response, options, e);
12578         Roo.callback(options.failure, options.scope, [response, options]);
12579         Roo.callback(options.callback, options.scope, [options, false, response]);
12580     },
12581
12582     // private
12583     doFormUpload : function(o, ps, url){
12584         var id = Roo.id();
12585         var frame = document.createElement('iframe');
12586         frame.id = id;
12587         frame.name = id;
12588         frame.className = 'x-hidden';
12589         if(Roo.isIE){
12590             frame.src = Roo.SSL_SECURE_URL;
12591         }
12592         document.body.appendChild(frame);
12593
12594         if(Roo.isIE){
12595            document.frames[id].name = id;
12596         }
12597
12598         var form = Roo.getDom(o.form);
12599         form.target = id;
12600         form.method = 'POST';
12601         form.enctype = form.encoding = 'multipart/form-data';
12602         if(url){
12603             form.action = url;
12604         }
12605
12606         var hiddens, hd;
12607         if(ps){ // add dynamic params
12608             hiddens = [];
12609             ps = Roo.urlDecode(ps, false);
12610             for(var k in ps){
12611                 if(ps.hasOwnProperty(k)){
12612                     hd = document.createElement('input');
12613                     hd.type = 'hidden';
12614                     hd.name = k;
12615                     hd.value = ps[k];
12616                     form.appendChild(hd);
12617                     hiddens.push(hd);
12618                 }
12619             }
12620         }
12621
12622         function cb(){
12623             var r = {  // bogus response object
12624                 responseText : '',
12625                 responseXML : null
12626             };
12627
12628             r.argument = o ? o.argument : null;
12629
12630             try { //
12631                 var doc;
12632                 if(Roo.isIE){
12633                     doc = frame.contentWindow.document;
12634                 }else {
12635                     doc = (frame.contentDocument || window.frames[id].document);
12636                 }
12637                 if(doc && doc.body){
12638                     r.responseText = doc.body.innerHTML;
12639                 }
12640                 if(doc && doc.XMLDocument){
12641                     r.responseXML = doc.XMLDocument;
12642                 }else {
12643                     r.responseXML = doc;
12644                 }
12645             }
12646             catch(e) {
12647                 // ignore
12648             }
12649
12650             Roo.EventManager.removeListener(frame, 'load', cb, this);
12651
12652             this.fireEvent("requestcomplete", this, r, o);
12653             Roo.callback(o.success, o.scope, [r, o]);
12654             Roo.callback(o.callback, o.scope, [o, true, r]);
12655
12656             setTimeout(function(){document.body.removeChild(frame);}, 100);
12657         }
12658
12659         Roo.EventManager.on(frame, 'load', cb, this);
12660         form.submit();
12661
12662         if(hiddens){ // remove dynamic params
12663             for(var i = 0, len = hiddens.length; i < len; i++){
12664                 form.removeChild(hiddens[i]);
12665             }
12666         }
12667     },
12668     // this is a 'formdata version???'
12669     
12670     
12671     doFormDataUpload : function(o,  url)
12672     {
12673         var formData;
12674         if (o.form) {
12675             var form =  Roo.getDom(o.form);
12676             form.enctype = form.encoding = 'multipart/form-data';
12677             formData = o.formData === true ? new FormData(form) : o.formData;
12678         } else {
12679             formData = o.formData === true ? new FormData() : o.formData;
12680         }
12681         
12682       
12683         var cb = {
12684             success: this.handleResponse,
12685             failure: this.handleFailure,
12686             scope: this,
12687             argument: {options: o},
12688             timeout : o.timeout || this.timeout
12689         };
12690  
12691         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12692             if(o.autoAbort){
12693                 this.abort();
12694             }
12695         }else if(this.autoAbort !== false){
12696             this.abort();
12697         }
12698
12699         //Roo.lib.Ajax.defaultPostHeader = null;
12700         Roo.lib.Ajax.useDefaultHeader = false;
12701         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12702         Roo.lib.Ajax.useDefaultHeader = true;
12703  
12704          
12705     }
12706     
12707 });
12708 /*
12709  * Based on:
12710  * Ext JS Library 1.1.1
12711  * Copyright(c) 2006-2007, Ext JS, LLC.
12712  *
12713  * Originally Released Under LGPL - original licence link has changed is not relivant.
12714  *
12715  * Fork - LGPL
12716  * <script type="text/javascript">
12717  */
12718  
12719 /**
12720  * Global Ajax request class.
12721  * 
12722  * @class Roo.Ajax
12723  * @extends Roo.data.Connection
12724  * @static
12725  * 
12726  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12727  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12728  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12729  * @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)
12730  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12731  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12732  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12733  */
12734 Roo.Ajax = new Roo.data.Connection({
12735     // fix up the docs
12736     /**
12737      * @scope Roo.Ajax
12738      * @type {Boolear} 
12739      */
12740     autoAbort : false,
12741
12742     /**
12743      * Serialize the passed form into a url encoded string
12744      * @scope Roo.Ajax
12745      * @param {String/HTMLElement} form
12746      * @return {String}
12747      */
12748     serializeForm : function(form){
12749         return Roo.lib.Ajax.serializeForm(form);
12750     }
12751 });/*
12752  * Based on:
12753  * Ext JS Library 1.1.1
12754  * Copyright(c) 2006-2007, Ext JS, LLC.
12755  *
12756  * Originally Released Under LGPL - original licence link has changed is not relivant.
12757  *
12758  * Fork - LGPL
12759  * <script type="text/javascript">
12760  */
12761
12762  
12763 /**
12764  * @class Roo.UpdateManager
12765  * @extends Roo.util.Observable
12766  * Provides AJAX-style update for Element object.<br><br>
12767  * Usage:<br>
12768  * <pre><code>
12769  * // Get it from a Roo.Element object
12770  * var el = Roo.get("foo");
12771  * var mgr = el.getUpdateManager();
12772  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12773  * ...
12774  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12775  * <br>
12776  * // or directly (returns the same UpdateManager instance)
12777  * var mgr = new Roo.UpdateManager("myElementId");
12778  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12779  * mgr.on("update", myFcnNeedsToKnow);
12780  * <br>
12781    // short handed call directly from the element object
12782    Roo.get("foo").load({
12783         url: "bar.php",
12784         scripts:true,
12785         params: "for=bar",
12786         text: "Loading Foo..."
12787    });
12788  * </code></pre>
12789  * @constructor
12790  * Create new UpdateManager directly.
12791  * @param {String/HTMLElement/Roo.Element} el The element to update
12792  * @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).
12793  */
12794 Roo.UpdateManager = function(el, forceNew){
12795     el = Roo.get(el);
12796     if(!forceNew && el.updateManager){
12797         return el.updateManager;
12798     }
12799     /**
12800      * The Element object
12801      * @type Roo.Element
12802      */
12803     this.el = el;
12804     /**
12805      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12806      * @type String
12807      */
12808     this.defaultUrl = null;
12809
12810     this.addEvents({
12811         /**
12812          * @event beforeupdate
12813          * Fired before an update is made, return false from your handler and the update is cancelled.
12814          * @param {Roo.Element} el
12815          * @param {String/Object/Function} url
12816          * @param {String/Object} params
12817          */
12818         "beforeupdate": true,
12819         /**
12820          * @event update
12821          * Fired after successful update is made.
12822          * @param {Roo.Element} el
12823          * @param {Object} oResponseObject The response Object
12824          */
12825         "update": true,
12826         /**
12827          * @event failure
12828          * Fired on update failure.
12829          * @param {Roo.Element} el
12830          * @param {Object} oResponseObject The response Object
12831          */
12832         "failure": true
12833     });
12834     var d = Roo.UpdateManager.defaults;
12835     /**
12836      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12837      * @type String
12838      */
12839     this.sslBlankUrl = d.sslBlankUrl;
12840     /**
12841      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12842      * @type Boolean
12843      */
12844     this.disableCaching = d.disableCaching;
12845     /**
12846      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12847      * @type String
12848      */
12849     this.indicatorText = d.indicatorText;
12850     /**
12851      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12852      * @type String
12853      */
12854     this.showLoadIndicator = d.showLoadIndicator;
12855     /**
12856      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12857      * @type Number
12858      */
12859     this.timeout = d.timeout;
12860
12861     /**
12862      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12863      * @type Boolean
12864      */
12865     this.loadScripts = d.loadScripts;
12866
12867     /**
12868      * Transaction object of current executing transaction
12869      */
12870     this.transaction = null;
12871
12872     /**
12873      * @private
12874      */
12875     this.autoRefreshProcId = null;
12876     /**
12877      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12878      * @type Function
12879      */
12880     this.refreshDelegate = this.refresh.createDelegate(this);
12881     /**
12882      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12883      * @type Function
12884      */
12885     this.updateDelegate = this.update.createDelegate(this);
12886     /**
12887      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12888      * @type Function
12889      */
12890     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12891     /**
12892      * @private
12893      */
12894     this.successDelegate = this.processSuccess.createDelegate(this);
12895     /**
12896      * @private
12897      */
12898     this.failureDelegate = this.processFailure.createDelegate(this);
12899
12900     if(!this.renderer){
12901      /**
12902       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12903       */
12904     this.renderer = new Roo.UpdateManager.BasicRenderer();
12905     }
12906     
12907     Roo.UpdateManager.superclass.constructor.call(this);
12908 };
12909
12910 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12911     /**
12912      * Get the Element this UpdateManager is bound to
12913      * @return {Roo.Element} The element
12914      */
12915     getEl : function(){
12916         return this.el;
12917     },
12918     /**
12919      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12920      * @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:
12921 <pre><code>
12922 um.update({<br/>
12923     url: "your-url.php",<br/>
12924     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
12925     callback: yourFunction,<br/>
12926     scope: yourObject, //(optional scope)  <br/>
12927     discardUrl: false, <br/>
12928     nocache: false,<br/>
12929     text: "Loading...",<br/>
12930     timeout: 30,<br/>
12931     scripts: false<br/>
12932 });
12933 </code></pre>
12934      * The only required property is url. The optional properties nocache, text and scripts
12935      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
12936      * @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}
12937      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12938      * @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.
12939      */
12940     update : function(url, params, callback, discardUrl){
12941         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
12942             var method = this.method,
12943                 cfg;
12944             if(typeof url == "object"){ // must be config object
12945                 cfg = url;
12946                 url = cfg.url;
12947                 params = params || cfg.params;
12948                 callback = callback || cfg.callback;
12949                 discardUrl = discardUrl || cfg.discardUrl;
12950                 if(callback && cfg.scope){
12951                     callback = callback.createDelegate(cfg.scope);
12952                 }
12953                 if(typeof cfg.method != "undefined"){method = cfg.method;};
12954                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
12955                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
12956                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
12957                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
12958             }
12959             this.showLoading();
12960             if(!discardUrl){
12961                 this.defaultUrl = url;
12962             }
12963             if(typeof url == "function"){
12964                 url = url.call(this);
12965             }
12966
12967             method = method || (params ? "POST" : "GET");
12968             if(method == "GET"){
12969                 url = this.prepareUrl(url);
12970             }
12971
12972             var o = Roo.apply(cfg ||{}, {
12973                 url : url,
12974                 params: params,
12975                 success: this.successDelegate,
12976                 failure: this.failureDelegate,
12977                 callback: undefined,
12978                 timeout: (this.timeout*1000),
12979                 argument: {"url": url, "form": null, "callback": callback, "params": params}
12980             });
12981             Roo.log("updated manager called with timeout of " + o.timeout);
12982             this.transaction = Roo.Ajax.request(o);
12983         }
12984     },
12985
12986     /**
12987      * 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.
12988      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
12989      * @param {String/HTMLElement} form The form Id or form element
12990      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
12991      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
12992      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
12993      */
12994     formUpdate : function(form, url, reset, callback){
12995         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
12996             if(typeof url == "function"){
12997                 url = url.call(this);
12998             }
12999             form = Roo.getDom(form);
13000             this.transaction = Roo.Ajax.request({
13001                 form: form,
13002                 url:url,
13003                 success: this.successDelegate,
13004                 failure: this.failureDelegate,
13005                 timeout: (this.timeout*1000),
13006                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13007             });
13008             this.showLoading.defer(1, this);
13009         }
13010     },
13011
13012     /**
13013      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13014      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13015      */
13016     refresh : function(callback){
13017         if(this.defaultUrl == null){
13018             return;
13019         }
13020         this.update(this.defaultUrl, null, callback, true);
13021     },
13022
13023     /**
13024      * Set this element to auto refresh.
13025      * @param {Number} interval How often to update (in seconds).
13026      * @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)
13027      * @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}
13028      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13029      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13030      */
13031     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13032         if(refreshNow){
13033             this.update(url || this.defaultUrl, params, callback, true);
13034         }
13035         if(this.autoRefreshProcId){
13036             clearInterval(this.autoRefreshProcId);
13037         }
13038         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13039     },
13040
13041     /**
13042      * Stop auto refresh on this element.
13043      */
13044      stopAutoRefresh : function(){
13045         if(this.autoRefreshProcId){
13046             clearInterval(this.autoRefreshProcId);
13047             delete this.autoRefreshProcId;
13048         }
13049     },
13050
13051     isAutoRefreshing : function(){
13052        return this.autoRefreshProcId ? true : false;
13053     },
13054     /**
13055      * Called to update the element to "Loading" state. Override to perform custom action.
13056      */
13057     showLoading : function(){
13058         if(this.showLoadIndicator){
13059             this.el.update(this.indicatorText);
13060         }
13061     },
13062
13063     /**
13064      * Adds unique parameter to query string if disableCaching = true
13065      * @private
13066      */
13067     prepareUrl : function(url){
13068         if(this.disableCaching){
13069             var append = "_dc=" + (new Date().getTime());
13070             if(url.indexOf("?") !== -1){
13071                 url += "&" + append;
13072             }else{
13073                 url += "?" + append;
13074             }
13075         }
13076         return url;
13077     },
13078
13079     /**
13080      * @private
13081      */
13082     processSuccess : function(response){
13083         this.transaction = null;
13084         if(response.argument.form && response.argument.reset){
13085             try{ // put in try/catch since some older FF releases had problems with this
13086                 response.argument.form.reset();
13087             }catch(e){}
13088         }
13089         if(this.loadScripts){
13090             this.renderer.render(this.el, response, this,
13091                 this.updateComplete.createDelegate(this, [response]));
13092         }else{
13093             this.renderer.render(this.el, response, this);
13094             this.updateComplete(response);
13095         }
13096     },
13097
13098     updateComplete : function(response){
13099         this.fireEvent("update", this.el, response);
13100         if(typeof response.argument.callback == "function"){
13101             response.argument.callback(this.el, true, response);
13102         }
13103     },
13104
13105     /**
13106      * @private
13107      */
13108     processFailure : function(response){
13109         this.transaction = null;
13110         this.fireEvent("failure", this.el, response);
13111         if(typeof response.argument.callback == "function"){
13112             response.argument.callback(this.el, false, response);
13113         }
13114     },
13115
13116     /**
13117      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13118      * @param {Object} renderer The object implementing the render() method
13119      */
13120     setRenderer : function(renderer){
13121         this.renderer = renderer;
13122     },
13123
13124     getRenderer : function(){
13125        return this.renderer;
13126     },
13127
13128     /**
13129      * Set the defaultUrl used for updates
13130      * @param {String/Function} defaultUrl The url or a function to call to get the url
13131      */
13132     setDefaultUrl : function(defaultUrl){
13133         this.defaultUrl = defaultUrl;
13134     },
13135
13136     /**
13137      * Aborts the executing transaction
13138      */
13139     abort : function(){
13140         if(this.transaction){
13141             Roo.Ajax.abort(this.transaction);
13142         }
13143     },
13144
13145     /**
13146      * Returns true if an update is in progress
13147      * @return {Boolean}
13148      */
13149     isUpdating : function(){
13150         if(this.transaction){
13151             return Roo.Ajax.isLoading(this.transaction);
13152         }
13153         return false;
13154     }
13155 });
13156
13157 /**
13158  * @class Roo.UpdateManager.defaults
13159  * @static (not really - but it helps the doc tool)
13160  * The defaults collection enables customizing the default properties of UpdateManager
13161  */
13162    Roo.UpdateManager.defaults = {
13163        /**
13164          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13165          * @type Number
13166          */
13167          timeout : 30,
13168
13169          /**
13170          * True to process scripts by default (Defaults to false).
13171          * @type Boolean
13172          */
13173         loadScripts : false,
13174
13175         /**
13176         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13177         * @type String
13178         */
13179         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13180         /**
13181          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13182          * @type Boolean
13183          */
13184         disableCaching : false,
13185         /**
13186          * Whether to show indicatorText when loading (Defaults to true).
13187          * @type Boolean
13188          */
13189         showLoadIndicator : true,
13190         /**
13191          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13192          * @type String
13193          */
13194         indicatorText : '<div class="loading-indicator">Loading...</div>'
13195    };
13196
13197 /**
13198  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13199  *Usage:
13200  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13201  * @param {String/HTMLElement/Roo.Element} el The element to update
13202  * @param {String} url The url
13203  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13204  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13205  * @static
13206  * @deprecated
13207  * @member Roo.UpdateManager
13208  */
13209 Roo.UpdateManager.updateElement = function(el, url, params, options){
13210     var um = Roo.get(el, true).getUpdateManager();
13211     Roo.apply(um, options);
13212     um.update(url, params, options ? options.callback : null);
13213 };
13214 // alias for backwards compat
13215 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13216 /**
13217  * @class Roo.UpdateManager.BasicRenderer
13218  * Default Content renderer. Updates the elements innerHTML with the responseText.
13219  */
13220 Roo.UpdateManager.BasicRenderer = function(){};
13221
13222 Roo.UpdateManager.BasicRenderer.prototype = {
13223     /**
13224      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13225      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13226      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13227      * @param {Roo.Element} el The element being rendered
13228      * @param {Object} response The YUI Connect response object
13229      * @param {UpdateManager} updateManager The calling update manager
13230      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13231      */
13232      render : function(el, response, updateManager, callback){
13233         el.update(response.responseText, updateManager.loadScripts, callback);
13234     }
13235 };
13236 /*
13237  * Based on:
13238  * Roo JS
13239  * (c)) Alan Knowles
13240  * Licence : LGPL
13241  */
13242
13243
13244 /**
13245  * @class Roo.DomTemplate
13246  * @extends Roo.Template
13247  * An effort at a dom based template engine..
13248  *
13249  * Similar to XTemplate, except it uses dom parsing to create the template..
13250  *
13251  * Supported features:
13252  *
13253  *  Tags:
13254
13255 <pre><code>
13256       {a_variable} - output encoded.
13257       {a_variable.format:("Y-m-d")} - call a method on the variable
13258       {a_variable:raw} - unencoded output
13259       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13260       {a_variable:this.method_on_template(...)} - call a method on the template object.
13261  
13262 </code></pre>
13263  *  The tpl tag:
13264 <pre><code>
13265         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13266         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13267         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13268         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13269   
13270 </code></pre>
13271  *      
13272  */
13273 Roo.DomTemplate = function()
13274 {
13275      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13276      if (this.html) {
13277         this.compile();
13278      }
13279 };
13280
13281
13282 Roo.extend(Roo.DomTemplate, Roo.Template, {
13283     /**
13284      * id counter for sub templates.
13285      */
13286     id : 0,
13287     /**
13288      * flag to indicate if dom parser is inside a pre,
13289      * it will strip whitespace if not.
13290      */
13291     inPre : false,
13292     
13293     /**
13294      * The various sub templates
13295      */
13296     tpls : false,
13297     
13298     
13299     
13300     /**
13301      *
13302      * basic tag replacing syntax
13303      * WORD:WORD()
13304      *
13305      * // you can fake an object call by doing this
13306      *  x.t:(test,tesT) 
13307      * 
13308      */
13309     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13310     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13311     
13312     iterChild : function (node, method) {
13313         
13314         var oldPre = this.inPre;
13315         if (node.tagName == 'PRE') {
13316             this.inPre = true;
13317         }
13318         for( var i = 0; i < node.childNodes.length; i++) {
13319             method.call(this, node.childNodes[i]);
13320         }
13321         this.inPre = oldPre;
13322     },
13323     
13324     
13325     
13326     /**
13327      * compile the template
13328      *
13329      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13330      *
13331      */
13332     compile: function()
13333     {
13334         var s = this.html;
13335         
13336         // covert the html into DOM...
13337         var doc = false;
13338         var div =false;
13339         try {
13340             doc = document.implementation.createHTMLDocument("");
13341             doc.documentElement.innerHTML =   this.html  ;
13342             div = doc.documentElement;
13343         } catch (e) {
13344             // old IE... - nasty -- it causes all sorts of issues.. with
13345             // images getting pulled from server..
13346             div = document.createElement('div');
13347             div.innerHTML = this.html;
13348         }
13349         //doc.documentElement.innerHTML = htmlBody
13350          
13351         
13352         
13353         this.tpls = [];
13354         var _t = this;
13355         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13356         
13357         var tpls = this.tpls;
13358         
13359         // create a top level template from the snippet..
13360         
13361         //Roo.log(div.innerHTML);
13362         
13363         var tpl = {
13364             uid : 'master',
13365             id : this.id++,
13366             attr : false,
13367             value : false,
13368             body : div.innerHTML,
13369             
13370             forCall : false,
13371             execCall : false,
13372             dom : div,
13373             isTop : true
13374             
13375         };
13376         tpls.unshift(tpl);
13377         
13378         
13379         // compile them...
13380         this.tpls = [];
13381         Roo.each(tpls, function(tp){
13382             this.compileTpl(tp);
13383             this.tpls[tp.id] = tp;
13384         }, this);
13385         
13386         this.master = tpls[0];
13387         return this;
13388         
13389         
13390     },
13391     
13392     compileNode : function(node, istop) {
13393         // test for
13394         //Roo.log(node);
13395         
13396         
13397         // skip anything not a tag..
13398         if (node.nodeType != 1) {
13399             if (node.nodeType == 3 && !this.inPre) {
13400                 // reduce white space..
13401                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13402                 
13403             }
13404             return;
13405         }
13406         
13407         var tpl = {
13408             uid : false,
13409             id : false,
13410             attr : false,
13411             value : false,
13412             body : '',
13413             
13414             forCall : false,
13415             execCall : false,
13416             dom : false,
13417             isTop : istop
13418             
13419             
13420         };
13421         
13422         
13423         switch(true) {
13424             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13425             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13426             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13427             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13428             // no default..
13429         }
13430         
13431         
13432         if (!tpl.attr) {
13433             // just itterate children..
13434             this.iterChild(node,this.compileNode);
13435             return;
13436         }
13437         tpl.uid = this.id++;
13438         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13439         node.removeAttribute('roo-'+ tpl.attr);
13440         if (tpl.attr != 'name') {
13441             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13442             node.parentNode.replaceChild(placeholder,  node);
13443         } else {
13444             
13445             var placeholder =  document.createElement('span');
13446             placeholder.className = 'roo-tpl-' + tpl.value;
13447             node.parentNode.replaceChild(placeholder,  node);
13448         }
13449         
13450         // parent now sees '{domtplXXXX}
13451         this.iterChild(node,this.compileNode);
13452         
13453         // we should now have node body...
13454         var div = document.createElement('div');
13455         div.appendChild(node);
13456         tpl.dom = node;
13457         // this has the unfortunate side effect of converting tagged attributes
13458         // eg. href="{...}" into %7C...%7D
13459         // this has been fixed by searching for those combo's although it's a bit hacky..
13460         
13461         
13462         tpl.body = div.innerHTML;
13463         
13464         
13465          
13466         tpl.id = tpl.uid;
13467         switch(tpl.attr) {
13468             case 'for' :
13469                 switch (tpl.value) {
13470                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13471                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13472                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13473                 }
13474                 break;
13475             
13476             case 'exec':
13477                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13478                 break;
13479             
13480             case 'if':     
13481                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13482                 break;
13483             
13484             case 'name':
13485                 tpl.id  = tpl.value; // replace non characters???
13486                 break;
13487             
13488         }
13489         
13490         
13491         this.tpls.push(tpl);
13492         
13493         
13494         
13495     },
13496     
13497     
13498     
13499     
13500     /**
13501      * Compile a segment of the template into a 'sub-template'
13502      *
13503      * 
13504      * 
13505      *
13506      */
13507     compileTpl : function(tpl)
13508     {
13509         var fm = Roo.util.Format;
13510         var useF = this.disableFormats !== true;
13511         
13512         var sep = Roo.isGecko ? "+\n" : ",\n";
13513         
13514         var undef = function(str) {
13515             Roo.debug && Roo.log("Property not found :"  + str);
13516             return '';
13517         };
13518           
13519         //Roo.log(tpl.body);
13520         
13521         
13522         
13523         var fn = function(m, lbrace, name, format, args)
13524         {
13525             //Roo.log("ARGS");
13526             //Roo.log(arguments);
13527             args = args ? args.replace(/\\'/g,"'") : args;
13528             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13529             if (typeof(format) == 'undefined') {
13530                 format =  'htmlEncode'; 
13531             }
13532             if (format == 'raw' ) {
13533                 format = false;
13534             }
13535             
13536             if(name.substr(0, 6) == 'domtpl'){
13537                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13538             }
13539             
13540             // build an array of options to determine if value is undefined..
13541             
13542             // basically get 'xxxx.yyyy' then do
13543             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13544             //    (function () { Roo.log("Property not found"); return ''; })() :
13545             //    ......
13546             
13547             var udef_ar = [];
13548             var lookfor = '';
13549             Roo.each(name.split('.'), function(st) {
13550                 lookfor += (lookfor.length ? '.': '') + st;
13551                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13552             });
13553             
13554             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13555             
13556             
13557             if(format && useF){
13558                 
13559                 args = args ? ',' + args : "";
13560                  
13561                 if(format.substr(0, 5) != "this."){
13562                     format = "fm." + format + '(';
13563                 }else{
13564                     format = 'this.call("'+ format.substr(5) + '", ';
13565                     args = ", values";
13566                 }
13567                 
13568                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13569             }
13570              
13571             if (args && args.length) {
13572                 // called with xxyx.yuu:(test,test)
13573                 // change to ()
13574                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13575             }
13576             // raw.. - :raw modifier..
13577             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13578             
13579         };
13580         var body;
13581         // branched to use + in gecko and [].join() in others
13582         if(Roo.isGecko){
13583             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13584                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13585                     "';};};";
13586         }else{
13587             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13588             body.push(tpl.body.replace(/(\r\n|\n)/g,
13589                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13590             body.push("'].join('');};};");
13591             body = body.join('');
13592         }
13593         
13594         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13595        
13596         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13597         eval(body);
13598         
13599         return this;
13600     },
13601      
13602     /**
13603      * same as applyTemplate, except it's done to one of the subTemplates
13604      * when using named templates, you can do:
13605      *
13606      * var str = pl.applySubTemplate('your-name', values);
13607      *
13608      * 
13609      * @param {Number} id of the template
13610      * @param {Object} values to apply to template
13611      * @param {Object} parent (normaly the instance of this object)
13612      */
13613     applySubTemplate : function(id, values, parent)
13614     {
13615         
13616         
13617         var t = this.tpls[id];
13618         
13619         
13620         try { 
13621             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13622                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13623                 return '';
13624             }
13625         } catch(e) {
13626             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13627             Roo.log(values);
13628           
13629             return '';
13630         }
13631         try { 
13632             
13633             if(t.execCall && t.execCall.call(this, values, parent)){
13634                 return '';
13635             }
13636         } catch(e) {
13637             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13638             Roo.log(values);
13639             return '';
13640         }
13641         
13642         try {
13643             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13644             parent = t.target ? values : parent;
13645             if(t.forCall && vs instanceof Array){
13646                 var buf = [];
13647                 for(var i = 0, len = vs.length; i < len; i++){
13648                     try {
13649                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13650                     } catch (e) {
13651                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13652                         Roo.log(e.body);
13653                         //Roo.log(t.compiled);
13654                         Roo.log(vs[i]);
13655                     }   
13656                 }
13657                 return buf.join('');
13658             }
13659         } catch (e) {
13660             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13661             Roo.log(values);
13662             return '';
13663         }
13664         try {
13665             return t.compiled.call(this, vs, parent);
13666         } catch (e) {
13667             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13668             Roo.log(e.body);
13669             //Roo.log(t.compiled);
13670             Roo.log(values);
13671             return '';
13672         }
13673     },
13674
13675    
13676
13677     applyTemplate : function(values){
13678         return this.master.compiled.call(this, values, {});
13679         //var s = this.subs;
13680     },
13681
13682     apply : function(){
13683         return this.applyTemplate.apply(this, arguments);
13684     }
13685
13686  });
13687
13688 Roo.DomTemplate.from = function(el){
13689     el = Roo.getDom(el);
13690     return new Roo.Domtemplate(el.value || el.innerHTML);
13691 };/*
13692  * Based on:
13693  * Ext JS Library 1.1.1
13694  * Copyright(c) 2006-2007, Ext JS, LLC.
13695  *
13696  * Originally Released Under LGPL - original licence link has changed is not relivant.
13697  *
13698  * Fork - LGPL
13699  * <script type="text/javascript">
13700  */
13701
13702 /**
13703  * @class Roo.util.DelayedTask
13704  * Provides a convenient method of performing setTimeout where a new
13705  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13706  * You can use this class to buffer
13707  * the keypress events for a certain number of milliseconds, and perform only if they stop
13708  * for that amount of time.
13709  * @constructor The parameters to this constructor serve as defaults and are not required.
13710  * @param {Function} fn (optional) The default function to timeout
13711  * @param {Object} scope (optional) The default scope of that timeout
13712  * @param {Array} args (optional) The default Array of arguments
13713  */
13714 Roo.util.DelayedTask = function(fn, scope, args){
13715     var id = null, d, t;
13716
13717     var call = function(){
13718         var now = new Date().getTime();
13719         if(now - t >= d){
13720             clearInterval(id);
13721             id = null;
13722             fn.apply(scope, args || []);
13723         }
13724     };
13725     /**
13726      * Cancels any pending timeout and queues a new one
13727      * @param {Number} delay The milliseconds to delay
13728      * @param {Function} newFn (optional) Overrides function passed to constructor
13729      * @param {Object} newScope (optional) Overrides scope passed to constructor
13730      * @param {Array} newArgs (optional) Overrides args passed to constructor
13731      */
13732     this.delay = function(delay, newFn, newScope, newArgs){
13733         if(id && delay != d){
13734             this.cancel();
13735         }
13736         d = delay;
13737         t = new Date().getTime();
13738         fn = newFn || fn;
13739         scope = newScope || scope;
13740         args = newArgs || args;
13741         if(!id){
13742             id = setInterval(call, d);
13743         }
13744     };
13745
13746     /**
13747      * Cancel the last queued timeout
13748      */
13749     this.cancel = function(){
13750         if(id){
13751             clearInterval(id);
13752             id = null;
13753         }
13754     };
13755 };/*
13756  * Based on:
13757  * Ext JS Library 1.1.1
13758  * Copyright(c) 2006-2007, Ext JS, LLC.
13759  *
13760  * Originally Released Under LGPL - original licence link has changed is not relivant.
13761  *
13762  * Fork - LGPL
13763  * <script type="text/javascript">
13764  */
13765 /**
13766  * @class Roo.util.TaskRunner
13767  * Manage background tasks - not sure why this is better that setInterval?
13768  * @static
13769  *
13770  */
13771  
13772 Roo.util.TaskRunner = function(interval){
13773     interval = interval || 10;
13774     var tasks = [], removeQueue = [];
13775     var id = 0;
13776     var running = false;
13777
13778     var stopThread = function(){
13779         running = false;
13780         clearInterval(id);
13781         id = 0;
13782     };
13783
13784     var startThread = function(){
13785         if(!running){
13786             running = true;
13787             id = setInterval(runTasks, interval);
13788         }
13789     };
13790
13791     var removeTask = function(task){
13792         removeQueue.push(task);
13793         if(task.onStop){
13794             task.onStop();
13795         }
13796     };
13797
13798     var runTasks = function(){
13799         if(removeQueue.length > 0){
13800             for(var i = 0, len = removeQueue.length; i < len; i++){
13801                 tasks.remove(removeQueue[i]);
13802             }
13803             removeQueue = [];
13804             if(tasks.length < 1){
13805                 stopThread();
13806                 return;
13807             }
13808         }
13809         var now = new Date().getTime();
13810         for(var i = 0, len = tasks.length; i < len; ++i){
13811             var t = tasks[i];
13812             var itime = now - t.taskRunTime;
13813             if(t.interval <= itime){
13814                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13815                 t.taskRunTime = now;
13816                 if(rt === false || t.taskRunCount === t.repeat){
13817                     removeTask(t);
13818                     return;
13819                 }
13820             }
13821             if(t.duration && t.duration <= (now - t.taskStartTime)){
13822                 removeTask(t);
13823             }
13824         }
13825     };
13826
13827     /**
13828      * Queues a new task.
13829      * @param {Object} task
13830      *
13831      * Task property : interval = how frequent to run.
13832      * Task object should implement
13833      * function run()
13834      * Task object may implement
13835      * function onStop()
13836      */
13837     this.start = function(task){
13838         tasks.push(task);
13839         task.taskStartTime = new Date().getTime();
13840         task.taskRunTime = 0;
13841         task.taskRunCount = 0;
13842         startThread();
13843         return task;
13844     };
13845     /**
13846      * Stop  new task.
13847      * @param {Object} task
13848      */
13849     this.stop = function(task){
13850         removeTask(task);
13851         return task;
13852     };
13853     /**
13854      * Stop all Tasks
13855      */
13856     this.stopAll = function(){
13857         stopThread();
13858         for(var i = 0, len = tasks.length; i < len; i++){
13859             if(tasks[i].onStop){
13860                 tasks[i].onStop();
13861             }
13862         }
13863         tasks = [];
13864         removeQueue = [];
13865     };
13866 };
13867
13868 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13869  * Based on:
13870  * Ext JS Library 1.1.1
13871  * Copyright(c) 2006-2007, Ext JS, LLC.
13872  *
13873  * Originally Released Under LGPL - original licence link has changed is not relivant.
13874  *
13875  * Fork - LGPL
13876  * <script type="text/javascript">
13877  */
13878
13879  
13880 /**
13881  * @class Roo.util.MixedCollection
13882  * @extends Roo.util.Observable
13883  * A Collection class that maintains both numeric indexes and keys and exposes events.
13884  * @constructor
13885  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13886  * collection (defaults to false)
13887  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13888  * and return the key value for that item.  This is used when available to look up the key on items that
13889  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13890  * equivalent to providing an implementation for the {@link #getKey} method.
13891  */
13892 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13893     this.items = [];
13894     this.map = {};
13895     this.keys = [];
13896     this.length = 0;
13897     this.addEvents({
13898         /**
13899          * @event clear
13900          * Fires when the collection is cleared.
13901          */
13902         "clear" : true,
13903         /**
13904          * @event add
13905          * Fires when an item is added to the collection.
13906          * @param {Number} index The index at which the item was added.
13907          * @param {Object} o The item added.
13908          * @param {String} key The key associated with the added item.
13909          */
13910         "add" : true,
13911         /**
13912          * @event replace
13913          * Fires when an item is replaced in the collection.
13914          * @param {String} key he key associated with the new added.
13915          * @param {Object} old The item being replaced.
13916          * @param {Object} new The new item.
13917          */
13918         "replace" : true,
13919         /**
13920          * @event remove
13921          * Fires when an item is removed from the collection.
13922          * @param {Object} o The item being removed.
13923          * @param {String} key (optional) The key associated with the removed item.
13924          */
13925         "remove" : true,
13926         "sort" : true
13927     });
13928     this.allowFunctions = allowFunctions === true;
13929     if(keyFn){
13930         this.getKey = keyFn;
13931     }
13932     Roo.util.MixedCollection.superclass.constructor.call(this);
13933 };
13934
13935 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
13936     allowFunctions : false,
13937     
13938 /**
13939  * Adds an item to the collection.
13940  * @param {String} key The key to associate with the item
13941  * @param {Object} o The item to add.
13942  * @return {Object} The item added.
13943  */
13944     add : function(key, o){
13945         if(arguments.length == 1){
13946             o = arguments[0];
13947             key = this.getKey(o);
13948         }
13949         if(typeof key == "undefined" || key === null){
13950             this.length++;
13951             this.items.push(o);
13952             this.keys.push(null);
13953         }else{
13954             var old = this.map[key];
13955             if(old){
13956                 return this.replace(key, o);
13957             }
13958             this.length++;
13959             this.items.push(o);
13960             this.map[key] = o;
13961             this.keys.push(key);
13962         }
13963         this.fireEvent("add", this.length-1, o, key);
13964         return o;
13965     },
13966        
13967 /**
13968   * MixedCollection has a generic way to fetch keys if you implement getKey.
13969 <pre><code>
13970 // normal way
13971 var mc = new Roo.util.MixedCollection();
13972 mc.add(someEl.dom.id, someEl);
13973 mc.add(otherEl.dom.id, otherEl);
13974 //and so on
13975
13976 // using getKey
13977 var mc = new Roo.util.MixedCollection();
13978 mc.getKey = function(el){
13979    return el.dom.id;
13980 };
13981 mc.add(someEl);
13982 mc.add(otherEl);
13983
13984 // or via the constructor
13985 var mc = new Roo.util.MixedCollection(false, function(el){
13986    return el.dom.id;
13987 });
13988 mc.add(someEl);
13989 mc.add(otherEl);
13990 </code></pre>
13991  * @param o {Object} The item for which to find the key.
13992  * @return {Object} The key for the passed item.
13993  */
13994     getKey : function(o){
13995          return o.id; 
13996     },
13997    
13998 /**
13999  * Replaces an item in the collection.
14000  * @param {String} key The key associated with the item to replace, or the item to replace.
14001  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14002  * @return {Object}  The new item.
14003  */
14004     replace : function(key, o){
14005         if(arguments.length == 1){
14006             o = arguments[0];
14007             key = this.getKey(o);
14008         }
14009         var old = this.item(key);
14010         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14011              return this.add(key, o);
14012         }
14013         var index = this.indexOfKey(key);
14014         this.items[index] = o;
14015         this.map[key] = o;
14016         this.fireEvent("replace", key, old, o);
14017         return o;
14018     },
14019    
14020 /**
14021  * Adds all elements of an Array or an Object to the collection.
14022  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14023  * an Array of values, each of which are added to the collection.
14024  */
14025     addAll : function(objs){
14026         if(arguments.length > 1 || objs instanceof Array){
14027             var args = arguments.length > 1 ? arguments : objs;
14028             for(var i = 0, len = args.length; i < len; i++){
14029                 this.add(args[i]);
14030             }
14031         }else{
14032             for(var key in objs){
14033                 if(this.allowFunctions || typeof objs[key] != "function"){
14034                     this.add(key, objs[key]);
14035                 }
14036             }
14037         }
14038     },
14039    
14040 /**
14041  * Executes the specified function once for every item in the collection, passing each
14042  * item as the first and only parameter. returning false from the function will stop the iteration.
14043  * @param {Function} fn The function to execute for each item.
14044  * @param {Object} scope (optional) The scope in which to execute the function.
14045  */
14046     each : function(fn, scope){
14047         var items = [].concat(this.items); // each safe for removal
14048         for(var i = 0, len = items.length; i < len; i++){
14049             if(fn.call(scope || items[i], items[i], i, len) === false){
14050                 break;
14051             }
14052         }
14053     },
14054    
14055 /**
14056  * Executes the specified function once for every key in the collection, passing each
14057  * key, and its associated item as the first two parameters.
14058  * @param {Function} fn The function to execute for each item.
14059  * @param {Object} scope (optional) The scope in which to execute the function.
14060  */
14061     eachKey : function(fn, scope){
14062         for(var i = 0, len = this.keys.length; i < len; i++){
14063             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14064         }
14065     },
14066    
14067 /**
14068  * Returns the first item in the collection which elicits a true return value from the
14069  * passed selection function.
14070  * @param {Function} fn The selection function to execute for each item.
14071  * @param {Object} scope (optional) The scope in which to execute the function.
14072  * @return {Object} The first item in the collection which returned true from the selection function.
14073  */
14074     find : function(fn, scope){
14075         for(var i = 0, len = this.items.length; i < len; i++){
14076             if(fn.call(scope || window, this.items[i], this.keys[i])){
14077                 return this.items[i];
14078             }
14079         }
14080         return null;
14081     },
14082    
14083 /**
14084  * Inserts an item at the specified index in the collection.
14085  * @param {Number} index The index to insert the item at.
14086  * @param {String} key The key to associate with the new item, or the item itself.
14087  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14088  * @return {Object} The item inserted.
14089  */
14090     insert : function(index, key, o){
14091         if(arguments.length == 2){
14092             o = arguments[1];
14093             key = this.getKey(o);
14094         }
14095         if(index >= this.length){
14096             return this.add(key, o);
14097         }
14098         this.length++;
14099         this.items.splice(index, 0, o);
14100         if(typeof key != "undefined" && key != null){
14101             this.map[key] = o;
14102         }
14103         this.keys.splice(index, 0, key);
14104         this.fireEvent("add", index, o, key);
14105         return o;
14106     },
14107    
14108 /**
14109  * Removed an item from the collection.
14110  * @param {Object} o The item to remove.
14111  * @return {Object} The item removed.
14112  */
14113     remove : function(o){
14114         return this.removeAt(this.indexOf(o));
14115     },
14116    
14117 /**
14118  * Remove an item from a specified index in the collection.
14119  * @param {Number} index The index within the collection of the item to remove.
14120  */
14121     removeAt : function(index){
14122         if(index < this.length && index >= 0){
14123             this.length--;
14124             var o = this.items[index];
14125             this.items.splice(index, 1);
14126             var key = this.keys[index];
14127             if(typeof key != "undefined"){
14128                 delete this.map[key];
14129             }
14130             this.keys.splice(index, 1);
14131             this.fireEvent("remove", o, key);
14132         }
14133     },
14134    
14135 /**
14136  * Removed an item associated with the passed key fom the collection.
14137  * @param {String} key The key of the item to remove.
14138  */
14139     removeKey : function(key){
14140         return this.removeAt(this.indexOfKey(key));
14141     },
14142    
14143 /**
14144  * Returns the number of items in the collection.
14145  * @return {Number} the number of items in the collection.
14146  */
14147     getCount : function(){
14148         return this.length; 
14149     },
14150    
14151 /**
14152  * Returns index within the collection of the passed Object.
14153  * @param {Object} o The item to find the index of.
14154  * @return {Number} index of the item.
14155  */
14156     indexOf : function(o){
14157         if(!this.items.indexOf){
14158             for(var i = 0, len = this.items.length; i < len; i++){
14159                 if(this.items[i] == o) {
14160                     return i;
14161                 }
14162             }
14163             return -1;
14164         }else{
14165             return this.items.indexOf(o);
14166         }
14167     },
14168    
14169 /**
14170  * Returns index within the collection of the passed key.
14171  * @param {String} key The key to find the index of.
14172  * @return {Number} index of the key.
14173  */
14174     indexOfKey : function(key){
14175         if(!this.keys.indexOf){
14176             for(var i = 0, len = this.keys.length; i < len; i++){
14177                 if(this.keys[i] == key) {
14178                     return i;
14179                 }
14180             }
14181             return -1;
14182         }else{
14183             return this.keys.indexOf(key);
14184         }
14185     },
14186    
14187 /**
14188  * Returns the item associated with the passed key OR index. Key has priority over index.
14189  * @param {String/Number} key The key or index of the item.
14190  * @return {Object} The item associated with the passed key.
14191  */
14192     item : function(key){
14193         if (key === 'length') {
14194             return null;
14195         }
14196         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14197         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14198     },
14199     
14200 /**
14201  * Returns the item at the specified index.
14202  * @param {Number} index The index of the item.
14203  * @return {Object}
14204  */
14205     itemAt : function(index){
14206         return this.items[index];
14207     },
14208     
14209 /**
14210  * Returns the item associated with the passed key.
14211  * @param {String/Number} key The key of the item.
14212  * @return {Object} The item associated with the passed key.
14213  */
14214     key : function(key){
14215         return this.map[key];
14216     },
14217    
14218 /**
14219  * Returns true if the collection contains the passed Object as an item.
14220  * @param {Object} o  The Object to look for in the collection.
14221  * @return {Boolean} True if the collection contains the Object as an item.
14222  */
14223     contains : function(o){
14224         return this.indexOf(o) != -1;
14225     },
14226    
14227 /**
14228  * Returns true if the collection contains the passed Object as a key.
14229  * @param {String} key The key to look for in the collection.
14230  * @return {Boolean} True if the collection contains the Object as a key.
14231  */
14232     containsKey : function(key){
14233         return typeof this.map[key] != "undefined";
14234     },
14235    
14236 /**
14237  * Removes all items from the collection.
14238  */
14239     clear : function(){
14240         this.length = 0;
14241         this.items = [];
14242         this.keys = [];
14243         this.map = {};
14244         this.fireEvent("clear");
14245     },
14246    
14247 /**
14248  * Returns the first item in the collection.
14249  * @return {Object} the first item in the collection..
14250  */
14251     first : function(){
14252         return this.items[0]; 
14253     },
14254    
14255 /**
14256  * Returns the last item in the collection.
14257  * @return {Object} the last item in the collection..
14258  */
14259     last : function(){
14260         return this.items[this.length-1];   
14261     },
14262     
14263     _sort : function(property, dir, fn){
14264         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14265         fn = fn || function(a, b){
14266             return a-b;
14267         };
14268         var c = [], k = this.keys, items = this.items;
14269         for(var i = 0, len = items.length; i < len; i++){
14270             c[c.length] = {key: k[i], value: items[i], index: i};
14271         }
14272         c.sort(function(a, b){
14273             var v = fn(a[property], b[property]) * dsc;
14274             if(v == 0){
14275                 v = (a.index < b.index ? -1 : 1);
14276             }
14277             return v;
14278         });
14279         for(var i = 0, len = c.length; i < len; i++){
14280             items[i] = c[i].value;
14281             k[i] = c[i].key;
14282         }
14283         this.fireEvent("sort", this);
14284     },
14285     
14286     /**
14287      * Sorts this collection with the passed comparison function
14288      * @param {String} direction (optional) "ASC" or "DESC"
14289      * @param {Function} fn (optional) comparison function
14290      */
14291     sort : function(dir, fn){
14292         this._sort("value", dir, fn);
14293     },
14294     
14295     /**
14296      * Sorts this collection by keys
14297      * @param {String} direction (optional) "ASC" or "DESC"
14298      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14299      */
14300     keySort : function(dir, fn){
14301         this._sort("key", dir, fn || function(a, b){
14302             return String(a).toUpperCase()-String(b).toUpperCase();
14303         });
14304     },
14305     
14306     /**
14307      * Returns a range of items in this collection
14308      * @param {Number} startIndex (optional) defaults to 0
14309      * @param {Number} endIndex (optional) default to the last item
14310      * @return {Array} An array of items
14311      */
14312     getRange : function(start, end){
14313         var items = this.items;
14314         if(items.length < 1){
14315             return [];
14316         }
14317         start = start || 0;
14318         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14319         var r = [];
14320         if(start <= end){
14321             for(var i = start; i <= end; i++) {
14322                     r[r.length] = items[i];
14323             }
14324         }else{
14325             for(var i = start; i >= end; i--) {
14326                     r[r.length] = items[i];
14327             }
14328         }
14329         return r;
14330     },
14331         
14332     /**
14333      * Filter the <i>objects</i> in this collection by a specific property. 
14334      * Returns a new collection that has been filtered.
14335      * @param {String} property A property on your objects
14336      * @param {String/RegExp} value Either string that the property values 
14337      * should start with or a RegExp to test against the property
14338      * @return {MixedCollection} The new filtered collection
14339      */
14340     filter : function(property, value){
14341         if(!value.exec){ // not a regex
14342             value = String(value);
14343             if(value.length == 0){
14344                 return this.clone();
14345             }
14346             value = new RegExp("^" + Roo.escapeRe(value), "i");
14347         }
14348         return this.filterBy(function(o){
14349             return o && value.test(o[property]);
14350         });
14351         },
14352     
14353     /**
14354      * Filter by a function. * Returns a new collection that has been filtered.
14355      * The passed function will be called with each 
14356      * object in the collection. If the function returns true, the value is included 
14357      * otherwise it is filtered.
14358      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14359      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14360      * @return {MixedCollection} The new filtered collection
14361      */
14362     filterBy : function(fn, scope){
14363         var r = new Roo.util.MixedCollection();
14364         r.getKey = this.getKey;
14365         var k = this.keys, it = this.items;
14366         for(var i = 0, len = it.length; i < len; i++){
14367             if(fn.call(scope||this, it[i], k[i])){
14368                                 r.add(k[i], it[i]);
14369                         }
14370         }
14371         return r;
14372     },
14373     
14374     /**
14375      * Creates a duplicate of this collection
14376      * @return {MixedCollection}
14377      */
14378     clone : function(){
14379         var r = new Roo.util.MixedCollection();
14380         var k = this.keys, it = this.items;
14381         for(var i = 0, len = it.length; i < len; i++){
14382             r.add(k[i], it[i]);
14383         }
14384         r.getKey = this.getKey;
14385         return r;
14386     }
14387 });
14388 /**
14389  * Returns the item associated with the passed key or index.
14390  * @method
14391  * @param {String/Number} key The key or index of the item.
14392  * @return {Object} The item associated with the passed key.
14393  */
14394 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14395  * Based on:
14396  * Ext JS Library 1.1.1
14397  * Copyright(c) 2006-2007, Ext JS, LLC.
14398  *
14399  * Originally Released Under LGPL - original licence link has changed is not relivant.
14400  *
14401  * Fork - LGPL
14402  * <script type="text/javascript">
14403  */
14404 /**
14405  * @class Roo.util.JSON
14406  * Modified version of Douglas Crockford"s json.js that doesn"t
14407  * mess with the Object prototype 
14408  * http://www.json.org/js.html
14409  * @static
14410  */
14411 Roo.util.JSON = new (function(){
14412     var useHasOwn = {}.hasOwnProperty ? true : false;
14413     
14414     // crashes Safari in some instances
14415     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14416     
14417     var pad = function(n) {
14418         return n < 10 ? "0" + n : n;
14419     };
14420     
14421     var m = {
14422         "\b": '\\b',
14423         "\t": '\\t',
14424         "\n": '\\n',
14425         "\f": '\\f',
14426         "\r": '\\r',
14427         '"' : '\\"',
14428         "\\": '\\\\'
14429     };
14430
14431     var encodeString = function(s){
14432         if (/["\\\x00-\x1f]/.test(s)) {
14433             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14434                 var c = m[b];
14435                 if(c){
14436                     return c;
14437                 }
14438                 c = b.charCodeAt();
14439                 return "\\u00" +
14440                     Math.floor(c / 16).toString(16) +
14441                     (c % 16).toString(16);
14442             }) + '"';
14443         }
14444         return '"' + s + '"';
14445     };
14446     
14447     var encodeArray = function(o){
14448         var a = ["["], b, i, l = o.length, v;
14449             for (i = 0; i < l; i += 1) {
14450                 v = o[i];
14451                 switch (typeof v) {
14452                     case "undefined":
14453                     case "function":
14454                     case "unknown":
14455                         break;
14456                     default:
14457                         if (b) {
14458                             a.push(',');
14459                         }
14460                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14461                         b = true;
14462                 }
14463             }
14464             a.push("]");
14465             return a.join("");
14466     };
14467     
14468     var encodeDate = function(o){
14469         return '"' + o.getFullYear() + "-" +
14470                 pad(o.getMonth() + 1) + "-" +
14471                 pad(o.getDate()) + "T" +
14472                 pad(o.getHours()) + ":" +
14473                 pad(o.getMinutes()) + ":" +
14474                 pad(o.getSeconds()) + '"';
14475     };
14476     
14477     /**
14478      * Encodes an Object, Array or other value
14479      * @param {Mixed} o The variable to encode
14480      * @return {String} The JSON string
14481      */
14482     this.encode = function(o)
14483     {
14484         // should this be extended to fully wrap stringify..
14485         
14486         if(typeof o == "undefined" || o === null){
14487             return "null";
14488         }else if(o instanceof Array){
14489             return encodeArray(o);
14490         }else if(o instanceof Date){
14491             return encodeDate(o);
14492         }else if(typeof o == "string"){
14493             return encodeString(o);
14494         }else if(typeof o == "number"){
14495             return isFinite(o) ? String(o) : "null";
14496         }else if(typeof o == "boolean"){
14497             return String(o);
14498         }else {
14499             var a = ["{"], b, i, v;
14500             for (i in o) {
14501                 if(!useHasOwn || o.hasOwnProperty(i)) {
14502                     v = o[i];
14503                     switch (typeof v) {
14504                     case "undefined":
14505                     case "function":
14506                     case "unknown":
14507                         break;
14508                     default:
14509                         if(b){
14510                             a.push(',');
14511                         }
14512                         a.push(this.encode(i), ":",
14513                                 v === null ? "null" : this.encode(v));
14514                         b = true;
14515                     }
14516                 }
14517             }
14518             a.push("}");
14519             return a.join("");
14520         }
14521     };
14522     
14523     /**
14524      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14525      * @param {String} json The JSON string
14526      * @return {Object} The resulting object
14527      */
14528     this.decode = function(json){
14529         
14530         return  /** eval:var:json */ eval("(" + json + ')');
14531     };
14532 })();
14533 /** 
14534  * Shorthand for {@link Roo.util.JSON#encode}
14535  * @member Roo encode 
14536  * @method */
14537 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14538 /** 
14539  * Shorthand for {@link Roo.util.JSON#decode}
14540  * @member Roo decode 
14541  * @method */
14542 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14543 /*
14544  * Based on:
14545  * Ext JS Library 1.1.1
14546  * Copyright(c) 2006-2007, Ext JS, LLC.
14547  *
14548  * Originally Released Under LGPL - original licence link has changed is not relivant.
14549  *
14550  * Fork - LGPL
14551  * <script type="text/javascript">
14552  */
14553  
14554 /**
14555  * @class Roo.util.Format
14556  * Reusable data formatting functions
14557  * @static
14558  */
14559 Roo.util.Format = function(){
14560     var trimRe = /^\s+|\s+$/g;
14561     return {
14562         /**
14563          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14564          * @param {String} value The string to truncate
14565          * @param {Number} length The maximum length to allow before truncating
14566          * @return {String} The converted text
14567          */
14568         ellipsis : function(value, len){
14569             if(value && value.length > len){
14570                 return value.substr(0, len-3)+"...";
14571             }
14572             return value;
14573         },
14574
14575         /**
14576          * Checks a reference and converts it to empty string if it is undefined
14577          * @param {Mixed} value Reference to check
14578          * @return {Mixed} Empty string if converted, otherwise the original value
14579          */
14580         undef : function(value){
14581             return typeof value != "undefined" ? value : "";
14582         },
14583
14584         /**
14585          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14586          * @param {String} value The string to encode
14587          * @return {String} The encoded text
14588          */
14589         htmlEncode : function(value){
14590             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14591         },
14592
14593         /**
14594          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14595          * @param {String} value The string to decode
14596          * @return {String} The decoded text
14597          */
14598         htmlDecode : function(value){
14599             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14600         },
14601
14602         /**
14603          * Trims any whitespace from either side of a string
14604          * @param {String} value The text to trim
14605          * @return {String} The trimmed text
14606          */
14607         trim : function(value){
14608             return String(value).replace(trimRe, "");
14609         },
14610
14611         /**
14612          * Returns a substring from within an original string
14613          * @param {String} value The original text
14614          * @param {Number} start The start index of the substring
14615          * @param {Number} length The length of the substring
14616          * @return {String} The substring
14617          */
14618         substr : function(value, start, length){
14619             return String(value).substr(start, length);
14620         },
14621
14622         /**
14623          * Converts a string to all lower case letters
14624          * @param {String} value The text to convert
14625          * @return {String} The converted text
14626          */
14627         lowercase : function(value){
14628             return String(value).toLowerCase();
14629         },
14630
14631         /**
14632          * Converts a string to all upper case letters
14633          * @param {String} value The text to convert
14634          * @return {String} The converted text
14635          */
14636         uppercase : function(value){
14637             return String(value).toUpperCase();
14638         },
14639
14640         /**
14641          * Converts the first character only of a string to upper case
14642          * @param {String} value The text to convert
14643          * @return {String} The converted text
14644          */
14645         capitalize : function(value){
14646             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14647         },
14648
14649         // private
14650         call : function(value, fn){
14651             if(arguments.length > 2){
14652                 var args = Array.prototype.slice.call(arguments, 2);
14653                 args.unshift(value);
14654                  
14655                 return /** eval:var:value */  eval(fn).apply(window, args);
14656             }else{
14657                 /** eval:var:value */
14658                 return /** eval:var:value */ eval(fn).call(window, value);
14659             }
14660         },
14661
14662        
14663         /**
14664          * safer version of Math.toFixed..??/
14665          * @param {Number/String} value The numeric value to format
14666          * @param {Number/String} value Decimal places 
14667          * @return {String} The formatted currency string
14668          */
14669         toFixed : function(v, n)
14670         {
14671             // why not use to fixed - precision is buggered???
14672             if (!n) {
14673                 return Math.round(v-0);
14674             }
14675             var fact = Math.pow(10,n+1);
14676             v = (Math.round((v-0)*fact))/fact;
14677             var z = (''+fact).substring(2);
14678             if (v == Math.floor(v)) {
14679                 return Math.floor(v) + '.' + z;
14680             }
14681             
14682             // now just padd decimals..
14683             var ps = String(v).split('.');
14684             var fd = (ps[1] + z);
14685             var r = fd.substring(0,n); 
14686             var rm = fd.substring(n); 
14687             if (rm < 5) {
14688                 return ps[0] + '.' + r;
14689             }
14690             r*=1; // turn it into a number;
14691             r++;
14692             if (String(r).length != n) {
14693                 ps[0]*=1;
14694                 ps[0]++;
14695                 r = String(r).substring(1); // chop the end off.
14696             }
14697             
14698             return ps[0] + '.' + r;
14699              
14700         },
14701         
14702         /**
14703          * Format a number as US currency
14704          * @param {Number/String} value The numeric value to format
14705          * @return {String} The formatted currency string
14706          */
14707         usMoney : function(v){
14708             return '$' + Roo.util.Format.number(v);
14709         },
14710         
14711         /**
14712          * Format a number
14713          * eventually this should probably emulate php's number_format
14714          * @param {Number/String} value The numeric value to format
14715          * @param {Number} decimals number of decimal places
14716          * @param {String} delimiter for thousands (default comma)
14717          * @return {String} The formatted currency string
14718          */
14719         number : function(v, decimals, thousandsDelimiter)
14720         {
14721             // multiply and round.
14722             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14723             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14724             
14725             var mul = Math.pow(10, decimals);
14726             var zero = String(mul).substring(1);
14727             v = (Math.round((v-0)*mul))/mul;
14728             
14729             // if it's '0' number.. then
14730             
14731             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14732             v = String(v);
14733             var ps = v.split('.');
14734             var whole = ps[0];
14735             
14736             var r = /(\d+)(\d{3})/;
14737             // add comma's
14738             
14739             if(thousandsDelimiter.length != 0) {
14740                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14741             } 
14742             
14743             var sub = ps[1] ?
14744                     // has decimals..
14745                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14746                     // does not have decimals
14747                     (decimals ? ('.' + zero) : '');
14748             
14749             
14750             return whole + sub ;
14751         },
14752         
14753         /**
14754          * Parse a value into a formatted date using the specified format pattern.
14755          * @param {Mixed} value The value to format
14756          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14757          * @return {String} The formatted date string
14758          */
14759         date : function(v, format){
14760             if(!v){
14761                 return "";
14762             }
14763             if(!(v instanceof Date)){
14764                 v = new Date(Date.parse(v));
14765             }
14766             return v.dateFormat(format || Roo.util.Format.defaults.date);
14767         },
14768
14769         /**
14770          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14771          * @param {String} format Any valid date format string
14772          * @return {Function} The date formatting function
14773          */
14774         dateRenderer : function(format){
14775             return function(v){
14776                 return Roo.util.Format.date(v, format);  
14777             };
14778         },
14779
14780         // private
14781         stripTagsRE : /<\/?[^>]+>/gi,
14782         
14783         /**
14784          * Strips all HTML tags
14785          * @param {Mixed} value The text from which to strip tags
14786          * @return {String} The stripped text
14787          */
14788         stripTags : function(v){
14789             return !v ? v : String(v).replace(this.stripTagsRE, "");
14790         },
14791         
14792         /**
14793          * Size in Mb,Gb etc.
14794          * @param {Number} value The number to be formated
14795          * @param {number} decimals how many decimal places
14796          * @return {String} the formated string
14797          */
14798         size : function(value, decimals)
14799         {
14800             var sizes = ['b', 'k', 'M', 'G', 'T'];
14801             if (value == 0) {
14802                 return 0;
14803             }
14804             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14805             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14806         }
14807         
14808         
14809         
14810     };
14811 }();
14812 Roo.util.Format.defaults = {
14813     date : 'd/M/Y'
14814 };/*
14815  * Based on:
14816  * Ext JS Library 1.1.1
14817  * Copyright(c) 2006-2007, Ext JS, LLC.
14818  *
14819  * Originally Released Under LGPL - original licence link has changed is not relivant.
14820  *
14821  * Fork - LGPL
14822  * <script type="text/javascript">
14823  */
14824
14825
14826  
14827
14828 /**
14829  * @class Roo.MasterTemplate
14830  * @extends Roo.Template
14831  * Provides a template that can have child templates. The syntax is:
14832 <pre><code>
14833 var t = new Roo.MasterTemplate(
14834         '&lt;select name="{name}"&gt;',
14835                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14836         '&lt;/select&gt;'
14837 );
14838 t.add('options', {value: 'foo', text: 'bar'});
14839 // or you can add multiple child elements in one shot
14840 t.addAll('options', [
14841     {value: 'foo', text: 'bar'},
14842     {value: 'foo2', text: 'bar2'},
14843     {value: 'foo3', text: 'bar3'}
14844 ]);
14845 // then append, applying the master template values
14846 t.append('my-form', {name: 'my-select'});
14847 </code></pre>
14848 * A name attribute for the child template is not required if you have only one child
14849 * template or you want to refer to them by index.
14850  */
14851 Roo.MasterTemplate = function(){
14852     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14853     this.originalHtml = this.html;
14854     var st = {};
14855     var m, re = this.subTemplateRe;
14856     re.lastIndex = 0;
14857     var subIndex = 0;
14858     while(m = re.exec(this.html)){
14859         var name = m[1], content = m[2];
14860         st[subIndex] = {
14861             name: name,
14862             index: subIndex,
14863             buffer: [],
14864             tpl : new Roo.Template(content)
14865         };
14866         if(name){
14867             st[name] = st[subIndex];
14868         }
14869         st[subIndex].tpl.compile();
14870         st[subIndex].tpl.call = this.call.createDelegate(this);
14871         subIndex++;
14872     }
14873     this.subCount = subIndex;
14874     this.subs = st;
14875 };
14876 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14877     /**
14878     * The regular expression used to match sub templates
14879     * @type RegExp
14880     * @property
14881     */
14882     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14883
14884     /**
14885      * Applies the passed values to a child template.
14886      * @param {String/Number} name (optional) The name or index of the child template
14887      * @param {Array/Object} values The values to be applied to the template
14888      * @return {MasterTemplate} this
14889      */
14890      add : function(name, values){
14891         if(arguments.length == 1){
14892             values = arguments[0];
14893             name = 0;
14894         }
14895         var s = this.subs[name];
14896         s.buffer[s.buffer.length] = s.tpl.apply(values);
14897         return this;
14898     },
14899
14900     /**
14901      * Applies all the passed values to a child template.
14902      * @param {String/Number} name (optional) The name or index of the child template
14903      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14904      * @param {Boolean} reset (optional) True to reset the template first
14905      * @return {MasterTemplate} this
14906      */
14907     fill : function(name, values, reset){
14908         var a = arguments;
14909         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14910             values = a[0];
14911             name = 0;
14912             reset = a[1];
14913         }
14914         if(reset){
14915             this.reset();
14916         }
14917         for(var i = 0, len = values.length; i < len; i++){
14918             this.add(name, values[i]);
14919         }
14920         return this;
14921     },
14922
14923     /**
14924      * Resets the template for reuse
14925      * @return {MasterTemplate} this
14926      */
14927      reset : function(){
14928         var s = this.subs;
14929         for(var i = 0; i < this.subCount; i++){
14930             s[i].buffer = [];
14931         }
14932         return this;
14933     },
14934
14935     applyTemplate : function(values){
14936         var s = this.subs;
14937         var replaceIndex = -1;
14938         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
14939             return s[++replaceIndex].buffer.join("");
14940         });
14941         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
14942     },
14943
14944     apply : function(){
14945         return this.applyTemplate.apply(this, arguments);
14946     },
14947
14948     compile : function(){return this;}
14949 });
14950
14951 /**
14952  * Alias for fill().
14953  * @method
14954  */
14955 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
14956  /**
14957  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
14958  * var tpl = Roo.MasterTemplate.from('element-id');
14959  * @param {String/HTMLElement} el
14960  * @param {Object} config
14961  * @static
14962  */
14963 Roo.MasterTemplate.from = function(el, config){
14964     el = Roo.getDom(el);
14965     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
14966 };/*
14967  * Based on:
14968  * Ext JS Library 1.1.1
14969  * Copyright(c) 2006-2007, Ext JS, LLC.
14970  *
14971  * Originally Released Under LGPL - original licence link has changed is not relivant.
14972  *
14973  * Fork - LGPL
14974  * <script type="text/javascript">
14975  */
14976
14977  
14978 /**
14979  * @class Roo.util.CSS
14980  * Utility class for manipulating CSS rules
14981  * @static
14982
14983  */
14984 Roo.util.CSS = function(){
14985         var rules = null;
14986         var doc = document;
14987
14988     var camelRe = /(-[a-z])/gi;
14989     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
14990
14991    return {
14992    /**
14993     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
14994     * tag and appended to the HEAD of the document.
14995     * @param {String|Object} cssText The text containing the css rules
14996     * @param {String} id An id to add to the stylesheet for later removal
14997     * @return {StyleSheet}
14998     */
14999     createStyleSheet : function(cssText, id){
15000         var ss;
15001         var head = doc.getElementsByTagName("head")[0];
15002         var nrules = doc.createElement("style");
15003         nrules.setAttribute("type", "text/css");
15004         if(id){
15005             nrules.setAttribute("id", id);
15006         }
15007         if (typeof(cssText) != 'string') {
15008             // support object maps..
15009             // not sure if this a good idea.. 
15010             // perhaps it should be merged with the general css handling
15011             // and handle js style props.
15012             var cssTextNew = [];
15013             for(var n in cssText) {
15014                 var citems = [];
15015                 for(var k in cssText[n]) {
15016                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15017                 }
15018                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15019                 
15020             }
15021             cssText = cssTextNew.join("\n");
15022             
15023         }
15024        
15025        
15026        if(Roo.isIE){
15027            head.appendChild(nrules);
15028            ss = nrules.styleSheet;
15029            ss.cssText = cssText;
15030        }else{
15031            try{
15032                 nrules.appendChild(doc.createTextNode(cssText));
15033            }catch(e){
15034                nrules.cssText = cssText; 
15035            }
15036            head.appendChild(nrules);
15037            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15038        }
15039        this.cacheStyleSheet(ss);
15040        return ss;
15041    },
15042
15043    /**
15044     * Removes a style or link tag by id
15045     * @param {String} id The id of the tag
15046     */
15047    removeStyleSheet : function(id){
15048        var existing = doc.getElementById(id);
15049        if(existing){
15050            existing.parentNode.removeChild(existing);
15051        }
15052    },
15053
15054    /**
15055     * Dynamically swaps an existing stylesheet reference for a new one
15056     * @param {String} id The id of an existing link tag to remove
15057     * @param {String} url The href of the new stylesheet to include
15058     */
15059    swapStyleSheet : function(id, url){
15060        this.removeStyleSheet(id);
15061        var ss = doc.createElement("link");
15062        ss.setAttribute("rel", "stylesheet");
15063        ss.setAttribute("type", "text/css");
15064        ss.setAttribute("id", id);
15065        ss.setAttribute("href", url);
15066        doc.getElementsByTagName("head")[0].appendChild(ss);
15067    },
15068    
15069    /**
15070     * Refresh the rule cache if you have dynamically added stylesheets
15071     * @return {Object} An object (hash) of rules indexed by selector
15072     */
15073    refreshCache : function(){
15074        return this.getRules(true);
15075    },
15076
15077    // private
15078    cacheStyleSheet : function(stylesheet){
15079        if(!rules){
15080            rules = {};
15081        }
15082        try{// try catch for cross domain access issue
15083            var ssRules = stylesheet.cssRules || stylesheet.rules;
15084            for(var j = ssRules.length-1; j >= 0; --j){
15085                rules[ssRules[j].selectorText] = ssRules[j];
15086            }
15087        }catch(e){}
15088    },
15089    
15090    /**
15091     * Gets all css rules for the document
15092     * @param {Boolean} refreshCache true to refresh the internal cache
15093     * @return {Object} An object (hash) of rules indexed by selector
15094     */
15095    getRules : function(refreshCache){
15096                 if(rules == null || refreshCache){
15097                         rules = {};
15098                         var ds = doc.styleSheets;
15099                         for(var i =0, len = ds.length; i < len; i++){
15100                             try{
15101                         this.cacheStyleSheet(ds[i]);
15102                     }catch(e){} 
15103                 }
15104                 }
15105                 return rules;
15106         },
15107         
15108         /**
15109     * Gets an an individual CSS rule by selector(s)
15110     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15111     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15112     * @return {CSSRule} The CSS rule or null if one is not found
15113     */
15114    getRule : function(selector, refreshCache){
15115                 var rs = this.getRules(refreshCache);
15116                 if(!(selector instanceof Array)){
15117                     return rs[selector];
15118                 }
15119                 for(var i = 0; i < selector.length; i++){
15120                         if(rs[selector[i]]){
15121                                 return rs[selector[i]];
15122                         }
15123                 }
15124                 return null;
15125         },
15126         
15127         
15128         /**
15129     * Updates a rule property
15130     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15131     * @param {String} property The css property
15132     * @param {String} value The new value for the property
15133     * @return {Boolean} true If a rule was found and updated
15134     */
15135    updateRule : function(selector, property, value){
15136                 if(!(selector instanceof Array)){
15137                         var rule = this.getRule(selector);
15138                         if(rule){
15139                                 rule.style[property.replace(camelRe, camelFn)] = value;
15140                                 return true;
15141                         }
15142                 }else{
15143                         for(var i = 0; i < selector.length; i++){
15144                                 if(this.updateRule(selector[i], property, value)){
15145                                         return true;
15146                                 }
15147                         }
15148                 }
15149                 return false;
15150         }
15151    };   
15152 }();/*
15153  * Based on:
15154  * Ext JS Library 1.1.1
15155  * Copyright(c) 2006-2007, Ext JS, LLC.
15156  *
15157  * Originally Released Under LGPL - original licence link has changed is not relivant.
15158  *
15159  * Fork - LGPL
15160  * <script type="text/javascript">
15161  */
15162
15163  
15164
15165 /**
15166  * @class Roo.util.ClickRepeater
15167  * @extends Roo.util.Observable
15168  * 
15169  * A wrapper class which can be applied to any element. Fires a "click" event while the
15170  * mouse is pressed. The interval between firings may be specified in the config but
15171  * defaults to 10 milliseconds.
15172  * 
15173  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15174  * 
15175  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15176  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15177  * Similar to an autorepeat key delay.
15178  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15179  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15180  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15181  *           "interval" and "delay" are ignored. "immediate" is honored.
15182  * @cfg {Boolean} preventDefault True to prevent the default click event
15183  * @cfg {Boolean} stopDefault True to stop the default click event
15184  * 
15185  * @history
15186  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15187  *     2007-02-02 jvs Renamed to ClickRepeater
15188  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15189  *
15190  *  @constructor
15191  * @param {String/HTMLElement/Element} el The element to listen on
15192  * @param {Object} config
15193  **/
15194 Roo.util.ClickRepeater = function(el, config)
15195 {
15196     this.el = Roo.get(el);
15197     this.el.unselectable();
15198
15199     Roo.apply(this, config);
15200
15201     this.addEvents({
15202     /**
15203      * @event mousedown
15204      * Fires when the mouse button is depressed.
15205      * @param {Roo.util.ClickRepeater} this
15206      */
15207         "mousedown" : true,
15208     /**
15209      * @event click
15210      * Fires on a specified interval during the time the element is pressed.
15211      * @param {Roo.util.ClickRepeater} this
15212      */
15213         "click" : true,
15214     /**
15215      * @event mouseup
15216      * Fires when the mouse key is released.
15217      * @param {Roo.util.ClickRepeater} this
15218      */
15219         "mouseup" : true
15220     });
15221
15222     this.el.on("mousedown", this.handleMouseDown, this);
15223     if(this.preventDefault || this.stopDefault){
15224         this.el.on("click", function(e){
15225             if(this.preventDefault){
15226                 e.preventDefault();
15227             }
15228             if(this.stopDefault){
15229                 e.stopEvent();
15230             }
15231         }, this);
15232     }
15233
15234     // allow inline handler
15235     if(this.handler){
15236         this.on("click", this.handler,  this.scope || this);
15237     }
15238
15239     Roo.util.ClickRepeater.superclass.constructor.call(this);
15240 };
15241
15242 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15243     interval : 20,
15244     delay: 250,
15245     preventDefault : true,
15246     stopDefault : false,
15247     timer : 0,
15248
15249     // private
15250     handleMouseDown : function(){
15251         clearTimeout(this.timer);
15252         this.el.blur();
15253         if(this.pressClass){
15254             this.el.addClass(this.pressClass);
15255         }
15256         this.mousedownTime = new Date();
15257
15258         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15259         this.el.on("mouseout", this.handleMouseOut, this);
15260
15261         this.fireEvent("mousedown", this);
15262         this.fireEvent("click", this);
15263         
15264         this.timer = this.click.defer(this.delay || this.interval, this);
15265     },
15266
15267     // private
15268     click : function(){
15269         this.fireEvent("click", this);
15270         this.timer = this.click.defer(this.getInterval(), this);
15271     },
15272
15273     // private
15274     getInterval: function(){
15275         if(!this.accelerate){
15276             return this.interval;
15277         }
15278         var pressTime = this.mousedownTime.getElapsed();
15279         if(pressTime < 500){
15280             return 400;
15281         }else if(pressTime < 1700){
15282             return 320;
15283         }else if(pressTime < 2600){
15284             return 250;
15285         }else if(pressTime < 3500){
15286             return 180;
15287         }else if(pressTime < 4400){
15288             return 140;
15289         }else if(pressTime < 5300){
15290             return 80;
15291         }else if(pressTime < 6200){
15292             return 50;
15293         }else{
15294             return 10;
15295         }
15296     },
15297
15298     // private
15299     handleMouseOut : function(){
15300         clearTimeout(this.timer);
15301         if(this.pressClass){
15302             this.el.removeClass(this.pressClass);
15303         }
15304         this.el.on("mouseover", this.handleMouseReturn, this);
15305     },
15306
15307     // private
15308     handleMouseReturn : function(){
15309         this.el.un("mouseover", this.handleMouseReturn);
15310         if(this.pressClass){
15311             this.el.addClass(this.pressClass);
15312         }
15313         this.click();
15314     },
15315
15316     // private
15317     handleMouseUp : function(){
15318         clearTimeout(this.timer);
15319         this.el.un("mouseover", this.handleMouseReturn);
15320         this.el.un("mouseout", this.handleMouseOut);
15321         Roo.get(document).un("mouseup", this.handleMouseUp);
15322         this.el.removeClass(this.pressClass);
15323         this.fireEvent("mouseup", this);
15324     }
15325 });/**
15326  * @class Roo.util.Clipboard
15327  * @static
15328  * 
15329  * Clipboard UTILS
15330  * 
15331  **/
15332 Roo.util.Clipboard = {
15333     /**
15334      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15335      * @param {String} text to copy to clipboard
15336      */
15337     write : function(text) {
15338         // navigator clipboard api needs a secure context (https)
15339         if (navigator.clipboard && window.isSecureContext) {
15340             // navigator clipboard api method'
15341             navigator.clipboard.writeText(text);
15342             return ;
15343         } 
15344         // text area method
15345         var ta = document.createElement("textarea");
15346         ta.value = text;
15347         // make the textarea out of viewport
15348         ta.style.position = "fixed";
15349         ta.style.left = "-999999px";
15350         ta.style.top = "-999999px";
15351         document.body.appendChild(ta);
15352         ta.focus();
15353         ta.select();
15354         document.execCommand('copy');
15355         (function() {
15356             ta.remove();
15357         }).defer(100);
15358         
15359     }
15360         
15361 }
15362     /*
15363  * Based on:
15364  * Ext JS Library 1.1.1
15365  * Copyright(c) 2006-2007, Ext JS, LLC.
15366  *
15367  * Originally Released Under LGPL - original licence link has changed is not relivant.
15368  *
15369  * Fork - LGPL
15370  * <script type="text/javascript">
15371  */
15372
15373  
15374 /**
15375  * @class Roo.KeyNav
15376  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15377  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15378  * way to implement custom navigation schemes for any UI component.</p>
15379  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15380  * pageUp, pageDown, del, home, end.  Usage:</p>
15381  <pre><code>
15382 var nav = new Roo.KeyNav("my-element", {
15383     "left" : function(e){
15384         this.moveLeft(e.ctrlKey);
15385     },
15386     "right" : function(e){
15387         this.moveRight(e.ctrlKey);
15388     },
15389     "enter" : function(e){
15390         this.save();
15391     },
15392     scope : this
15393 });
15394 </code></pre>
15395  * @constructor
15396  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15397  * @param {Object} config The config
15398  */
15399 Roo.KeyNav = function(el, config){
15400     this.el = Roo.get(el);
15401     Roo.apply(this, config);
15402     if(!this.disabled){
15403         this.disabled = true;
15404         this.enable();
15405     }
15406 };
15407
15408 Roo.KeyNav.prototype = {
15409     /**
15410      * @cfg {Boolean} disabled
15411      * True to disable this KeyNav instance (defaults to false)
15412      */
15413     disabled : false,
15414     /**
15415      * @cfg {String} defaultEventAction
15416      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15417      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15418      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15419      */
15420     defaultEventAction: "stopEvent",
15421     /**
15422      * @cfg {Boolean} forceKeyDown
15423      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15424      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15425      * handle keydown instead of keypress.
15426      */
15427     forceKeyDown : false,
15428
15429     // private
15430     prepareEvent : function(e){
15431         var k = e.getKey();
15432         var h = this.keyToHandler[k];
15433         //if(h && this[h]){
15434         //    e.stopPropagation();
15435         //}
15436         if(Roo.isSafari && h && k >= 37 && k <= 40){
15437             e.stopEvent();
15438         }
15439     },
15440
15441     // private
15442     relay : function(e){
15443         var k = e.getKey();
15444         var h = this.keyToHandler[k];
15445         if(h && this[h]){
15446             if(this.doRelay(e, this[h], h) !== true){
15447                 e[this.defaultEventAction]();
15448             }
15449         }
15450     },
15451
15452     // private
15453     doRelay : function(e, h, hname){
15454         return h.call(this.scope || this, e);
15455     },
15456
15457     // possible handlers
15458     enter : false,
15459     left : false,
15460     right : false,
15461     up : false,
15462     down : false,
15463     tab : false,
15464     esc : false,
15465     pageUp : false,
15466     pageDown : false,
15467     del : false,
15468     home : false,
15469     end : false,
15470
15471     // quick lookup hash
15472     keyToHandler : {
15473         37 : "left",
15474         39 : "right",
15475         38 : "up",
15476         40 : "down",
15477         33 : "pageUp",
15478         34 : "pageDown",
15479         46 : "del",
15480         36 : "home",
15481         35 : "end",
15482         13 : "enter",
15483         27 : "esc",
15484         9  : "tab"
15485     },
15486
15487         /**
15488          * Enable this KeyNav
15489          */
15490         enable: function(){
15491                 if(this.disabled){
15492             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15493             // the EventObject will normalize Safari automatically
15494             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15495                 this.el.on("keydown", this.relay,  this);
15496             }else{
15497                 this.el.on("keydown", this.prepareEvent,  this);
15498                 this.el.on("keypress", this.relay,  this);
15499             }
15500                     this.disabled = false;
15501                 }
15502         },
15503
15504         /**
15505          * Disable this KeyNav
15506          */
15507         disable: function(){
15508                 if(!this.disabled){
15509                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15510                 this.el.un("keydown", this.relay);
15511             }else{
15512                 this.el.un("keydown", this.prepareEvent);
15513                 this.el.un("keypress", this.relay);
15514             }
15515                     this.disabled = true;
15516                 }
15517         }
15518 };/*
15519  * Based on:
15520  * Ext JS Library 1.1.1
15521  * Copyright(c) 2006-2007, Ext JS, LLC.
15522  *
15523  * Originally Released Under LGPL - original licence link has changed is not relivant.
15524  *
15525  * Fork - LGPL
15526  * <script type="text/javascript">
15527  */
15528
15529  
15530 /**
15531  * @class Roo.KeyMap
15532  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15533  * The constructor accepts the same config object as defined by {@link #addBinding}.
15534  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15535  * combination it will call the function with this signature (if the match is a multi-key
15536  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15537  * A KeyMap can also handle a string representation of keys.<br />
15538  * Usage:
15539  <pre><code>
15540 // map one key by key code
15541 var map = new Roo.KeyMap("my-element", {
15542     key: 13, // or Roo.EventObject.ENTER
15543     fn: myHandler,
15544     scope: myObject
15545 });
15546
15547 // map multiple keys to one action by string
15548 var map = new Roo.KeyMap("my-element", {
15549     key: "a\r\n\t",
15550     fn: myHandler,
15551     scope: myObject
15552 });
15553
15554 // map multiple keys to multiple actions by strings and array of codes
15555 var map = new Roo.KeyMap("my-element", [
15556     {
15557         key: [10,13],
15558         fn: function(){ alert("Return was pressed"); }
15559     }, {
15560         key: "abc",
15561         fn: function(){ alert('a, b or c was pressed'); }
15562     }, {
15563         key: "\t",
15564         ctrl:true,
15565         shift:true,
15566         fn: function(){ alert('Control + shift + tab was pressed.'); }
15567     }
15568 ]);
15569 </code></pre>
15570  * <b>Note: A KeyMap starts enabled</b>
15571  * @constructor
15572  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15573  * @param {Object} config The config (see {@link #addBinding})
15574  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15575  */
15576 Roo.KeyMap = function(el, config, eventName){
15577     this.el  = Roo.get(el);
15578     this.eventName = eventName || "keydown";
15579     this.bindings = [];
15580     if(config){
15581         this.addBinding(config);
15582     }
15583     this.enable();
15584 };
15585
15586 Roo.KeyMap.prototype = {
15587     /**
15588      * True to stop the event from bubbling and prevent the default browser action if the
15589      * key was handled by the KeyMap (defaults to false)
15590      * @type Boolean
15591      */
15592     stopEvent : false,
15593
15594     /**
15595      * Add a new binding to this KeyMap. The following config object properties are supported:
15596      * <pre>
15597 Property    Type             Description
15598 ----------  ---------------  ----------------------------------------------------------------------
15599 key         String/Array     A single keycode or an array of keycodes to handle
15600 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15601 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15602 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15603 fn          Function         The function to call when KeyMap finds the expected key combination
15604 scope       Object           The scope of the callback function
15605 </pre>
15606      *
15607      * Usage:
15608      * <pre><code>
15609 // Create a KeyMap
15610 var map = new Roo.KeyMap(document, {
15611     key: Roo.EventObject.ENTER,
15612     fn: handleKey,
15613     scope: this
15614 });
15615
15616 //Add a new binding to the existing KeyMap later
15617 map.addBinding({
15618     key: 'abc',
15619     shift: true,
15620     fn: handleKey,
15621     scope: this
15622 });
15623 </code></pre>
15624      * @param {Object/Array} config A single KeyMap config or an array of configs
15625      */
15626         addBinding : function(config){
15627         if(config instanceof Array){
15628             for(var i = 0, len = config.length; i < len; i++){
15629                 this.addBinding(config[i]);
15630             }
15631             return;
15632         }
15633         var keyCode = config.key,
15634             shift = config.shift, 
15635             ctrl = config.ctrl, 
15636             alt = config.alt,
15637             fn = config.fn,
15638             scope = config.scope;
15639         if(typeof keyCode == "string"){
15640             var ks = [];
15641             var keyString = keyCode.toUpperCase();
15642             for(var j = 0, len = keyString.length; j < len; j++){
15643                 ks.push(keyString.charCodeAt(j));
15644             }
15645             keyCode = ks;
15646         }
15647         var keyArray = keyCode instanceof Array;
15648         var handler = function(e){
15649             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15650                 var k = e.getKey();
15651                 if(keyArray){
15652                     for(var i = 0, len = keyCode.length; i < len; i++){
15653                         if(keyCode[i] == k){
15654                           if(this.stopEvent){
15655                               e.stopEvent();
15656                           }
15657                           fn.call(scope || window, k, e);
15658                           return;
15659                         }
15660                     }
15661                 }else{
15662                     if(k == keyCode){
15663                         if(this.stopEvent){
15664                            e.stopEvent();
15665                         }
15666                         fn.call(scope || window, k, e);
15667                     }
15668                 }
15669             }
15670         };
15671         this.bindings.push(handler);  
15672         },
15673
15674     /**
15675      * Shorthand for adding a single key listener
15676      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15677      * following options:
15678      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15679      * @param {Function} fn The function to call
15680      * @param {Object} scope (optional) The scope of the function
15681      */
15682     on : function(key, fn, scope){
15683         var keyCode, shift, ctrl, alt;
15684         if(typeof key == "object" && !(key instanceof Array)){
15685             keyCode = key.key;
15686             shift = key.shift;
15687             ctrl = key.ctrl;
15688             alt = key.alt;
15689         }else{
15690             keyCode = key;
15691         }
15692         this.addBinding({
15693             key: keyCode,
15694             shift: shift,
15695             ctrl: ctrl,
15696             alt: alt,
15697             fn: fn,
15698             scope: scope
15699         })
15700     },
15701
15702     // private
15703     handleKeyDown : function(e){
15704             if(this.enabled){ //just in case
15705             var b = this.bindings;
15706             for(var i = 0, len = b.length; i < len; i++){
15707                 b[i].call(this, e);
15708             }
15709             }
15710         },
15711         
15712         /**
15713          * Returns true if this KeyMap is enabled
15714          * @return {Boolean} 
15715          */
15716         isEnabled : function(){
15717             return this.enabled;  
15718         },
15719         
15720         /**
15721          * Enables this KeyMap
15722          */
15723         enable: function(){
15724                 if(!this.enabled){
15725                     this.el.on(this.eventName, this.handleKeyDown, this);
15726                     this.enabled = true;
15727                 }
15728         },
15729
15730         /**
15731          * Disable this KeyMap
15732          */
15733         disable: function(){
15734                 if(this.enabled){
15735                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15736                     this.enabled = false;
15737                 }
15738         }
15739 };/*
15740  * Based on:
15741  * Ext JS Library 1.1.1
15742  * Copyright(c) 2006-2007, Ext JS, LLC.
15743  *
15744  * Originally Released Under LGPL - original licence link has changed is not relivant.
15745  *
15746  * Fork - LGPL
15747  * <script type="text/javascript">
15748  */
15749
15750  
15751 /**
15752  * @class Roo.util.TextMetrics
15753  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15754  * wide, in pixels, a given block of text will be.
15755  * @static
15756  */
15757 Roo.util.TextMetrics = function(){
15758     var shared;
15759     return {
15760         /**
15761          * Measures the size of the specified text
15762          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15763          * that can affect the size of the rendered text
15764          * @param {String} text The text to measure
15765          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15766          * in order to accurately measure the text height
15767          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15768          */
15769         measure : function(el, text, fixedWidth){
15770             if(!shared){
15771                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15772             }
15773             shared.bind(el);
15774             shared.setFixedWidth(fixedWidth || 'auto');
15775             return shared.getSize(text);
15776         },
15777
15778         /**
15779          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15780          * the overhead of multiple calls to initialize the style properties on each measurement.
15781          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15782          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15783          * in order to accurately measure the text height
15784          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15785          */
15786         createInstance : function(el, fixedWidth){
15787             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15788         }
15789     };
15790 }();
15791
15792 /**
15793  * @class Roo.util.TextMetrics.Instance
15794  * Instance of  TextMetrics Calcuation
15795  * @constructor
15796  * Create a new TextMetrics Instance
15797  * @param {Object} bindto
15798  * @param {Boolean} fixedWidth
15799  */
15800
15801 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15802 {
15803     var ml = new Roo.Element(document.createElement('div'));
15804     document.body.appendChild(ml.dom);
15805     ml.position('absolute');
15806     ml.setLeftTop(-1000, -1000);
15807     ml.hide();
15808
15809     if(fixedWidth){
15810         ml.setWidth(fixedWidth);
15811     }
15812      
15813     var instance = {
15814         /**
15815          * Returns the size of the specified text based on the internal element's style and width properties
15816          * @param {String} text The text to measure
15817          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15818          */
15819         getSize : function(text){
15820             ml.update(text);
15821             var s = ml.getSize();
15822             ml.update('');
15823             return s;
15824         },
15825
15826         /**
15827          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15828          * that can affect the size of the rendered text
15829          * @param {String/HTMLElement} el The element, dom node or id
15830          */
15831         bind : function(el){
15832             ml.setStyle(
15833                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15834             );
15835         },
15836
15837         /**
15838          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15839          * to set a fixed width in order to accurately measure the text height.
15840          * @param {Number} width The width to set on the element
15841          */
15842         setFixedWidth : function(width){
15843             ml.setWidth(width);
15844         },
15845
15846         /**
15847          * Returns the measured width of the specified text
15848          * @param {String} text The text to measure
15849          * @return {Number} width The width in pixels
15850          */
15851         getWidth : function(text){
15852             ml.dom.style.width = 'auto';
15853             return this.getSize(text).width;
15854         },
15855
15856         /**
15857          * Returns the measured height of the specified text.  For multiline text, be sure to call
15858          * {@link #setFixedWidth} if necessary.
15859          * @param {String} text The text to measure
15860          * @return {Number} height The height in pixels
15861          */
15862         getHeight : function(text){
15863             return this.getSize(text).height;
15864         }
15865     };
15866
15867     instance.bind(bindTo);
15868
15869     return instance;
15870 };
15871
15872 // backwards compat
15873 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15874  * Based on:
15875  * Ext JS Library 1.1.1
15876  * Copyright(c) 2006-2007, Ext JS, LLC.
15877  *
15878  * Originally Released Under LGPL - original licence link has changed is not relivant.
15879  *
15880  * Fork - LGPL
15881  * <script type="text/javascript">
15882  */
15883
15884 /**
15885  * @class Roo.state.Provider
15886  * Abstract base class for state provider implementations. This class provides methods
15887  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15888  * Provider interface.
15889  */
15890 Roo.state.Provider = function(){
15891     /**
15892      * @event statechange
15893      * Fires when a state change occurs.
15894      * @param {Provider} this This state provider
15895      * @param {String} key The state key which was changed
15896      * @param {String} value The encoded value for the state
15897      */
15898     this.addEvents({
15899         "statechange": true
15900     });
15901     this.state = {};
15902     Roo.state.Provider.superclass.constructor.call(this);
15903 };
15904 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15905     /**
15906      * Returns the current value for a key
15907      * @param {String} name The key name
15908      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15909      * @return {Mixed} The state data
15910      */
15911     get : function(name, defaultValue){
15912         return typeof this.state[name] == "undefined" ?
15913             defaultValue : this.state[name];
15914     },
15915     
15916     /**
15917      * Clears a value from the state
15918      * @param {String} name The key name
15919      */
15920     clear : function(name){
15921         delete this.state[name];
15922         this.fireEvent("statechange", this, name, null);
15923     },
15924     
15925     /**
15926      * Sets the value for a key
15927      * @param {String} name The key name
15928      * @param {Mixed} value The value to set
15929      */
15930     set : function(name, value){
15931         this.state[name] = value;
15932         this.fireEvent("statechange", this, name, value);
15933     },
15934     
15935     /**
15936      * Decodes a string previously encoded with {@link #encodeValue}.
15937      * @param {String} value The value to decode
15938      * @return {Mixed} The decoded value
15939      */
15940     decodeValue : function(cookie){
15941         var re = /^(a|n|d|b|s|o)\:(.*)$/;
15942         var matches = re.exec(unescape(cookie));
15943         if(!matches || !matches[1]) {
15944             return; // non state cookie
15945         }
15946         var type = matches[1];
15947         var v = matches[2];
15948         switch(type){
15949             case "n":
15950                 return parseFloat(v);
15951             case "d":
15952                 return new Date(Date.parse(v));
15953             case "b":
15954                 return (v == "1");
15955             case "a":
15956                 var all = [];
15957                 var values = v.split("^");
15958                 for(var i = 0, len = values.length; i < len; i++){
15959                     all.push(this.decodeValue(values[i]));
15960                 }
15961                 return all;
15962            case "o":
15963                 var all = {};
15964                 var values = v.split("^");
15965                 for(var i = 0, len = values.length; i < len; i++){
15966                     var kv = values[i].split("=");
15967                     all[kv[0]] = this.decodeValue(kv[1]);
15968                 }
15969                 return all;
15970            default:
15971                 return v;
15972         }
15973     },
15974     
15975     /**
15976      * Encodes a value including type information.  Decode with {@link #decodeValue}.
15977      * @param {Mixed} value The value to encode
15978      * @return {String} The encoded value
15979      */
15980     encodeValue : function(v){
15981         var enc;
15982         if(typeof v == "number"){
15983             enc = "n:" + v;
15984         }else if(typeof v == "boolean"){
15985             enc = "b:" + (v ? "1" : "0");
15986         }else if(v instanceof Date){
15987             enc = "d:" + v.toGMTString();
15988         }else if(v instanceof Array){
15989             var flat = "";
15990             for(var i = 0, len = v.length; i < len; i++){
15991                 flat += this.encodeValue(v[i]);
15992                 if(i != len-1) {
15993                     flat += "^";
15994                 }
15995             }
15996             enc = "a:" + flat;
15997         }else if(typeof v == "object"){
15998             var flat = "";
15999             for(var key in v){
16000                 if(typeof v[key] != "function"){
16001                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16002                 }
16003             }
16004             enc = "o:" + flat.substring(0, flat.length-1);
16005         }else{
16006             enc = "s:" + v;
16007         }
16008         return escape(enc);        
16009     }
16010 });
16011
16012 /*
16013  * Based on:
16014  * Ext JS Library 1.1.1
16015  * Copyright(c) 2006-2007, Ext JS, LLC.
16016  *
16017  * Originally Released Under LGPL - original licence link has changed is not relivant.
16018  *
16019  * Fork - LGPL
16020  * <script type="text/javascript">
16021  */
16022 /**
16023  * @class Roo.state.Manager
16024  * This is the global state manager. By default all components that are "state aware" check this class
16025  * for state information if you don't pass them a custom state provider. In order for this class
16026  * to be useful, it must be initialized with a provider when your application initializes.
16027  <pre><code>
16028 // in your initialization function
16029 init : function(){
16030    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16031    ...
16032    // supposed you have a {@link Roo.BorderLayout}
16033    var layout = new Roo.BorderLayout(...);
16034    layout.restoreState();
16035    // or a {Roo.BasicDialog}
16036    var dialog = new Roo.BasicDialog(...);
16037    dialog.restoreState();
16038  </code></pre>
16039  * @static
16040  */
16041 Roo.state.Manager = function(){
16042     var provider = new Roo.state.Provider();
16043     
16044     return {
16045         /**
16046          * Configures the default state provider for your application
16047          * @param {Provider} stateProvider The state provider to set
16048          */
16049         setProvider : function(stateProvider){
16050             provider = stateProvider;
16051         },
16052         
16053         /**
16054          * Returns the current value for a key
16055          * @param {String} name The key name
16056          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16057          * @return {Mixed} The state data
16058          */
16059         get : function(key, defaultValue){
16060             return provider.get(key, defaultValue);
16061         },
16062         
16063         /**
16064          * Sets the value for a key
16065          * @param {String} name The key name
16066          * @param {Mixed} value The state data
16067          */
16068          set : function(key, value){
16069             provider.set(key, value);
16070         },
16071         
16072         /**
16073          * Clears a value from the state
16074          * @param {String} name The key name
16075          */
16076         clear : function(key){
16077             provider.clear(key);
16078         },
16079         
16080         /**
16081          * Gets the currently configured state provider
16082          * @return {Provider} The state provider
16083          */
16084         getProvider : function(){
16085             return provider;
16086         }
16087     };
16088 }();
16089 /*
16090  * Based on:
16091  * Ext JS Library 1.1.1
16092  * Copyright(c) 2006-2007, Ext JS, LLC.
16093  *
16094  * Originally Released Under LGPL - original licence link has changed is not relivant.
16095  *
16096  * Fork - LGPL
16097  * <script type="text/javascript">
16098  */
16099 /**
16100  * @class Roo.state.CookieProvider
16101  * @extends Roo.state.Provider
16102  * The default Provider implementation which saves state via cookies.
16103  * <br />Usage:
16104  <pre><code>
16105    var cp = new Roo.state.CookieProvider({
16106        path: "/cgi-bin/",
16107        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16108        domain: "roojs.com"
16109    })
16110    Roo.state.Manager.setProvider(cp);
16111  </code></pre>
16112  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16113  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16114  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16115  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16116  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16117  * domain the page is running on including the 'www' like 'www.roojs.com')
16118  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16119  * @constructor
16120  * Create a new CookieProvider
16121  * @param {Object} config The configuration object
16122  */
16123 Roo.state.CookieProvider = function(config){
16124     Roo.state.CookieProvider.superclass.constructor.call(this);
16125     this.path = "/";
16126     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16127     this.domain = null;
16128     this.secure = false;
16129     Roo.apply(this, config);
16130     this.state = this.readCookies();
16131 };
16132
16133 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16134     // private
16135     set : function(name, value){
16136         if(typeof value == "undefined" || value === null){
16137             this.clear(name);
16138             return;
16139         }
16140         this.setCookie(name, value);
16141         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16142     },
16143
16144     // private
16145     clear : function(name){
16146         this.clearCookie(name);
16147         Roo.state.CookieProvider.superclass.clear.call(this, name);
16148     },
16149
16150     // private
16151     readCookies : function(){
16152         var cookies = {};
16153         var c = document.cookie + ";";
16154         var re = /\s?(.*?)=(.*?);/g;
16155         var matches;
16156         while((matches = re.exec(c)) != null){
16157             var name = matches[1];
16158             var value = matches[2];
16159             if(name && name.substring(0,3) == "ys-"){
16160                 cookies[name.substr(3)] = this.decodeValue(value);
16161             }
16162         }
16163         return cookies;
16164     },
16165
16166     // private
16167     setCookie : function(name, value){
16168         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16169            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16170            ((this.path == null) ? "" : ("; path=" + this.path)) +
16171            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16172            ((this.secure == true) ? "; secure" : "");
16173     },
16174
16175     // private
16176     clearCookie : function(name){
16177         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16178            ((this.path == null) ? "" : ("; path=" + this.path)) +
16179            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16180            ((this.secure == true) ? "; secure" : "");
16181     }
16182 });/*
16183  * Based on:
16184  * Ext JS Library 1.1.1
16185  * Copyright(c) 2006-2007, Ext JS, LLC.
16186  *
16187  * Originally Released Under LGPL - original licence link has changed is not relivant.
16188  *
16189  * Fork - LGPL
16190  * <script type="text/javascript">
16191  */
16192  
16193
16194 /**
16195  * @class Roo.ComponentMgr
16196  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16197  * @static
16198  */
16199 Roo.ComponentMgr = function(){
16200     var all = new Roo.util.MixedCollection();
16201
16202     return {
16203         /**
16204          * Registers a component.
16205          * @param {Roo.Component} c The component
16206          */
16207         register : function(c){
16208             all.add(c);
16209         },
16210
16211         /**
16212          * Unregisters a component.
16213          * @param {Roo.Component} c The component
16214          */
16215         unregister : function(c){
16216             all.remove(c);
16217         },
16218
16219         /**
16220          * Returns a component by id
16221          * @param {String} id The component id
16222          */
16223         get : function(id){
16224             return all.get(id);
16225         },
16226
16227         /**
16228          * Registers a function that will be called when a specified component is added to ComponentMgr
16229          * @param {String} id The component id
16230          * @param {Funtction} fn The callback function
16231          * @param {Object} scope The scope of the callback
16232          */
16233         onAvailable : function(id, fn, scope){
16234             all.on("add", function(index, o){
16235                 if(o.id == id){
16236                     fn.call(scope || o, o);
16237                     all.un("add", fn, scope);
16238                 }
16239             });
16240         }
16241     };
16242 }();/*
16243  * Based on:
16244  * Ext JS Library 1.1.1
16245  * Copyright(c) 2006-2007, Ext JS, LLC.
16246  *
16247  * Originally Released Under LGPL - original licence link has changed is not relivant.
16248  *
16249  * Fork - LGPL
16250  * <script type="text/javascript">
16251  */
16252  
16253 /**
16254  * @class Roo.Component
16255  * @extends Roo.util.Observable
16256  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16257  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16258  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16259  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16260  * All visual components (widgets) that require rendering into a layout should subclass Component.
16261  * @constructor
16262  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16263  * 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
16264  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16265  */
16266 Roo.Component = function(config){
16267     config = config || {};
16268     if(config.tagName || config.dom || typeof config == "string"){ // element object
16269         config = {el: config, id: config.id || config};
16270     }
16271     this.initialConfig = config;
16272
16273     Roo.apply(this, config);
16274     this.addEvents({
16275         /**
16276          * @event disable
16277          * Fires after the component is disabled.
16278              * @param {Roo.Component} this
16279              */
16280         disable : true,
16281         /**
16282          * @event enable
16283          * Fires after the component is enabled.
16284              * @param {Roo.Component} this
16285              */
16286         enable : true,
16287         /**
16288          * @event beforeshow
16289          * Fires before the component is shown.  Return false to stop the show.
16290              * @param {Roo.Component} this
16291              */
16292         beforeshow : true,
16293         /**
16294          * @event show
16295          * Fires after the component is shown.
16296              * @param {Roo.Component} this
16297              */
16298         show : true,
16299         /**
16300          * @event beforehide
16301          * Fires before the component is hidden. Return false to stop the hide.
16302              * @param {Roo.Component} this
16303              */
16304         beforehide : true,
16305         /**
16306          * @event hide
16307          * Fires after the component is hidden.
16308              * @param {Roo.Component} this
16309              */
16310         hide : true,
16311         /**
16312          * @event beforerender
16313          * Fires before the component is rendered. Return false to stop the render.
16314              * @param {Roo.Component} this
16315              */
16316         beforerender : true,
16317         /**
16318          * @event render
16319          * Fires after the component is rendered.
16320              * @param {Roo.Component} this
16321              */
16322         render : true,
16323         /**
16324          * @event beforedestroy
16325          * Fires before the component is destroyed. Return false to stop the destroy.
16326              * @param {Roo.Component} this
16327              */
16328         beforedestroy : true,
16329         /**
16330          * @event destroy
16331          * Fires after the component is destroyed.
16332              * @param {Roo.Component} this
16333              */
16334         destroy : true
16335     });
16336     if(!this.id){
16337         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16338     }
16339     Roo.ComponentMgr.register(this);
16340     Roo.Component.superclass.constructor.call(this);
16341     this.initComponent();
16342     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16343         this.render(this.renderTo);
16344         delete this.renderTo;
16345     }
16346 };
16347
16348 /** @private */
16349 Roo.Component.AUTO_ID = 1000;
16350
16351 Roo.extend(Roo.Component, Roo.util.Observable, {
16352     /**
16353      * @scope Roo.Component.prototype
16354      * @type {Boolean}
16355      * true if this component is hidden. Read-only.
16356      */
16357     hidden : false,
16358     /**
16359      * @type {Boolean}
16360      * true if this component is disabled. Read-only.
16361      */
16362     disabled : false,
16363     /**
16364      * @type {Boolean}
16365      * true if this component has been rendered. Read-only.
16366      */
16367     rendered : false,
16368     
16369     /** @cfg {String} disableClass
16370      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16371      */
16372     disabledClass : "x-item-disabled",
16373         /** @cfg {Boolean} allowDomMove
16374          * Whether the component can move the Dom node when rendering (defaults to true).
16375          */
16376     allowDomMove : true,
16377     /** @cfg {String} hideMode (display|visibility)
16378      * How this component should hidden. Supported values are
16379      * "visibility" (css visibility), "offsets" (negative offset position) and
16380      * "display" (css display) - defaults to "display".
16381      */
16382     hideMode: 'display',
16383
16384     /** @private */
16385     ctype : "Roo.Component",
16386
16387     /**
16388      * @cfg {String} actionMode 
16389      * which property holds the element that used for  hide() / show() / disable() / enable()
16390      * default is 'el' for forms you probably want to set this to fieldEl 
16391      */
16392     actionMode : "el",
16393
16394     /** @private */
16395     getActionEl : function(){
16396         return this[this.actionMode];
16397     },
16398
16399     initComponent : Roo.emptyFn,
16400     /**
16401      * If this is a lazy rendering component, render it to its container element.
16402      * @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.
16403      */
16404     render : function(container, position){
16405         
16406         if(this.rendered){
16407             return this;
16408         }
16409         
16410         if(this.fireEvent("beforerender", this) === false){
16411             return false;
16412         }
16413         
16414         if(!container && this.el){
16415             this.el = Roo.get(this.el);
16416             container = this.el.dom.parentNode;
16417             this.allowDomMove = false;
16418         }
16419         this.container = Roo.get(container);
16420         this.rendered = true;
16421         if(position !== undefined){
16422             if(typeof position == 'number'){
16423                 position = this.container.dom.childNodes[position];
16424             }else{
16425                 position = Roo.getDom(position);
16426             }
16427         }
16428         this.onRender(this.container, position || null);
16429         if(this.cls){
16430             this.el.addClass(this.cls);
16431             delete this.cls;
16432         }
16433         if(this.style){
16434             this.el.applyStyles(this.style);
16435             delete this.style;
16436         }
16437         this.fireEvent("render", this);
16438         this.afterRender(this.container);
16439         if(this.hidden){
16440             this.hide();
16441         }
16442         if(this.disabled){
16443             this.disable();
16444         }
16445
16446         return this;
16447         
16448     },
16449
16450     /** @private */
16451     // default function is not really useful
16452     onRender : function(ct, position){
16453         if(this.el){
16454             this.el = Roo.get(this.el);
16455             if(this.allowDomMove !== false){
16456                 ct.dom.insertBefore(this.el.dom, position);
16457             }
16458         }
16459     },
16460
16461     /** @private */
16462     getAutoCreate : function(){
16463         var cfg = typeof this.autoCreate == "object" ?
16464                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16465         if(this.id && !cfg.id){
16466             cfg.id = this.id;
16467         }
16468         return cfg;
16469     },
16470
16471     /** @private */
16472     afterRender : Roo.emptyFn,
16473
16474     /**
16475      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16476      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16477      */
16478     destroy : function(){
16479         if(this.fireEvent("beforedestroy", this) !== false){
16480             this.purgeListeners();
16481             this.beforeDestroy();
16482             if(this.rendered){
16483                 this.el.removeAllListeners();
16484                 this.el.remove();
16485                 if(this.actionMode == "container"){
16486                     this.container.remove();
16487                 }
16488             }
16489             this.onDestroy();
16490             Roo.ComponentMgr.unregister(this);
16491             this.fireEvent("destroy", this);
16492         }
16493     },
16494
16495         /** @private */
16496     beforeDestroy : function(){
16497
16498     },
16499
16500         /** @private */
16501         onDestroy : function(){
16502
16503     },
16504
16505     /**
16506      * Returns the underlying {@link Roo.Element}.
16507      * @return {Roo.Element} The element
16508      */
16509     getEl : function(){
16510         return this.el;
16511     },
16512
16513     /**
16514      * Returns the id of this component.
16515      * @return {String}
16516      */
16517     getId : function(){
16518         return this.id;
16519     },
16520
16521     /**
16522      * Try to focus this component.
16523      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16524      * @return {Roo.Component} this
16525      */
16526     focus : function(selectText){
16527         if(this.rendered){
16528             this.el.focus();
16529             if(selectText === true){
16530                 this.el.dom.select();
16531             }
16532         }
16533         return this;
16534     },
16535
16536     /** @private */
16537     blur : function(){
16538         if(this.rendered){
16539             this.el.blur();
16540         }
16541         return this;
16542     },
16543
16544     /**
16545      * Disable this component.
16546      * @return {Roo.Component} this
16547      */
16548     disable : function(){
16549         if(this.rendered){
16550             this.onDisable();
16551         }
16552         this.disabled = true;
16553         this.fireEvent("disable", this);
16554         return this;
16555     },
16556
16557         // private
16558     onDisable : function(){
16559         this.getActionEl().addClass(this.disabledClass);
16560         this.el.dom.disabled = true;
16561     },
16562
16563     /**
16564      * Enable this component.
16565      * @return {Roo.Component} this
16566      */
16567     enable : function(){
16568         if(this.rendered){
16569             this.onEnable();
16570         }
16571         this.disabled = false;
16572         this.fireEvent("enable", this);
16573         return this;
16574     },
16575
16576         // private
16577     onEnable : function(){
16578         this.getActionEl().removeClass(this.disabledClass);
16579         this.el.dom.disabled = false;
16580     },
16581
16582     /**
16583      * Convenience function for setting disabled/enabled by boolean.
16584      * @param {Boolean} disabled
16585      */
16586     setDisabled : function(disabled){
16587         this[disabled ? "disable" : "enable"]();
16588     },
16589
16590     /**
16591      * Show this component.
16592      * @return {Roo.Component} this
16593      */
16594     show: function(){
16595         if(this.fireEvent("beforeshow", this) !== false){
16596             this.hidden = false;
16597             if(this.rendered){
16598                 this.onShow();
16599             }
16600             this.fireEvent("show", this);
16601         }
16602         return this;
16603     },
16604
16605     // private
16606     onShow : function(){
16607         var ae = this.getActionEl();
16608         if(this.hideMode == 'visibility'){
16609             ae.dom.style.visibility = "visible";
16610         }else if(this.hideMode == 'offsets'){
16611             ae.removeClass('x-hidden');
16612         }else{
16613             ae.dom.style.display = "";
16614         }
16615     },
16616
16617     /**
16618      * Hide this component.
16619      * @return {Roo.Component} this
16620      */
16621     hide: function(){
16622         if(this.fireEvent("beforehide", this) !== false){
16623             this.hidden = true;
16624             if(this.rendered){
16625                 this.onHide();
16626             }
16627             this.fireEvent("hide", this);
16628         }
16629         return this;
16630     },
16631
16632     // private
16633     onHide : function(){
16634         var ae = this.getActionEl();
16635         if(this.hideMode == 'visibility'){
16636             ae.dom.style.visibility = "hidden";
16637         }else if(this.hideMode == 'offsets'){
16638             ae.addClass('x-hidden');
16639         }else{
16640             ae.dom.style.display = "none";
16641         }
16642     },
16643
16644     /**
16645      * Convenience function to hide or show this component by boolean.
16646      * @param {Boolean} visible True to show, false to hide
16647      * @return {Roo.Component} this
16648      */
16649     setVisible: function(visible){
16650         if(visible) {
16651             this.show();
16652         }else{
16653             this.hide();
16654         }
16655         return this;
16656     },
16657
16658     /**
16659      * Returns true if this component is visible.
16660      */
16661     isVisible : function(){
16662         return this.getActionEl().isVisible();
16663     },
16664
16665     cloneConfig : function(overrides){
16666         overrides = overrides || {};
16667         var id = overrides.id || Roo.id();
16668         var cfg = Roo.applyIf(overrides, this.initialConfig);
16669         cfg.id = id; // prevent dup id
16670         return new this.constructor(cfg);
16671     }
16672 });/*
16673  * Based on:
16674  * Ext JS Library 1.1.1
16675  * Copyright(c) 2006-2007, Ext JS, LLC.
16676  *
16677  * Originally Released Under LGPL - original licence link has changed is not relivant.
16678  *
16679  * Fork - LGPL
16680  * <script type="text/javascript">
16681  */
16682
16683 /**
16684  * @class Roo.BoxComponent
16685  * @extends Roo.Component
16686  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16687  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16688  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16689  * layout containers.
16690  * @constructor
16691  * @param {Roo.Element/String/Object} config The configuration options.
16692  */
16693 Roo.BoxComponent = function(config){
16694     Roo.Component.call(this, config);
16695     this.addEvents({
16696         /**
16697          * @event resize
16698          * Fires after the component is resized.
16699              * @param {Roo.Component} this
16700              * @param {Number} adjWidth The box-adjusted width that was set
16701              * @param {Number} adjHeight The box-adjusted height that was set
16702              * @param {Number} rawWidth The width that was originally specified
16703              * @param {Number} rawHeight The height that was originally specified
16704              */
16705         resize : true,
16706         /**
16707          * @event move
16708          * Fires after the component is moved.
16709              * @param {Roo.Component} this
16710              * @param {Number} x The new x position
16711              * @param {Number} y The new y position
16712              */
16713         move : true
16714     });
16715 };
16716
16717 Roo.extend(Roo.BoxComponent, Roo.Component, {
16718     // private, set in afterRender to signify that the component has been rendered
16719     boxReady : false,
16720     // private, used to defer height settings to subclasses
16721     deferHeight: false,
16722     /** @cfg {Number} width
16723      * width (optional) size of component
16724      */
16725      /** @cfg {Number} height
16726      * height (optional) size of component
16727      */
16728      
16729     /**
16730      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16731      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16732      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16733      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16734      * @return {Roo.BoxComponent} this
16735      */
16736     setSize : function(w, h){
16737         // support for standard size objects
16738         if(typeof w == 'object'){
16739             h = w.height;
16740             w = w.width;
16741         }
16742         // not rendered
16743         if(!this.boxReady){
16744             this.width = w;
16745             this.height = h;
16746             return this;
16747         }
16748
16749         // prevent recalcs when not needed
16750         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16751             return this;
16752         }
16753         this.lastSize = {width: w, height: h};
16754
16755         var adj = this.adjustSize(w, h);
16756         var aw = adj.width, ah = adj.height;
16757         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16758             var rz = this.getResizeEl();
16759             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16760                 rz.setSize(aw, ah);
16761             }else if(!this.deferHeight && ah !== undefined){
16762                 rz.setHeight(ah);
16763             }else if(aw !== undefined){
16764                 rz.setWidth(aw);
16765             }
16766             this.onResize(aw, ah, w, h);
16767             this.fireEvent('resize', this, aw, ah, w, h);
16768         }
16769         return this;
16770     },
16771
16772     /**
16773      * Gets the current size of the component's underlying element.
16774      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16775      */
16776     getSize : function(){
16777         return this.el.getSize();
16778     },
16779
16780     /**
16781      * Gets the current XY position of the component's underlying element.
16782      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16783      * @return {Array} The XY position of the element (e.g., [100, 200])
16784      */
16785     getPosition : function(local){
16786         if(local === true){
16787             return [this.el.getLeft(true), this.el.getTop(true)];
16788         }
16789         return this.xy || this.el.getXY();
16790     },
16791
16792     /**
16793      * Gets the current box measurements of the component's underlying element.
16794      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16795      * @returns {Object} box An object in the format {x, y, width, height}
16796      */
16797     getBox : function(local){
16798         var s = this.el.getSize();
16799         if(local){
16800             s.x = this.el.getLeft(true);
16801             s.y = this.el.getTop(true);
16802         }else{
16803             var xy = this.xy || this.el.getXY();
16804             s.x = xy[0];
16805             s.y = xy[1];
16806         }
16807         return s;
16808     },
16809
16810     /**
16811      * Sets the current box measurements of the component's underlying element.
16812      * @param {Object} box An object in the format {x, y, width, height}
16813      * @returns {Roo.BoxComponent} this
16814      */
16815     updateBox : function(box){
16816         this.setSize(box.width, box.height);
16817         this.setPagePosition(box.x, box.y);
16818         return this;
16819     },
16820
16821     // protected
16822     getResizeEl : function(){
16823         return this.resizeEl || this.el;
16824     },
16825
16826     // protected
16827     getPositionEl : function(){
16828         return this.positionEl || this.el;
16829     },
16830
16831     /**
16832      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16833      * This method fires the move event.
16834      * @param {Number} left The new left
16835      * @param {Number} top The new top
16836      * @returns {Roo.BoxComponent} this
16837      */
16838     setPosition : function(x, y){
16839         this.x = x;
16840         this.y = y;
16841         if(!this.boxReady){
16842             return this;
16843         }
16844         var adj = this.adjustPosition(x, y);
16845         var ax = adj.x, ay = adj.y;
16846
16847         var el = this.getPositionEl();
16848         if(ax !== undefined || ay !== undefined){
16849             if(ax !== undefined && ay !== undefined){
16850                 el.setLeftTop(ax, ay);
16851             }else if(ax !== undefined){
16852                 el.setLeft(ax);
16853             }else if(ay !== undefined){
16854                 el.setTop(ay);
16855             }
16856             this.onPosition(ax, ay);
16857             this.fireEvent('move', this, ax, ay);
16858         }
16859         return this;
16860     },
16861
16862     /**
16863      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16864      * This method fires the move event.
16865      * @param {Number} x The new x position
16866      * @param {Number} y The new y position
16867      * @returns {Roo.BoxComponent} this
16868      */
16869     setPagePosition : function(x, y){
16870         this.pageX = x;
16871         this.pageY = y;
16872         if(!this.boxReady){
16873             return;
16874         }
16875         if(x === undefined || y === undefined){ // cannot translate undefined points
16876             return;
16877         }
16878         var p = this.el.translatePoints(x, y);
16879         this.setPosition(p.left, p.top);
16880         return this;
16881     },
16882
16883     // private
16884     onRender : function(ct, position){
16885         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16886         if(this.resizeEl){
16887             this.resizeEl = Roo.get(this.resizeEl);
16888         }
16889         if(this.positionEl){
16890             this.positionEl = Roo.get(this.positionEl);
16891         }
16892     },
16893
16894     // private
16895     afterRender : function(){
16896         Roo.BoxComponent.superclass.afterRender.call(this);
16897         this.boxReady = true;
16898         this.setSize(this.width, this.height);
16899         if(this.x || this.y){
16900             this.setPosition(this.x, this.y);
16901         }
16902         if(this.pageX || this.pageY){
16903             this.setPagePosition(this.pageX, this.pageY);
16904         }
16905     },
16906
16907     /**
16908      * Force the component's size to recalculate based on the underlying element's current height and width.
16909      * @returns {Roo.BoxComponent} this
16910      */
16911     syncSize : function(){
16912         delete this.lastSize;
16913         this.setSize(this.el.getWidth(), this.el.getHeight());
16914         return this;
16915     },
16916
16917     /**
16918      * Called after the component is resized, this method is empty by default but can be implemented by any
16919      * subclass that needs to perform custom logic after a resize occurs.
16920      * @param {Number} adjWidth The box-adjusted width that was set
16921      * @param {Number} adjHeight The box-adjusted height that was set
16922      * @param {Number} rawWidth The width that was originally specified
16923      * @param {Number} rawHeight The height that was originally specified
16924      */
16925     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
16926
16927     },
16928
16929     /**
16930      * Called after the component is moved, this method is empty by default but can be implemented by any
16931      * subclass that needs to perform custom logic after a move occurs.
16932      * @param {Number} x The new x position
16933      * @param {Number} y The new y position
16934      */
16935     onPosition : function(x, y){
16936
16937     },
16938
16939     // private
16940     adjustSize : function(w, h){
16941         if(this.autoWidth){
16942             w = 'auto';
16943         }
16944         if(this.autoHeight){
16945             h = 'auto';
16946         }
16947         return {width : w, height: h};
16948     },
16949
16950     // private
16951     adjustPosition : function(x, y){
16952         return {x : x, y: y};
16953     }
16954 });/*
16955  * Based on:
16956  * Ext JS Library 1.1.1
16957  * Copyright(c) 2006-2007, Ext JS, LLC.
16958  *
16959  * Originally Released Under LGPL - original licence link has changed is not relivant.
16960  *
16961  * Fork - LGPL
16962  * <script type="text/javascript">
16963  */
16964  (function(){ 
16965 /**
16966  * @class Roo.Layer
16967  * @extends Roo.Element
16968  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
16969  * automatic maintaining of shadow/shim positions.
16970  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
16971  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
16972  * you can pass a string with a CSS class name. False turns off the shadow.
16973  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
16974  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
16975  * @cfg {String} cls CSS class to add to the element
16976  * @cfg {Number} zindex Starting z-index (defaults to 11000)
16977  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
16978  * @constructor
16979  * @param {Object} config An object with config options.
16980  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
16981  */
16982
16983 Roo.Layer = function(config, existingEl){
16984     config = config || {};
16985     var dh = Roo.DomHelper;
16986     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
16987     if(existingEl){
16988         this.dom = Roo.getDom(existingEl);
16989     }
16990     if(!this.dom){
16991         var o = config.dh || {tag: "div", cls: "x-layer"};
16992         this.dom = dh.append(pel, o);
16993     }
16994     if(config.cls){
16995         this.addClass(config.cls);
16996     }
16997     this.constrain = config.constrain !== false;
16998     this.visibilityMode = Roo.Element.VISIBILITY;
16999     if(config.id){
17000         this.id = this.dom.id = config.id;
17001     }else{
17002         this.id = Roo.id(this.dom);
17003     }
17004     this.zindex = config.zindex || this.getZIndex();
17005     this.position("absolute", this.zindex);
17006     if(config.shadow){
17007         this.shadowOffset = config.shadowOffset || 4;
17008         this.shadow = new Roo.Shadow({
17009             offset : this.shadowOffset,
17010             mode : config.shadow
17011         });
17012     }else{
17013         this.shadowOffset = 0;
17014     }
17015     this.useShim = config.shim !== false && Roo.useShims;
17016     this.useDisplay = config.useDisplay;
17017     this.hide();
17018 };
17019
17020 var supr = Roo.Element.prototype;
17021
17022 // shims are shared among layer to keep from having 100 iframes
17023 var shims = [];
17024
17025 Roo.extend(Roo.Layer, Roo.Element, {
17026
17027     getZIndex : function(){
17028         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17029     },
17030
17031     getShim : function(){
17032         if(!this.useShim){
17033             return null;
17034         }
17035         if(this.shim){
17036             return this.shim;
17037         }
17038         var shim = shims.shift();
17039         if(!shim){
17040             shim = this.createShim();
17041             shim.enableDisplayMode('block');
17042             shim.dom.style.display = 'none';
17043             shim.dom.style.visibility = 'visible';
17044         }
17045         var pn = this.dom.parentNode;
17046         if(shim.dom.parentNode != pn){
17047             pn.insertBefore(shim.dom, this.dom);
17048         }
17049         shim.setStyle('z-index', this.getZIndex()-2);
17050         this.shim = shim;
17051         return shim;
17052     },
17053
17054     hideShim : function(){
17055         if(this.shim){
17056             this.shim.setDisplayed(false);
17057             shims.push(this.shim);
17058             delete this.shim;
17059         }
17060     },
17061
17062     disableShadow : function(){
17063         if(this.shadow){
17064             this.shadowDisabled = true;
17065             this.shadow.hide();
17066             this.lastShadowOffset = this.shadowOffset;
17067             this.shadowOffset = 0;
17068         }
17069     },
17070
17071     enableShadow : function(show){
17072         if(this.shadow){
17073             this.shadowDisabled = false;
17074             this.shadowOffset = this.lastShadowOffset;
17075             delete this.lastShadowOffset;
17076             if(show){
17077                 this.sync(true);
17078             }
17079         }
17080     },
17081
17082     // private
17083     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17084     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17085     sync : function(doShow){
17086         var sw = this.shadow;
17087         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17088             var sh = this.getShim();
17089
17090             var w = this.getWidth(),
17091                 h = this.getHeight();
17092
17093             var l = this.getLeft(true),
17094                 t = this.getTop(true);
17095
17096             if(sw && !this.shadowDisabled){
17097                 if(doShow && !sw.isVisible()){
17098                     sw.show(this);
17099                 }else{
17100                     sw.realign(l, t, w, h);
17101                 }
17102                 if(sh){
17103                     if(doShow){
17104                        sh.show();
17105                     }
17106                     // fit the shim behind the shadow, so it is shimmed too
17107                     var a = sw.adjusts, s = sh.dom.style;
17108                     s.left = (Math.min(l, l+a.l))+"px";
17109                     s.top = (Math.min(t, t+a.t))+"px";
17110                     s.width = (w+a.w)+"px";
17111                     s.height = (h+a.h)+"px";
17112                 }
17113             }else if(sh){
17114                 if(doShow){
17115                    sh.show();
17116                 }
17117                 sh.setSize(w, h);
17118                 sh.setLeftTop(l, t);
17119             }
17120             
17121         }
17122     },
17123
17124     // private
17125     destroy : function(){
17126         this.hideShim();
17127         if(this.shadow){
17128             this.shadow.hide();
17129         }
17130         this.removeAllListeners();
17131         var pn = this.dom.parentNode;
17132         if(pn){
17133             pn.removeChild(this.dom);
17134         }
17135         Roo.Element.uncache(this.id);
17136     },
17137
17138     remove : function(){
17139         this.destroy();
17140     },
17141
17142     // private
17143     beginUpdate : function(){
17144         this.updating = true;
17145     },
17146
17147     // private
17148     endUpdate : function(){
17149         this.updating = false;
17150         this.sync(true);
17151     },
17152
17153     // private
17154     hideUnders : function(negOffset){
17155         if(this.shadow){
17156             this.shadow.hide();
17157         }
17158         this.hideShim();
17159     },
17160
17161     // private
17162     constrainXY : function(){
17163         if(this.constrain){
17164             var vw = Roo.lib.Dom.getViewWidth(),
17165                 vh = Roo.lib.Dom.getViewHeight();
17166             var s = Roo.get(document).getScroll();
17167
17168             var xy = this.getXY();
17169             var x = xy[0], y = xy[1];   
17170             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17171             // only move it if it needs it
17172             var moved = false;
17173             // first validate right/bottom
17174             if((x + w) > vw+s.left){
17175                 x = vw - w - this.shadowOffset;
17176                 moved = true;
17177             }
17178             if((y + h) > vh+s.top){
17179                 y = vh - h - this.shadowOffset;
17180                 moved = true;
17181             }
17182             // then make sure top/left isn't negative
17183             if(x < s.left){
17184                 x = s.left;
17185                 moved = true;
17186             }
17187             if(y < s.top){
17188                 y = s.top;
17189                 moved = true;
17190             }
17191             if(moved){
17192                 if(this.avoidY){
17193                     var ay = this.avoidY;
17194                     if(y <= ay && (y+h) >= ay){
17195                         y = ay-h-5;   
17196                     }
17197                 }
17198                 xy = [x, y];
17199                 this.storeXY(xy);
17200                 supr.setXY.call(this, xy);
17201                 this.sync();
17202             }
17203         }
17204     },
17205
17206     isVisible : function(){
17207         return this.visible;    
17208     },
17209
17210     // private
17211     showAction : function(){
17212         this.visible = true; // track visibility to prevent getStyle calls
17213         if(this.useDisplay === true){
17214             this.setDisplayed("");
17215         }else if(this.lastXY){
17216             supr.setXY.call(this, this.lastXY);
17217         }else if(this.lastLT){
17218             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17219         }
17220     },
17221
17222     // private
17223     hideAction : function(){
17224         this.visible = false;
17225         if(this.useDisplay === true){
17226             this.setDisplayed(false);
17227         }else{
17228             this.setLeftTop(-10000,-10000);
17229         }
17230     },
17231
17232     // overridden Element method
17233     setVisible : function(v, a, d, c, e){
17234         if(v){
17235             this.showAction();
17236         }
17237         if(a && v){
17238             var cb = function(){
17239                 this.sync(true);
17240                 if(c){
17241                     c();
17242                 }
17243             }.createDelegate(this);
17244             supr.setVisible.call(this, true, true, d, cb, e);
17245         }else{
17246             if(!v){
17247                 this.hideUnders(true);
17248             }
17249             var cb = c;
17250             if(a){
17251                 cb = function(){
17252                     this.hideAction();
17253                     if(c){
17254                         c();
17255                     }
17256                 }.createDelegate(this);
17257             }
17258             supr.setVisible.call(this, v, a, d, cb, e);
17259             if(v){
17260                 this.sync(true);
17261             }else if(!a){
17262                 this.hideAction();
17263             }
17264         }
17265     },
17266
17267     storeXY : function(xy){
17268         delete this.lastLT;
17269         this.lastXY = xy;
17270     },
17271
17272     storeLeftTop : function(left, top){
17273         delete this.lastXY;
17274         this.lastLT = [left, top];
17275     },
17276
17277     // private
17278     beforeFx : function(){
17279         this.beforeAction();
17280         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17281     },
17282
17283     // private
17284     afterFx : function(){
17285         Roo.Layer.superclass.afterFx.apply(this, arguments);
17286         this.sync(this.isVisible());
17287     },
17288
17289     // private
17290     beforeAction : function(){
17291         if(!this.updating && this.shadow){
17292             this.shadow.hide();
17293         }
17294     },
17295
17296     // overridden Element method
17297     setLeft : function(left){
17298         this.storeLeftTop(left, this.getTop(true));
17299         supr.setLeft.apply(this, arguments);
17300         this.sync();
17301     },
17302
17303     setTop : function(top){
17304         this.storeLeftTop(this.getLeft(true), top);
17305         supr.setTop.apply(this, arguments);
17306         this.sync();
17307     },
17308
17309     setLeftTop : function(left, top){
17310         this.storeLeftTop(left, top);
17311         supr.setLeftTop.apply(this, arguments);
17312         this.sync();
17313     },
17314
17315     setXY : function(xy, a, d, c, e){
17316         this.fixDisplay();
17317         this.beforeAction();
17318         this.storeXY(xy);
17319         var cb = this.createCB(c);
17320         supr.setXY.call(this, xy, a, d, cb, e);
17321         if(!a){
17322             cb();
17323         }
17324     },
17325
17326     // private
17327     createCB : function(c){
17328         var el = this;
17329         return function(){
17330             el.constrainXY();
17331             el.sync(true);
17332             if(c){
17333                 c();
17334             }
17335         };
17336     },
17337
17338     // overridden Element method
17339     setX : function(x, a, d, c, e){
17340         this.setXY([x, this.getY()], a, d, c, e);
17341     },
17342
17343     // overridden Element method
17344     setY : function(y, a, d, c, e){
17345         this.setXY([this.getX(), y], a, d, c, e);
17346     },
17347
17348     // overridden Element method
17349     setSize : function(w, h, a, d, c, e){
17350         this.beforeAction();
17351         var cb = this.createCB(c);
17352         supr.setSize.call(this, w, h, a, d, cb, e);
17353         if(!a){
17354             cb();
17355         }
17356     },
17357
17358     // overridden Element method
17359     setWidth : function(w, a, d, c, e){
17360         this.beforeAction();
17361         var cb = this.createCB(c);
17362         supr.setWidth.call(this, w, a, d, cb, e);
17363         if(!a){
17364             cb();
17365         }
17366     },
17367
17368     // overridden Element method
17369     setHeight : function(h, a, d, c, e){
17370         this.beforeAction();
17371         var cb = this.createCB(c);
17372         supr.setHeight.call(this, h, a, d, cb, e);
17373         if(!a){
17374             cb();
17375         }
17376     },
17377
17378     // overridden Element method
17379     setBounds : function(x, y, w, h, a, d, c, e){
17380         this.beforeAction();
17381         var cb = this.createCB(c);
17382         if(!a){
17383             this.storeXY([x, y]);
17384             supr.setXY.call(this, [x, y]);
17385             supr.setSize.call(this, w, h, a, d, cb, e);
17386             cb();
17387         }else{
17388             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17389         }
17390         return this;
17391     },
17392     
17393     /**
17394      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17395      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17396      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17397      * @param {Number} zindex The new z-index to set
17398      * @return {this} The Layer
17399      */
17400     setZIndex : function(zindex){
17401         this.zindex = zindex;
17402         this.setStyle("z-index", zindex + 2);
17403         if(this.shadow){
17404             this.shadow.setZIndex(zindex + 1);
17405         }
17406         if(this.shim){
17407             this.shim.setStyle("z-index", zindex);
17408         }
17409     }
17410 });
17411 })();/*
17412  * Original code for Roojs - LGPL
17413  * <script type="text/javascript">
17414  */
17415  
17416 /**
17417  * @class Roo.XComponent
17418  * A delayed Element creator...
17419  * Or a way to group chunks of interface together.
17420  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17421  *  used in conjunction with XComponent.build() it will create an instance of each element,
17422  *  then call addxtype() to build the User interface.
17423  * 
17424  * Mypart.xyx = new Roo.XComponent({
17425
17426     parent : 'Mypart.xyz', // empty == document.element.!!
17427     order : '001',
17428     name : 'xxxx'
17429     region : 'xxxx'
17430     disabled : function() {} 
17431      
17432     tree : function() { // return an tree of xtype declared components
17433         var MODULE = this;
17434         return 
17435         {
17436             xtype : 'NestedLayoutPanel',
17437             // technicall
17438         }
17439      ]
17440  *})
17441  *
17442  *
17443  * It can be used to build a big heiracy, with parent etc.
17444  * or you can just use this to render a single compoent to a dom element
17445  * MYPART.render(Roo.Element | String(id) | dom_element )
17446  *
17447  *
17448  * Usage patterns.
17449  *
17450  * Classic Roo
17451  *
17452  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17453  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17454  *
17455  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17456  *
17457  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17458  * - if mulitple topModules exist, the last one is defined as the top module.
17459  *
17460  * Embeded Roo
17461  * 
17462  * When the top level or multiple modules are to embedded into a existing HTML page,
17463  * the parent element can container '#id' of the element where the module will be drawn.
17464  *
17465  * Bootstrap Roo
17466  *
17467  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17468  * it relies more on a include mechanism, where sub modules are included into an outer page.
17469  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17470  * 
17471  * Bootstrap Roo Included elements
17472  *
17473  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17474  * hence confusing the component builder as it thinks there are multiple top level elements. 
17475  *
17476  * String Over-ride & Translations
17477  *
17478  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17479  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17480  * are needed. @see Roo.XComponent.overlayString  
17481  * 
17482  * 
17483  * 
17484  * @extends Roo.util.Observable
17485  * @constructor
17486  * @param cfg {Object} configuration of component
17487  * 
17488  */
17489 Roo.XComponent = function(cfg) {
17490     Roo.apply(this, cfg);
17491     this.addEvents({ 
17492         /**
17493              * @event built
17494              * Fires when this the componnt is built
17495              * @param {Roo.XComponent} c the component
17496              */
17497         'built' : true
17498         
17499     });
17500     this.region = this.region || 'center'; // default..
17501     Roo.XComponent.register(this);
17502     this.modules = false;
17503     this.el = false; // where the layout goes..
17504     
17505     
17506 }
17507 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17508     /**
17509      * @property el
17510      * The created element (with Roo.factory())
17511      * @type {Roo.Layout}
17512      */
17513     el  : false,
17514     
17515     /**
17516      * @property el
17517      * for BC  - use el in new code
17518      * @type {Roo.Layout}
17519      */
17520     panel : false,
17521     
17522     /**
17523      * @property layout
17524      * for BC  - use el in new code
17525      * @type {Roo.Layout}
17526      */
17527     layout : false,
17528     
17529      /**
17530      * @cfg {Function|boolean} disabled
17531      * If this module is disabled by some rule, return true from the funtion
17532      */
17533     disabled : false,
17534     
17535     /**
17536      * @cfg {String} parent 
17537      * Name of parent element which it get xtype added to..
17538      */
17539     parent: false,
17540     
17541     /**
17542      * @cfg {String} order
17543      * Used to set the order in which elements are created (usefull for multiple tabs)
17544      */
17545     
17546     order : false,
17547     /**
17548      * @cfg {String} name
17549      * String to display while loading.
17550      */
17551     name : false,
17552     /**
17553      * @cfg {String} region
17554      * Region to render component to (defaults to center)
17555      */
17556     region : 'center',
17557     
17558     /**
17559      * @cfg {Array} items
17560      * A single item array - the first element is the root of the tree..
17561      * It's done this way to stay compatible with the Xtype system...
17562      */
17563     items : false,
17564     
17565     /**
17566      * @property _tree
17567      * The method that retuns the tree of parts that make up this compoennt 
17568      * @type {function}
17569      */
17570     _tree  : false,
17571     
17572      /**
17573      * render
17574      * render element to dom or tree
17575      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17576      */
17577     
17578     render : function(el)
17579     {
17580         
17581         el = el || false;
17582         var hp = this.parent ? 1 : 0;
17583         Roo.debug &&  Roo.log(this);
17584         
17585         var tree = this._tree ? this._tree() : this.tree();
17586
17587         
17588         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17589             // if parent is a '#.....' string, then let's use that..
17590             var ename = this.parent.substr(1);
17591             this.parent = false;
17592             Roo.debug && Roo.log(ename);
17593             switch (ename) {
17594                 case 'bootstrap-body':
17595                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17596                         // this is the BorderLayout standard?
17597                        this.parent = { el : true };
17598                        break;
17599                     }
17600                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17601                         // need to insert stuff...
17602                         this.parent =  {
17603                              el : new Roo.bootstrap.layout.Border({
17604                                  el : document.body, 
17605                      
17606                                  center: {
17607                                     titlebar: false,
17608                                     autoScroll:false,
17609                                     closeOnTab: true,
17610                                     tabPosition: 'top',
17611                                       //resizeTabs: true,
17612                                     alwaysShowTabs: true,
17613                                     hideTabs: false
17614                                      //minTabWidth: 140
17615                                  }
17616                              })
17617                         
17618                          };
17619                          break;
17620                     }
17621                          
17622                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17623                         this.parent = { el :  new  Roo.bootstrap.Body() };
17624                         Roo.debug && Roo.log("setting el to doc body");
17625                          
17626                     } else {
17627                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17628                     }
17629                     break;
17630                 case 'bootstrap':
17631                     this.parent = { el : true};
17632                     // fall through
17633                 default:
17634                     el = Roo.get(ename);
17635                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17636                         this.parent = { el : true};
17637                     }
17638                     
17639                     break;
17640             }
17641                 
17642             
17643             if (!el && !this.parent) {
17644                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17645                 return;
17646             }
17647         }
17648         
17649         Roo.debug && Roo.log("EL:");
17650         Roo.debug && Roo.log(el);
17651         Roo.debug && Roo.log("this.parent.el:");
17652         Roo.debug && Roo.log(this.parent.el);
17653         
17654
17655         // altertive root elements ??? - we need a better way to indicate these.
17656         var is_alt = Roo.XComponent.is_alt ||
17657                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17658                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17659                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17660         
17661         
17662         
17663         if (!this.parent && is_alt) {
17664             //el = Roo.get(document.body);
17665             this.parent = { el : true };
17666         }
17667             
17668             
17669         
17670         if (!this.parent) {
17671             
17672             Roo.debug && Roo.log("no parent - creating one");
17673             
17674             el = el ? Roo.get(el) : false;      
17675             
17676             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17677                 
17678                 this.parent =  {
17679                     el : new Roo.bootstrap.layout.Border({
17680                         el: el || document.body,
17681                     
17682                         center: {
17683                             titlebar: false,
17684                             autoScroll:false,
17685                             closeOnTab: true,
17686                             tabPosition: 'top',
17687                              //resizeTabs: true,
17688                             alwaysShowTabs: false,
17689                             hideTabs: true,
17690                             minTabWidth: 140,
17691                             overflow: 'visible'
17692                          }
17693                      })
17694                 };
17695             } else {
17696             
17697                 // it's a top level one..
17698                 this.parent =  {
17699                     el : new Roo.BorderLayout(el || document.body, {
17700                         center: {
17701                             titlebar: false,
17702                             autoScroll:false,
17703                             closeOnTab: true,
17704                             tabPosition: 'top',
17705                              //resizeTabs: true,
17706                             alwaysShowTabs: el && hp? false :  true,
17707                             hideTabs: el || !hp ? true :  false,
17708                             minTabWidth: 140
17709                          }
17710                     })
17711                 };
17712             }
17713         }
17714         
17715         if (!this.parent.el) {
17716                 // probably an old style ctor, which has been disabled.
17717                 return;
17718
17719         }
17720                 // The 'tree' method is  '_tree now' 
17721             
17722         tree.region = tree.region || this.region;
17723         var is_body = false;
17724         if (this.parent.el === true) {
17725             // bootstrap... - body..
17726             if (el) {
17727                 tree.el = el;
17728             }
17729             this.parent.el = Roo.factory(tree);
17730             is_body = true;
17731         }
17732         
17733         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17734         this.fireEvent('built', this);
17735         
17736         this.panel = this.el;
17737         this.layout = this.panel.layout;
17738         this.parentLayout = this.parent.layout  || false;  
17739          
17740     }
17741     
17742 });
17743
17744 Roo.apply(Roo.XComponent, {
17745     /**
17746      * @property  hideProgress
17747      * true to disable the building progress bar.. usefull on single page renders.
17748      * @type Boolean
17749      */
17750     hideProgress : false,
17751     /**
17752      * @property  buildCompleted
17753      * True when the builder has completed building the interface.
17754      * @type Boolean
17755      */
17756     buildCompleted : false,
17757      
17758     /**
17759      * @property  topModule
17760      * the upper most module - uses document.element as it's constructor.
17761      * @type Object
17762      */
17763      
17764     topModule  : false,
17765       
17766     /**
17767      * @property  modules
17768      * array of modules to be created by registration system.
17769      * @type {Array} of Roo.XComponent
17770      */
17771     
17772     modules : [],
17773     /**
17774      * @property  elmodules
17775      * array of modules to be created by which use #ID 
17776      * @type {Array} of Roo.XComponent
17777      */
17778      
17779     elmodules : [],
17780
17781      /**
17782      * @property  is_alt
17783      * Is an alternative Root - normally used by bootstrap or other systems,
17784      *    where the top element in the tree can wrap 'body' 
17785      * @type {boolean}  (default false)
17786      */
17787      
17788     is_alt : false,
17789     /**
17790      * @property  build_from_html
17791      * Build elements from html - used by bootstrap HTML stuff 
17792      *    - this is cleared after build is completed
17793      * @type {boolean}    (default false)
17794      */
17795      
17796     build_from_html : false,
17797     /**
17798      * Register components to be built later.
17799      *
17800      * This solves the following issues
17801      * - Building is not done on page load, but after an authentication process has occured.
17802      * - Interface elements are registered on page load
17803      * - Parent Interface elements may not be loaded before child, so this handles that..
17804      * 
17805      *
17806      * example:
17807      * 
17808      * MyApp.register({
17809           order : '000001',
17810           module : 'Pman.Tab.projectMgr',
17811           region : 'center',
17812           parent : 'Pman.layout',
17813           disabled : false,  // or use a function..
17814         })
17815      
17816      * * @param {Object} details about module
17817      */
17818     register : function(obj) {
17819                 
17820         Roo.XComponent.event.fireEvent('register', obj);
17821         switch(typeof(obj.disabled) ) {
17822                 
17823             case 'undefined':
17824                 break;
17825             
17826             case 'function':
17827                 if ( obj.disabled() ) {
17828                         return;
17829                 }
17830                 break;
17831             
17832             default:
17833                 if (obj.disabled || obj.region == '#disabled') {
17834                         return;
17835                 }
17836                 break;
17837         }
17838                 
17839         this.modules.push(obj);
17840          
17841     },
17842     /**
17843      * convert a string to an object..
17844      * eg. 'AAA.BBB' -> finds AAA.BBB
17845
17846      */
17847     
17848     toObject : function(str)
17849     {
17850         if (!str || typeof(str) == 'object') {
17851             return str;
17852         }
17853         if (str.substring(0,1) == '#') {
17854             return str;
17855         }
17856
17857         var ar = str.split('.');
17858         var rt, o;
17859         rt = ar.shift();
17860             /** eval:var:o */
17861         try {
17862             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17863         } catch (e) {
17864             throw "Module not found : " + str;
17865         }
17866         
17867         if (o === false) {
17868             throw "Module not found : " + str;
17869         }
17870         Roo.each(ar, function(e) {
17871             if (typeof(o[e]) == 'undefined') {
17872                 throw "Module not found : " + str;
17873             }
17874             o = o[e];
17875         });
17876         
17877         return o;
17878         
17879     },
17880     
17881     
17882     /**
17883      * move modules into their correct place in the tree..
17884      * 
17885      */
17886     preBuild : function ()
17887     {
17888         var _t = this;
17889         Roo.each(this.modules , function (obj)
17890         {
17891             Roo.XComponent.event.fireEvent('beforebuild', obj);
17892             
17893             var opar = obj.parent;
17894             try { 
17895                 obj.parent = this.toObject(opar);
17896             } catch(e) {
17897                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17898                 return;
17899             }
17900             
17901             if (!obj.parent) {
17902                 Roo.debug && Roo.log("GOT top level module");
17903                 Roo.debug && Roo.log(obj);
17904                 obj.modules = new Roo.util.MixedCollection(false, 
17905                     function(o) { return o.order + '' }
17906                 );
17907                 this.topModule = obj;
17908                 return;
17909             }
17910                         // parent is a string (usually a dom element name..)
17911             if (typeof(obj.parent) == 'string') {
17912                 this.elmodules.push(obj);
17913                 return;
17914             }
17915             if (obj.parent.constructor != Roo.XComponent) {
17916                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17917             }
17918             if (!obj.parent.modules) {
17919                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17920                     function(o) { return o.order + '' }
17921                 );
17922             }
17923             if (obj.parent.disabled) {
17924                 obj.disabled = true;
17925             }
17926             obj.parent.modules.add(obj);
17927         }, this);
17928     },
17929     
17930      /**
17931      * make a list of modules to build.
17932      * @return {Array} list of modules. 
17933      */ 
17934     
17935     buildOrder : function()
17936     {
17937         var _this = this;
17938         var cmp = function(a,b) {   
17939             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
17940         };
17941         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
17942             throw "No top level modules to build";
17943         }
17944         
17945         // make a flat list in order of modules to build.
17946         var mods = this.topModule ? [ this.topModule ] : [];
17947                 
17948         
17949         // elmodules (is a list of DOM based modules )
17950         Roo.each(this.elmodules, function(e) {
17951             mods.push(e);
17952             if (!this.topModule &&
17953                 typeof(e.parent) == 'string' &&
17954                 e.parent.substring(0,1) == '#' &&
17955                 Roo.get(e.parent.substr(1))
17956                ) {
17957                 
17958                 _this.topModule = e;
17959             }
17960             
17961         });
17962
17963         
17964         // add modules to their parents..
17965         var addMod = function(m) {
17966             Roo.debug && Roo.log("build Order: add: " + m.name);
17967                 
17968             mods.push(m);
17969             if (m.modules && !m.disabled) {
17970                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
17971                 m.modules.keySort('ASC',  cmp );
17972                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
17973     
17974                 m.modules.each(addMod);
17975             } else {
17976                 Roo.debug && Roo.log("build Order: no child modules");
17977             }
17978             // not sure if this is used any more..
17979             if (m.finalize) {
17980                 m.finalize.name = m.name + " (clean up) ";
17981                 mods.push(m.finalize);
17982             }
17983             
17984         }
17985         if (this.topModule && this.topModule.modules) { 
17986             this.topModule.modules.keySort('ASC',  cmp );
17987             this.topModule.modules.each(addMod);
17988         } 
17989         return mods;
17990     },
17991     
17992      /**
17993      * Build the registered modules.
17994      * @param {Object} parent element.
17995      * @param {Function} optional method to call after module has been added.
17996      * 
17997      */ 
17998    
17999     build : function(opts) 
18000     {
18001         
18002         if (typeof(opts) != 'undefined') {
18003             Roo.apply(this,opts);
18004         }
18005         
18006         this.preBuild();
18007         var mods = this.buildOrder();
18008       
18009         //this.allmods = mods;
18010         //Roo.debug && Roo.log(mods);
18011         //return;
18012         if (!mods.length) { // should not happen
18013             throw "NO modules!!!";
18014         }
18015         
18016         
18017         var msg = "Building Interface...";
18018         // flash it up as modal - so we store the mask!?
18019         if (!this.hideProgress && Roo.MessageBox) {
18020             Roo.MessageBox.show({ title: 'loading' });
18021             Roo.MessageBox.show({
18022                title: "Please wait...",
18023                msg: msg,
18024                width:450,
18025                progress:true,
18026                buttons : false,
18027                closable:false,
18028                modal: false
18029               
18030             });
18031         }
18032         var total = mods.length;
18033         
18034         var _this = this;
18035         var progressRun = function() {
18036             if (!mods.length) {
18037                 Roo.debug && Roo.log('hide?');
18038                 if (!this.hideProgress && Roo.MessageBox) {
18039                     Roo.MessageBox.hide();
18040                 }
18041                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18042                 
18043                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18044                 
18045                 // THE END...
18046                 return false;   
18047             }
18048             
18049             var m = mods.shift();
18050             
18051             
18052             Roo.debug && Roo.log(m);
18053             // not sure if this is supported any more.. - modules that are are just function
18054             if (typeof(m) == 'function') { 
18055                 m.call(this);
18056                 return progressRun.defer(10, _this);
18057             } 
18058             
18059             
18060             msg = "Building Interface " + (total  - mods.length) + 
18061                     " of " + total + 
18062                     (m.name ? (' - ' + m.name) : '');
18063                         Roo.debug && Roo.log(msg);
18064             if (!_this.hideProgress &&  Roo.MessageBox) { 
18065                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18066             }
18067             
18068          
18069             // is the module disabled?
18070             var disabled = (typeof(m.disabled) == 'function') ?
18071                 m.disabled.call(m.module.disabled) : m.disabled;    
18072             
18073             
18074             if (disabled) {
18075                 return progressRun(); // we do not update the display!
18076             }
18077             
18078             // now build 
18079             
18080                         
18081                         
18082             m.render();
18083             // it's 10 on top level, and 1 on others??? why...
18084             return progressRun.defer(10, _this);
18085              
18086         }
18087         progressRun.defer(1, _this);
18088      
18089         
18090         
18091     },
18092     /**
18093      * Overlay a set of modified strings onto a component
18094      * This is dependant on our builder exporting the strings and 'named strings' elements.
18095      * 
18096      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18097      * @param {Object} associative array of 'named' string and it's new value.
18098      * 
18099      */
18100         overlayStrings : function( component, strings )
18101     {
18102         if (typeof(component['_named_strings']) == 'undefined') {
18103             throw "ERROR: component does not have _named_strings";
18104         }
18105         for ( var k in strings ) {
18106             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18107             if (md !== false) {
18108                 component['_strings'][md] = strings[k];
18109             } else {
18110                 Roo.log('could not find named string: ' + k + ' in');
18111                 Roo.log(component);
18112             }
18113             
18114         }
18115         
18116     },
18117     
18118         
18119         /**
18120          * Event Object.
18121          *
18122          *
18123          */
18124         event: false, 
18125     /**
18126          * wrapper for event.on - aliased later..  
18127          * Typically use to register a event handler for register:
18128          *
18129          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18130          *
18131          */
18132     on : false
18133    
18134     
18135     
18136 });
18137
18138 Roo.XComponent.event = new Roo.util.Observable({
18139                 events : { 
18140                         /**
18141                          * @event register
18142                          * Fires when an Component is registered,
18143                          * set the disable property on the Component to stop registration.
18144                          * @param {Roo.XComponent} c the component being registerd.
18145                          * 
18146                          */
18147                         'register' : true,
18148             /**
18149                          * @event beforebuild
18150                          * Fires before each Component is built
18151                          * can be used to apply permissions.
18152                          * @param {Roo.XComponent} c the component being registerd.
18153                          * 
18154                          */
18155                         'beforebuild' : true,
18156                         /**
18157                          * @event buildcomplete
18158                          * Fires on the top level element when all elements have been built
18159                          * @param {Roo.XComponent} the top level component.
18160                          */
18161                         'buildcomplete' : true
18162                         
18163                 }
18164 });
18165
18166 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18167  //
18168  /**
18169  * marked - a markdown parser
18170  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18171  * https://github.com/chjj/marked
18172  */
18173
18174
18175 /**
18176  *
18177  * Roo.Markdown - is a very crude wrapper around marked..
18178  *
18179  * usage:
18180  * 
18181  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18182  * 
18183  * Note: move the sample code to the bottom of this
18184  * file before uncommenting it.
18185  *
18186  */
18187
18188 Roo.Markdown = {};
18189 Roo.Markdown.toHtml = function(text) {
18190     
18191     var c = new Roo.Markdown.marked.setOptions({
18192             renderer: new Roo.Markdown.marked.Renderer(),
18193             gfm: true,
18194             tables: true,
18195             breaks: false,
18196             pedantic: false,
18197             sanitize: false,
18198             smartLists: true,
18199             smartypants: false
18200           });
18201     // A FEW HACKS!!?
18202     
18203     text = text.replace(/\\\n/g,' ');
18204     return Roo.Markdown.marked(text);
18205 };
18206 //
18207 // converter
18208 //
18209 // Wraps all "globals" so that the only thing
18210 // exposed is makeHtml().
18211 //
18212 (function() {
18213     
18214      /**
18215          * eval:var:escape
18216          * eval:var:unescape
18217          * eval:var:replace
18218          */
18219       
18220     /**
18221      * Helpers
18222      */
18223     
18224     var escape = function (html, encode) {
18225       return html
18226         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18227         .replace(/</g, '&lt;')
18228         .replace(/>/g, '&gt;')
18229         .replace(/"/g, '&quot;')
18230         .replace(/'/g, '&#39;');
18231     }
18232     
18233     var unescape = function (html) {
18234         // explicitly match decimal, hex, and named HTML entities 
18235       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18236         n = n.toLowerCase();
18237         if (n === 'colon') { return ':'; }
18238         if (n.charAt(0) === '#') {
18239           return n.charAt(1) === 'x'
18240             ? String.fromCharCode(parseInt(n.substring(2), 16))
18241             : String.fromCharCode(+n.substring(1));
18242         }
18243         return '';
18244       });
18245     }
18246     
18247     var replace = function (regex, opt) {
18248       regex = regex.source;
18249       opt = opt || '';
18250       return function self(name, val) {
18251         if (!name) { return new RegExp(regex, opt); }
18252         val = val.source || val;
18253         val = val.replace(/(^|[^\[])\^/g, '$1');
18254         regex = regex.replace(name, val);
18255         return self;
18256       };
18257     }
18258
18259
18260          /**
18261          * eval:var:noop
18262     */
18263     var noop = function () {}
18264     noop.exec = noop;
18265     
18266          /**
18267          * eval:var:merge
18268     */
18269     var merge = function (obj) {
18270       var i = 1
18271         , target
18272         , key;
18273     
18274       for (; i < arguments.length; i++) {
18275         target = arguments[i];
18276         for (key in target) {
18277           if (Object.prototype.hasOwnProperty.call(target, key)) {
18278             obj[key] = target[key];
18279           }
18280         }
18281       }
18282     
18283       return obj;
18284     }
18285     
18286     
18287     /**
18288      * Block-Level Grammar
18289      */
18290     
18291     
18292     
18293     
18294     var block = {
18295       newline: /^\n+/,
18296       code: /^( {4}[^\n]+\n*)+/,
18297       fences: noop,
18298       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18299       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18300       nptable: noop,
18301       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18302       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18303       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18304       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18305       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18306       table: noop,
18307       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18308       text: /^[^\n]+/
18309     };
18310     
18311     block.bullet = /(?:[*+-]|\d+\.)/;
18312     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18313     block.item = replace(block.item, 'gm')
18314       (/bull/g, block.bullet)
18315       ();
18316     
18317     block.list = replace(block.list)
18318       (/bull/g, block.bullet)
18319       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18320       ('def', '\\n+(?=' + block.def.source + ')')
18321       ();
18322     
18323     block.blockquote = replace(block.blockquote)
18324       ('def', block.def)
18325       ();
18326     
18327     block._tag = '(?!(?:'
18328       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18329       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18330       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18331     
18332     block.html = replace(block.html)
18333       ('comment', /<!--[\s\S]*?-->/)
18334       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18335       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18336       (/tag/g, block._tag)
18337       ();
18338     
18339     block.paragraph = replace(block.paragraph)
18340       ('hr', block.hr)
18341       ('heading', block.heading)
18342       ('lheading', block.lheading)
18343       ('blockquote', block.blockquote)
18344       ('tag', '<' + block._tag)
18345       ('def', block.def)
18346       ();
18347     
18348     /**
18349      * Normal Block Grammar
18350      */
18351     
18352     block.normal = merge({}, block);
18353     
18354     /**
18355      * GFM Block Grammar
18356      */
18357     
18358     block.gfm = merge({}, block.normal, {
18359       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18360       paragraph: /^/,
18361       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18362     });
18363     
18364     block.gfm.paragraph = replace(block.paragraph)
18365       ('(?!', '(?!'
18366         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18367         + block.list.source.replace('\\1', '\\3') + '|')
18368       ();
18369     
18370     /**
18371      * GFM + Tables Block Grammar
18372      */
18373     
18374     block.tables = merge({}, block.gfm, {
18375       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18376       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18377     });
18378     
18379     /**
18380      * Block Lexer
18381      */
18382     
18383     var Lexer = function (options) {
18384       this.tokens = [];
18385       this.tokens.links = {};
18386       this.options = options || marked.defaults;
18387       this.rules = block.normal;
18388     
18389       if (this.options.gfm) {
18390         if (this.options.tables) {
18391           this.rules = block.tables;
18392         } else {
18393           this.rules = block.gfm;
18394         }
18395       }
18396     }
18397     
18398     /**
18399      * Expose Block Rules
18400      */
18401     
18402     Lexer.rules = block;
18403     
18404     /**
18405      * Static Lex Method
18406      */
18407     
18408     Lexer.lex = function(src, options) {
18409       var lexer = new Lexer(options);
18410       return lexer.lex(src);
18411     };
18412     
18413     /**
18414      * Preprocessing
18415      */
18416     
18417     Lexer.prototype.lex = function(src) {
18418       src = src
18419         .replace(/\r\n|\r/g, '\n')
18420         .replace(/\t/g, '    ')
18421         .replace(/\u00a0/g, ' ')
18422         .replace(/\u2424/g, '\n');
18423     
18424       return this.token(src, true);
18425     };
18426     
18427     /**
18428      * Lexing
18429      */
18430     
18431     Lexer.prototype.token = function(src, top, bq) {
18432       var src = src.replace(/^ +$/gm, '')
18433         , next
18434         , loose
18435         , cap
18436         , bull
18437         , b
18438         , item
18439         , space
18440         , i
18441         , l;
18442     
18443       while (src) {
18444         // newline
18445         if (cap = this.rules.newline.exec(src)) {
18446           src = src.substring(cap[0].length);
18447           if (cap[0].length > 1) {
18448             this.tokens.push({
18449               type: 'space'
18450             });
18451           }
18452         }
18453     
18454         // code
18455         if (cap = this.rules.code.exec(src)) {
18456           src = src.substring(cap[0].length);
18457           cap = cap[0].replace(/^ {4}/gm, '');
18458           this.tokens.push({
18459             type: 'code',
18460             text: !this.options.pedantic
18461               ? cap.replace(/\n+$/, '')
18462               : cap
18463           });
18464           continue;
18465         }
18466     
18467         // fences (gfm)
18468         if (cap = this.rules.fences.exec(src)) {
18469           src = src.substring(cap[0].length);
18470           this.tokens.push({
18471             type: 'code',
18472             lang: cap[2],
18473             text: cap[3] || ''
18474           });
18475           continue;
18476         }
18477     
18478         // heading
18479         if (cap = this.rules.heading.exec(src)) {
18480           src = src.substring(cap[0].length);
18481           this.tokens.push({
18482             type: 'heading',
18483             depth: cap[1].length,
18484             text: cap[2]
18485           });
18486           continue;
18487         }
18488     
18489         // table no leading pipe (gfm)
18490         if (top && (cap = this.rules.nptable.exec(src))) {
18491           src = src.substring(cap[0].length);
18492     
18493           item = {
18494             type: 'table',
18495             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18496             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18497             cells: cap[3].replace(/\n$/, '').split('\n')
18498           };
18499     
18500           for (i = 0; i < item.align.length; i++) {
18501             if (/^ *-+: *$/.test(item.align[i])) {
18502               item.align[i] = 'right';
18503             } else if (/^ *:-+: *$/.test(item.align[i])) {
18504               item.align[i] = 'center';
18505             } else if (/^ *:-+ *$/.test(item.align[i])) {
18506               item.align[i] = 'left';
18507             } else {
18508               item.align[i] = null;
18509             }
18510           }
18511     
18512           for (i = 0; i < item.cells.length; i++) {
18513             item.cells[i] = item.cells[i].split(/ *\| */);
18514           }
18515     
18516           this.tokens.push(item);
18517     
18518           continue;
18519         }
18520     
18521         // lheading
18522         if (cap = this.rules.lheading.exec(src)) {
18523           src = src.substring(cap[0].length);
18524           this.tokens.push({
18525             type: 'heading',
18526             depth: cap[2] === '=' ? 1 : 2,
18527             text: cap[1]
18528           });
18529           continue;
18530         }
18531     
18532         // hr
18533         if (cap = this.rules.hr.exec(src)) {
18534           src = src.substring(cap[0].length);
18535           this.tokens.push({
18536             type: 'hr'
18537           });
18538           continue;
18539         }
18540     
18541         // blockquote
18542         if (cap = this.rules.blockquote.exec(src)) {
18543           src = src.substring(cap[0].length);
18544     
18545           this.tokens.push({
18546             type: 'blockquote_start'
18547           });
18548     
18549           cap = cap[0].replace(/^ *> ?/gm, '');
18550     
18551           // Pass `top` to keep the current
18552           // "toplevel" state. This is exactly
18553           // how markdown.pl works.
18554           this.token(cap, top, true);
18555     
18556           this.tokens.push({
18557             type: 'blockquote_end'
18558           });
18559     
18560           continue;
18561         }
18562     
18563         // list
18564         if (cap = this.rules.list.exec(src)) {
18565           src = src.substring(cap[0].length);
18566           bull = cap[2];
18567     
18568           this.tokens.push({
18569             type: 'list_start',
18570             ordered: bull.length > 1
18571           });
18572     
18573           // Get each top-level item.
18574           cap = cap[0].match(this.rules.item);
18575     
18576           next = false;
18577           l = cap.length;
18578           i = 0;
18579     
18580           for (; i < l; i++) {
18581             item = cap[i];
18582     
18583             // Remove the list item's bullet
18584             // so it is seen as the next token.
18585             space = item.length;
18586             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18587     
18588             // Outdent whatever the
18589             // list item contains. Hacky.
18590             if (~item.indexOf('\n ')) {
18591               space -= item.length;
18592               item = !this.options.pedantic
18593                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18594                 : item.replace(/^ {1,4}/gm, '');
18595             }
18596     
18597             // Determine whether the next list item belongs here.
18598             // Backpedal if it does not belong in this list.
18599             if (this.options.smartLists && i !== l - 1) {
18600               b = block.bullet.exec(cap[i + 1])[0];
18601               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18602                 src = cap.slice(i + 1).join('\n') + src;
18603                 i = l - 1;
18604               }
18605             }
18606     
18607             // Determine whether item is loose or not.
18608             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18609             // for discount behavior.
18610             loose = next || /\n\n(?!\s*$)/.test(item);
18611             if (i !== l - 1) {
18612               next = item.charAt(item.length - 1) === '\n';
18613               if (!loose) { loose = next; }
18614             }
18615     
18616             this.tokens.push({
18617               type: loose
18618                 ? 'loose_item_start'
18619                 : 'list_item_start'
18620             });
18621     
18622             // Recurse.
18623             this.token(item, false, bq);
18624     
18625             this.tokens.push({
18626               type: 'list_item_end'
18627             });
18628           }
18629     
18630           this.tokens.push({
18631             type: 'list_end'
18632           });
18633     
18634           continue;
18635         }
18636     
18637         // html
18638         if (cap = this.rules.html.exec(src)) {
18639           src = src.substring(cap[0].length);
18640           this.tokens.push({
18641             type: this.options.sanitize
18642               ? 'paragraph'
18643               : 'html',
18644             pre: !this.options.sanitizer
18645               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18646             text: cap[0]
18647           });
18648           continue;
18649         }
18650     
18651         // def
18652         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18653           src = src.substring(cap[0].length);
18654           this.tokens.links[cap[1].toLowerCase()] = {
18655             href: cap[2],
18656             title: cap[3]
18657           };
18658           continue;
18659         }
18660     
18661         // table (gfm)
18662         if (top && (cap = this.rules.table.exec(src))) {
18663           src = src.substring(cap[0].length);
18664     
18665           item = {
18666             type: 'table',
18667             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18668             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18669             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18670           };
18671     
18672           for (i = 0; i < item.align.length; i++) {
18673             if (/^ *-+: *$/.test(item.align[i])) {
18674               item.align[i] = 'right';
18675             } else if (/^ *:-+: *$/.test(item.align[i])) {
18676               item.align[i] = 'center';
18677             } else if (/^ *:-+ *$/.test(item.align[i])) {
18678               item.align[i] = 'left';
18679             } else {
18680               item.align[i] = null;
18681             }
18682           }
18683     
18684           for (i = 0; i < item.cells.length; i++) {
18685             item.cells[i] = item.cells[i]
18686               .replace(/^ *\| *| *\| *$/g, '')
18687               .split(/ *\| */);
18688           }
18689     
18690           this.tokens.push(item);
18691     
18692           continue;
18693         }
18694     
18695         // top-level paragraph
18696         if (top && (cap = this.rules.paragraph.exec(src))) {
18697           src = src.substring(cap[0].length);
18698           this.tokens.push({
18699             type: 'paragraph',
18700             text: cap[1].charAt(cap[1].length - 1) === '\n'
18701               ? cap[1].slice(0, -1)
18702               : cap[1]
18703           });
18704           continue;
18705         }
18706     
18707         // text
18708         if (cap = this.rules.text.exec(src)) {
18709           // Top-level should never reach here.
18710           src = src.substring(cap[0].length);
18711           this.tokens.push({
18712             type: 'text',
18713             text: cap[0]
18714           });
18715           continue;
18716         }
18717     
18718         if (src) {
18719           throw new
18720             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18721         }
18722       }
18723     
18724       return this.tokens;
18725     };
18726     
18727     /**
18728      * Inline-Level Grammar
18729      */
18730     
18731     var inline = {
18732       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18733       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18734       url: noop,
18735       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18736       link: /^!?\[(inside)\]\(href\)/,
18737       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18738       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18739       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18740       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18741       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18742       br: /^ {2,}\n(?!\s*$)/,
18743       del: noop,
18744       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18745     };
18746     
18747     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18748     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18749     
18750     inline.link = replace(inline.link)
18751       ('inside', inline._inside)
18752       ('href', inline._href)
18753       ();
18754     
18755     inline.reflink = replace(inline.reflink)
18756       ('inside', inline._inside)
18757       ();
18758     
18759     /**
18760      * Normal Inline Grammar
18761      */
18762     
18763     inline.normal = merge({}, inline);
18764     
18765     /**
18766      * Pedantic Inline Grammar
18767      */
18768     
18769     inline.pedantic = merge({}, inline.normal, {
18770       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18771       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18772     });
18773     
18774     /**
18775      * GFM Inline Grammar
18776      */
18777     
18778     inline.gfm = merge({}, inline.normal, {
18779       escape: replace(inline.escape)('])', '~|])')(),
18780       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18781       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18782       text: replace(inline.text)
18783         (']|', '~]|')
18784         ('|', '|https?://|')
18785         ()
18786     });
18787     
18788     /**
18789      * GFM + Line Breaks Inline Grammar
18790      */
18791     
18792     inline.breaks = merge({}, inline.gfm, {
18793       br: replace(inline.br)('{2,}', '*')(),
18794       text: replace(inline.gfm.text)('{2,}', '*')()
18795     });
18796     
18797     /**
18798      * Inline Lexer & Compiler
18799      */
18800     
18801     var InlineLexer  = function (links, options) {
18802       this.options = options || marked.defaults;
18803       this.links = links;
18804       this.rules = inline.normal;
18805       this.renderer = this.options.renderer || new Renderer;
18806       this.renderer.options = this.options;
18807     
18808       if (!this.links) {
18809         throw new
18810           Error('Tokens array requires a `links` property.');
18811       }
18812     
18813       if (this.options.gfm) {
18814         if (this.options.breaks) {
18815           this.rules = inline.breaks;
18816         } else {
18817           this.rules = inline.gfm;
18818         }
18819       } else if (this.options.pedantic) {
18820         this.rules = inline.pedantic;
18821       }
18822     }
18823     
18824     /**
18825      * Expose Inline Rules
18826      */
18827     
18828     InlineLexer.rules = inline;
18829     
18830     /**
18831      * Static Lexing/Compiling Method
18832      */
18833     
18834     InlineLexer.output = function(src, links, options) {
18835       var inline = new InlineLexer(links, options);
18836       return inline.output(src);
18837     };
18838     
18839     /**
18840      * Lexing/Compiling
18841      */
18842     
18843     InlineLexer.prototype.output = function(src) {
18844       var out = ''
18845         , link
18846         , text
18847         , href
18848         , cap;
18849     
18850       while (src) {
18851         // escape
18852         if (cap = this.rules.escape.exec(src)) {
18853           src = src.substring(cap[0].length);
18854           out += cap[1];
18855           continue;
18856         }
18857     
18858         // autolink
18859         if (cap = this.rules.autolink.exec(src)) {
18860           src = src.substring(cap[0].length);
18861           if (cap[2] === '@') {
18862             text = cap[1].charAt(6) === ':'
18863               ? this.mangle(cap[1].substring(7))
18864               : this.mangle(cap[1]);
18865             href = this.mangle('mailto:') + text;
18866           } else {
18867             text = escape(cap[1]);
18868             href = text;
18869           }
18870           out += this.renderer.link(href, null, text);
18871           continue;
18872         }
18873     
18874         // url (gfm)
18875         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18876           src = src.substring(cap[0].length);
18877           text = escape(cap[1]);
18878           href = text;
18879           out += this.renderer.link(href, null, text);
18880           continue;
18881         }
18882     
18883         // tag
18884         if (cap = this.rules.tag.exec(src)) {
18885           if (!this.inLink && /^<a /i.test(cap[0])) {
18886             this.inLink = true;
18887           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18888             this.inLink = false;
18889           }
18890           src = src.substring(cap[0].length);
18891           out += this.options.sanitize
18892             ? this.options.sanitizer
18893               ? this.options.sanitizer(cap[0])
18894               : escape(cap[0])
18895             : cap[0];
18896           continue;
18897         }
18898     
18899         // link
18900         if (cap = this.rules.link.exec(src)) {
18901           src = src.substring(cap[0].length);
18902           this.inLink = true;
18903           out += this.outputLink(cap, {
18904             href: cap[2],
18905             title: cap[3]
18906           });
18907           this.inLink = false;
18908           continue;
18909         }
18910     
18911         // reflink, nolink
18912         if ((cap = this.rules.reflink.exec(src))
18913             || (cap = this.rules.nolink.exec(src))) {
18914           src = src.substring(cap[0].length);
18915           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18916           link = this.links[link.toLowerCase()];
18917           if (!link || !link.href) {
18918             out += cap[0].charAt(0);
18919             src = cap[0].substring(1) + src;
18920             continue;
18921           }
18922           this.inLink = true;
18923           out += this.outputLink(cap, link);
18924           this.inLink = false;
18925           continue;
18926         }
18927     
18928         // strong
18929         if (cap = this.rules.strong.exec(src)) {
18930           src = src.substring(cap[0].length);
18931           out += this.renderer.strong(this.output(cap[2] || cap[1]));
18932           continue;
18933         }
18934     
18935         // em
18936         if (cap = this.rules.em.exec(src)) {
18937           src = src.substring(cap[0].length);
18938           out += this.renderer.em(this.output(cap[2] || cap[1]));
18939           continue;
18940         }
18941     
18942         // code
18943         if (cap = this.rules.code.exec(src)) {
18944           src = src.substring(cap[0].length);
18945           out += this.renderer.codespan(escape(cap[2], true));
18946           continue;
18947         }
18948     
18949         // br
18950         if (cap = this.rules.br.exec(src)) {
18951           src = src.substring(cap[0].length);
18952           out += this.renderer.br();
18953           continue;
18954         }
18955     
18956         // del (gfm)
18957         if (cap = this.rules.del.exec(src)) {
18958           src = src.substring(cap[0].length);
18959           out += this.renderer.del(this.output(cap[1]));
18960           continue;
18961         }
18962     
18963         // text
18964         if (cap = this.rules.text.exec(src)) {
18965           src = src.substring(cap[0].length);
18966           out += this.renderer.text(escape(this.smartypants(cap[0])));
18967           continue;
18968         }
18969     
18970         if (src) {
18971           throw new
18972             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18973         }
18974       }
18975     
18976       return out;
18977     };
18978     
18979     /**
18980      * Compile Link
18981      */
18982     
18983     InlineLexer.prototype.outputLink = function(cap, link) {
18984       var href = escape(link.href)
18985         , title = link.title ? escape(link.title) : null;
18986     
18987       return cap[0].charAt(0) !== '!'
18988         ? this.renderer.link(href, title, this.output(cap[1]))
18989         : this.renderer.image(href, title, escape(cap[1]));
18990     };
18991     
18992     /**
18993      * Smartypants Transformations
18994      */
18995     
18996     InlineLexer.prototype.smartypants = function(text) {
18997       if (!this.options.smartypants)  { return text; }
18998       return text
18999         // em-dashes
19000         .replace(/---/g, '\u2014')
19001         // en-dashes
19002         .replace(/--/g, '\u2013')
19003         // opening singles
19004         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19005         // closing singles & apostrophes
19006         .replace(/'/g, '\u2019')
19007         // opening doubles
19008         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19009         // closing doubles
19010         .replace(/"/g, '\u201d')
19011         // ellipses
19012         .replace(/\.{3}/g, '\u2026');
19013     };
19014     
19015     /**
19016      * Mangle Links
19017      */
19018     
19019     InlineLexer.prototype.mangle = function(text) {
19020       if (!this.options.mangle) { return text; }
19021       var out = ''
19022         , l = text.length
19023         , i = 0
19024         , ch;
19025     
19026       for (; i < l; i++) {
19027         ch = text.charCodeAt(i);
19028         if (Math.random() > 0.5) {
19029           ch = 'x' + ch.toString(16);
19030         }
19031         out += '&#' + ch + ';';
19032       }
19033     
19034       return out;
19035     };
19036     
19037     /**
19038      * Renderer
19039      */
19040     
19041      /**
19042          * eval:var:Renderer
19043     */
19044     
19045     var Renderer   = function (options) {
19046       this.options = options || {};
19047     }
19048     
19049     Renderer.prototype.code = function(code, lang, escaped) {
19050       if (this.options.highlight) {
19051         var out = this.options.highlight(code, lang);
19052         if (out != null && out !== code) {
19053           escaped = true;
19054           code = out;
19055         }
19056       } else {
19057             // hack!!! - it's already escapeD?
19058             escaped = true;
19059       }
19060     
19061       if (!lang) {
19062         return '<pre><code>'
19063           + (escaped ? code : escape(code, true))
19064           + '\n</code></pre>';
19065       }
19066     
19067       return '<pre><code class="'
19068         + this.options.langPrefix
19069         + escape(lang, true)
19070         + '">'
19071         + (escaped ? code : escape(code, true))
19072         + '\n</code></pre>\n';
19073     };
19074     
19075     Renderer.prototype.blockquote = function(quote) {
19076       return '<blockquote>\n' + quote + '</blockquote>\n';
19077     };
19078     
19079     Renderer.prototype.html = function(html) {
19080       return html;
19081     };
19082     
19083     Renderer.prototype.heading = function(text, level, raw) {
19084       return '<h'
19085         + level
19086         + ' id="'
19087         + this.options.headerPrefix
19088         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19089         + '">'
19090         + text
19091         + '</h'
19092         + level
19093         + '>\n';
19094     };
19095     
19096     Renderer.prototype.hr = function() {
19097       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19098     };
19099     
19100     Renderer.prototype.list = function(body, ordered) {
19101       var type = ordered ? 'ol' : 'ul';
19102       return '<' + type + '>\n' + body + '</' + type + '>\n';
19103     };
19104     
19105     Renderer.prototype.listitem = function(text) {
19106       return '<li>' + text + '</li>\n';
19107     };
19108     
19109     Renderer.prototype.paragraph = function(text) {
19110       return '<p>' + text + '</p>\n';
19111     };
19112     
19113     Renderer.prototype.table = function(header, body) {
19114       return '<table class="table table-striped">\n'
19115         + '<thead>\n'
19116         + header
19117         + '</thead>\n'
19118         + '<tbody>\n'
19119         + body
19120         + '</tbody>\n'
19121         + '</table>\n';
19122     };
19123     
19124     Renderer.prototype.tablerow = function(content) {
19125       return '<tr>\n' + content + '</tr>\n';
19126     };
19127     
19128     Renderer.prototype.tablecell = function(content, flags) {
19129       var type = flags.header ? 'th' : 'td';
19130       var tag = flags.align
19131         ? '<' + type + ' style="text-align:' + flags.align + '">'
19132         : '<' + type + '>';
19133       return tag + content + '</' + type + '>\n';
19134     };
19135     
19136     // span level renderer
19137     Renderer.prototype.strong = function(text) {
19138       return '<strong>' + text + '</strong>';
19139     };
19140     
19141     Renderer.prototype.em = function(text) {
19142       return '<em>' + text + '</em>';
19143     };
19144     
19145     Renderer.prototype.codespan = function(text) {
19146       return '<code>' + text + '</code>';
19147     };
19148     
19149     Renderer.prototype.br = function() {
19150       return this.options.xhtml ? '<br/>' : '<br>';
19151     };
19152     
19153     Renderer.prototype.del = function(text) {
19154       return '<del>' + text + '</del>';
19155     };
19156     
19157     Renderer.prototype.link = function(href, title, text) {
19158       if (this.options.sanitize) {
19159         try {
19160           var prot = decodeURIComponent(unescape(href))
19161             .replace(/[^\w:]/g, '')
19162             .toLowerCase();
19163         } catch (e) {
19164           return '';
19165         }
19166         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19167           return '';
19168         }
19169       }
19170       var out = '<a href="' + href + '"';
19171       if (title) {
19172         out += ' title="' + title + '"';
19173       }
19174       out += '>' + text + '</a>';
19175       return out;
19176     };
19177     
19178     Renderer.prototype.image = function(href, title, text) {
19179       var out = '<img src="' + href + '" alt="' + text + '"';
19180       if (title) {
19181         out += ' title="' + title + '"';
19182       }
19183       out += this.options.xhtml ? '/>' : '>';
19184       return out;
19185     };
19186     
19187     Renderer.prototype.text = function(text) {
19188       return text;
19189     };
19190     
19191     /**
19192      * Parsing & Compiling
19193      */
19194          /**
19195          * eval:var:Parser
19196     */
19197     
19198     var Parser= function (options) {
19199       this.tokens = [];
19200       this.token = null;
19201       this.options = options || marked.defaults;
19202       this.options.renderer = this.options.renderer || new Renderer;
19203       this.renderer = this.options.renderer;
19204       this.renderer.options = this.options;
19205     }
19206     
19207     /**
19208      * Static Parse Method
19209      */
19210     
19211     Parser.parse = function(src, options, renderer) {
19212       var parser = new Parser(options, renderer);
19213       return parser.parse(src);
19214     };
19215     
19216     /**
19217      * Parse Loop
19218      */
19219     
19220     Parser.prototype.parse = function(src) {
19221       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19222       this.tokens = src.reverse();
19223     
19224       var out = '';
19225       while (this.next()) {
19226         out += this.tok();
19227       }
19228     
19229       return out;
19230     };
19231     
19232     /**
19233      * Next Token
19234      */
19235     
19236     Parser.prototype.next = function() {
19237       return this.token = this.tokens.pop();
19238     };
19239     
19240     /**
19241      * Preview Next Token
19242      */
19243     
19244     Parser.prototype.peek = function() {
19245       return this.tokens[this.tokens.length - 1] || 0;
19246     };
19247     
19248     /**
19249      * Parse Text Tokens
19250      */
19251     
19252     Parser.prototype.parseText = function() {
19253       var body = this.token.text;
19254     
19255       while (this.peek().type === 'text') {
19256         body += '\n' + this.next().text;
19257       }
19258     
19259       return this.inline.output(body);
19260     };
19261     
19262     /**
19263      * Parse Current Token
19264      */
19265     
19266     Parser.prototype.tok = function() {
19267       switch (this.token.type) {
19268         case 'space': {
19269           return '';
19270         }
19271         case 'hr': {
19272           return this.renderer.hr();
19273         }
19274         case 'heading': {
19275           return this.renderer.heading(
19276             this.inline.output(this.token.text),
19277             this.token.depth,
19278             this.token.text);
19279         }
19280         case 'code': {
19281           return this.renderer.code(this.token.text,
19282             this.token.lang,
19283             this.token.escaped);
19284         }
19285         case 'table': {
19286           var header = ''
19287             , body = ''
19288             , i
19289             , row
19290             , cell
19291             , flags
19292             , j;
19293     
19294           // header
19295           cell = '';
19296           for (i = 0; i < this.token.header.length; i++) {
19297             flags = { header: true, align: this.token.align[i] };
19298             cell += this.renderer.tablecell(
19299               this.inline.output(this.token.header[i]),
19300               { header: true, align: this.token.align[i] }
19301             );
19302           }
19303           header += this.renderer.tablerow(cell);
19304     
19305           for (i = 0; i < this.token.cells.length; i++) {
19306             row = this.token.cells[i];
19307     
19308             cell = '';
19309             for (j = 0; j < row.length; j++) {
19310               cell += this.renderer.tablecell(
19311                 this.inline.output(row[j]),
19312                 { header: false, align: this.token.align[j] }
19313               );
19314             }
19315     
19316             body += this.renderer.tablerow(cell);
19317           }
19318           return this.renderer.table(header, body);
19319         }
19320         case 'blockquote_start': {
19321           var body = '';
19322     
19323           while (this.next().type !== 'blockquote_end') {
19324             body += this.tok();
19325           }
19326     
19327           return this.renderer.blockquote(body);
19328         }
19329         case 'list_start': {
19330           var body = ''
19331             , ordered = this.token.ordered;
19332     
19333           while (this.next().type !== 'list_end') {
19334             body += this.tok();
19335           }
19336     
19337           return this.renderer.list(body, ordered);
19338         }
19339         case 'list_item_start': {
19340           var body = '';
19341     
19342           while (this.next().type !== 'list_item_end') {
19343             body += this.token.type === 'text'
19344               ? this.parseText()
19345               : this.tok();
19346           }
19347     
19348           return this.renderer.listitem(body);
19349         }
19350         case 'loose_item_start': {
19351           var body = '';
19352     
19353           while (this.next().type !== 'list_item_end') {
19354             body += this.tok();
19355           }
19356     
19357           return this.renderer.listitem(body);
19358         }
19359         case 'html': {
19360           var html = !this.token.pre && !this.options.pedantic
19361             ? this.inline.output(this.token.text)
19362             : this.token.text;
19363           return this.renderer.html(html);
19364         }
19365         case 'paragraph': {
19366           return this.renderer.paragraph(this.inline.output(this.token.text));
19367         }
19368         case 'text': {
19369           return this.renderer.paragraph(this.parseText());
19370         }
19371       }
19372     };
19373   
19374     
19375     /**
19376      * Marked
19377      */
19378          /**
19379          * eval:var:marked
19380     */
19381     var marked = function (src, opt, callback) {
19382       if (callback || typeof opt === 'function') {
19383         if (!callback) {
19384           callback = opt;
19385           opt = null;
19386         }
19387     
19388         opt = merge({}, marked.defaults, opt || {});
19389     
19390         var highlight = opt.highlight
19391           , tokens
19392           , pending
19393           , i = 0;
19394     
19395         try {
19396           tokens = Lexer.lex(src, opt)
19397         } catch (e) {
19398           return callback(e);
19399         }
19400     
19401         pending = tokens.length;
19402          /**
19403          * eval:var:done
19404     */
19405         var done = function(err) {
19406           if (err) {
19407             opt.highlight = highlight;
19408             return callback(err);
19409           }
19410     
19411           var out;
19412     
19413           try {
19414             out = Parser.parse(tokens, opt);
19415           } catch (e) {
19416             err = e;
19417           }
19418     
19419           opt.highlight = highlight;
19420     
19421           return err
19422             ? callback(err)
19423             : callback(null, out);
19424         };
19425     
19426         if (!highlight || highlight.length < 3) {
19427           return done();
19428         }
19429     
19430         delete opt.highlight;
19431     
19432         if (!pending) { return done(); }
19433     
19434         for (; i < tokens.length; i++) {
19435           (function(token) {
19436             if (token.type !== 'code') {
19437               return --pending || done();
19438             }
19439             return highlight(token.text, token.lang, function(err, code) {
19440               if (err) { return done(err); }
19441               if (code == null || code === token.text) {
19442                 return --pending || done();
19443               }
19444               token.text = code;
19445               token.escaped = true;
19446               --pending || done();
19447             });
19448           })(tokens[i]);
19449         }
19450     
19451         return;
19452       }
19453       try {
19454         if (opt) { opt = merge({}, marked.defaults, opt); }
19455         return Parser.parse(Lexer.lex(src, opt), opt);
19456       } catch (e) {
19457         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19458         if ((opt || marked.defaults).silent) {
19459           return '<p>An error occured:</p><pre>'
19460             + escape(e.message + '', true)
19461             + '</pre>';
19462         }
19463         throw e;
19464       }
19465     }
19466     
19467     /**
19468      * Options
19469      */
19470     
19471     marked.options =
19472     marked.setOptions = function(opt) {
19473       merge(marked.defaults, opt);
19474       return marked;
19475     };
19476     
19477     marked.defaults = {
19478       gfm: true,
19479       tables: true,
19480       breaks: false,
19481       pedantic: false,
19482       sanitize: false,
19483       sanitizer: null,
19484       mangle: true,
19485       smartLists: false,
19486       silent: false,
19487       highlight: null,
19488       langPrefix: 'lang-',
19489       smartypants: false,
19490       headerPrefix: '',
19491       renderer: new Renderer,
19492       xhtml: false
19493     };
19494     
19495     /**
19496      * Expose
19497      */
19498     
19499     marked.Parser = Parser;
19500     marked.parser = Parser.parse;
19501     
19502     marked.Renderer = Renderer;
19503     
19504     marked.Lexer = Lexer;
19505     marked.lexer = Lexer.lex;
19506     
19507     marked.InlineLexer = InlineLexer;
19508     marked.inlineLexer = InlineLexer.output;
19509     
19510     marked.parse = marked;
19511     
19512     Roo.Markdown.marked = marked;
19513
19514 })();/*
19515  * Based on:
19516  * Ext JS Library 1.1.1
19517  * Copyright(c) 2006-2007, Ext JS, LLC.
19518  *
19519  * Originally Released Under LGPL - original licence link has changed is not relivant.
19520  *
19521  * Fork - LGPL
19522  * <script type="text/javascript">
19523  */
19524
19525
19526
19527 /*
19528  * These classes are derivatives of the similarly named classes in the YUI Library.
19529  * The original license:
19530  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19531  * Code licensed under the BSD License:
19532  * http://developer.yahoo.net/yui/license.txt
19533  */
19534
19535 (function() {
19536
19537 var Event=Roo.EventManager;
19538 var Dom=Roo.lib.Dom;
19539
19540 /**
19541  * @class Roo.dd.DragDrop
19542  * @extends Roo.util.Observable
19543  * Defines the interface and base operation of items that that can be
19544  * dragged or can be drop targets.  It was designed to be extended, overriding
19545  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19546  * Up to three html elements can be associated with a DragDrop instance:
19547  * <ul>
19548  * <li>linked element: the element that is passed into the constructor.
19549  * This is the element which defines the boundaries for interaction with
19550  * other DragDrop objects.</li>
19551  * <li>handle element(s): The drag operation only occurs if the element that
19552  * was clicked matches a handle element.  By default this is the linked
19553  * element, but there are times that you will want only a portion of the
19554  * linked element to initiate the drag operation, and the setHandleElId()
19555  * method provides a way to define this.</li>
19556  * <li>drag element: this represents the element that would be moved along
19557  * with the cursor during a drag operation.  By default, this is the linked
19558  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19559  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19560  * </li>
19561  * </ul>
19562  * This class should not be instantiated until the onload event to ensure that
19563  * the associated elements are available.
19564  * The following would define a DragDrop obj that would interact with any
19565  * other DragDrop obj in the "group1" group:
19566  * <pre>
19567  *  dd = new Roo.dd.DragDrop("div1", "group1");
19568  * </pre>
19569  * Since none of the event handlers have been implemented, nothing would
19570  * actually happen if you were to run the code above.  Normally you would
19571  * override this class or one of the default implementations, but you can
19572  * also override the methods you want on an instance of the class...
19573  * <pre>
19574  *  dd.onDragDrop = function(e, id) {
19575  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19576  *  }
19577  * </pre>
19578  * @constructor
19579  * @param {String} id of the element that is linked to this instance
19580  * @param {String} sGroup the group of related DragDrop objects
19581  * @param {object} config an object containing configurable attributes
19582  *                Valid properties for DragDrop:
19583  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19584  */
19585 Roo.dd.DragDrop = function(id, sGroup, config) {
19586     if (id) {
19587         this.init(id, sGroup, config);
19588     }
19589     
19590 };
19591
19592 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19593
19594     /**
19595      * The id of the element associated with this object.  This is what we
19596      * refer to as the "linked element" because the size and position of
19597      * this element is used to determine when the drag and drop objects have
19598      * interacted.
19599      * @property id
19600      * @type String
19601      */
19602     id: null,
19603
19604     /**
19605      * Configuration attributes passed into the constructor
19606      * @property config
19607      * @type object
19608      */
19609     config: null,
19610
19611     /**
19612      * The id of the element that will be dragged.  By default this is same
19613      * as the linked element , but could be changed to another element. Ex:
19614      * Roo.dd.DDProxy
19615      * @property dragElId
19616      * @type String
19617      * @private
19618      */
19619     dragElId: null,
19620
19621     /**
19622      * the id of the element that initiates the drag operation.  By default
19623      * this is the linked element, but could be changed to be a child of this
19624      * element.  This lets us do things like only starting the drag when the
19625      * header element within the linked html element is clicked.
19626      * @property handleElId
19627      * @type String
19628      * @private
19629      */
19630     handleElId: null,
19631
19632     /**
19633      * An associative array of HTML tags that will be ignored if clicked.
19634      * @property invalidHandleTypes
19635      * @type {string: string}
19636      */
19637     invalidHandleTypes: null,
19638
19639     /**
19640      * An associative array of ids for elements that will be ignored if clicked
19641      * @property invalidHandleIds
19642      * @type {string: string}
19643      */
19644     invalidHandleIds: null,
19645
19646     /**
19647      * An indexted array of css class names for elements that will be ignored
19648      * if clicked.
19649      * @property invalidHandleClasses
19650      * @type string[]
19651      */
19652     invalidHandleClasses: null,
19653
19654     /**
19655      * The linked element's absolute X position at the time the drag was
19656      * started
19657      * @property startPageX
19658      * @type int
19659      * @private
19660      */
19661     startPageX: 0,
19662
19663     /**
19664      * The linked element's absolute X position at the time the drag was
19665      * started
19666      * @property startPageY
19667      * @type int
19668      * @private
19669      */
19670     startPageY: 0,
19671
19672     /**
19673      * The group defines a logical collection of DragDrop objects that are
19674      * related.  Instances only get events when interacting with other
19675      * DragDrop object in the same group.  This lets us define multiple
19676      * groups using a single DragDrop subclass if we want.
19677      * @property groups
19678      * @type {string: string}
19679      */
19680     groups: null,
19681
19682     /**
19683      * Individual drag/drop instances can be locked.  This will prevent
19684      * onmousedown start drag.
19685      * @property locked
19686      * @type boolean
19687      * @private
19688      */
19689     locked: false,
19690
19691     /**
19692      * Lock this instance
19693      * @method lock
19694      */
19695     lock: function() { this.locked = true; },
19696
19697     /**
19698      * Unlock this instace
19699      * @method unlock
19700      */
19701     unlock: function() { this.locked = false; },
19702
19703     /**
19704      * By default, all insances can be a drop target.  This can be disabled by
19705      * setting isTarget to false.
19706      * @method isTarget
19707      * @type boolean
19708      */
19709     isTarget: true,
19710
19711     /**
19712      * The padding configured for this drag and drop object for calculating
19713      * the drop zone intersection with this object.
19714      * @method padding
19715      * @type int[]
19716      */
19717     padding: null,
19718
19719     /**
19720      * Cached reference to the linked element
19721      * @property _domRef
19722      * @private
19723      */
19724     _domRef: null,
19725
19726     /**
19727      * Internal typeof flag
19728      * @property __ygDragDrop
19729      * @private
19730      */
19731     __ygDragDrop: true,
19732
19733     /**
19734      * Set to true when horizontal contraints are applied
19735      * @property constrainX
19736      * @type boolean
19737      * @private
19738      */
19739     constrainX: false,
19740
19741     /**
19742      * Set to true when vertical contraints are applied
19743      * @property constrainY
19744      * @type boolean
19745      * @private
19746      */
19747     constrainY: false,
19748
19749     /**
19750      * The left constraint
19751      * @property minX
19752      * @type int
19753      * @private
19754      */
19755     minX: 0,
19756
19757     /**
19758      * The right constraint
19759      * @property maxX
19760      * @type int
19761      * @private
19762      */
19763     maxX: 0,
19764
19765     /**
19766      * The up constraint
19767      * @property minY
19768      * @type int
19769      * @type int
19770      * @private
19771      */
19772     minY: 0,
19773
19774     /**
19775      * The down constraint
19776      * @property maxY
19777      * @type int
19778      * @private
19779      */
19780     maxY: 0,
19781
19782     /**
19783      * Maintain offsets when we resetconstraints.  Set to true when you want
19784      * the position of the element relative to its parent to stay the same
19785      * when the page changes
19786      *
19787      * @property maintainOffset
19788      * @type boolean
19789      */
19790     maintainOffset: false,
19791
19792     /**
19793      * Array of pixel locations the element will snap to if we specified a
19794      * horizontal graduation/interval.  This array is generated automatically
19795      * when you define a tick interval.
19796      * @property xTicks
19797      * @type int[]
19798      */
19799     xTicks: null,
19800
19801     /**
19802      * Array of pixel locations the element will snap to if we specified a
19803      * vertical graduation/interval.  This array is generated automatically
19804      * when you define a tick interval.
19805      * @property yTicks
19806      * @type int[]
19807      */
19808     yTicks: null,
19809
19810     /**
19811      * By default the drag and drop instance will only respond to the primary
19812      * button click (left button for a right-handed mouse).  Set to true to
19813      * allow drag and drop to start with any mouse click that is propogated
19814      * by the browser
19815      * @property primaryButtonOnly
19816      * @type boolean
19817      */
19818     primaryButtonOnly: true,
19819
19820     /**
19821      * The availabe property is false until the linked dom element is accessible.
19822      * @property available
19823      * @type boolean
19824      */
19825     available: false,
19826
19827     /**
19828      * By default, drags can only be initiated if the mousedown occurs in the
19829      * region the linked element is.  This is done in part to work around a
19830      * bug in some browsers that mis-report the mousedown if the previous
19831      * mouseup happened outside of the window.  This property is set to true
19832      * if outer handles are defined.
19833      *
19834      * @property hasOuterHandles
19835      * @type boolean
19836      * @default false
19837      */
19838     hasOuterHandles: false,
19839
19840     /**
19841      * Code that executes immediately before the startDrag event
19842      * @method b4StartDrag
19843      * @private
19844      */
19845     b4StartDrag: function(x, y) { },
19846
19847     /**
19848      * Abstract method called after a drag/drop object is clicked
19849      * and the drag or mousedown time thresholds have beeen met.
19850      * @method startDrag
19851      * @param {int} X click location
19852      * @param {int} Y click location
19853      */
19854     startDrag: function(x, y) { /* override this */ },
19855
19856     /**
19857      * Code that executes immediately before the onDrag event
19858      * @method b4Drag
19859      * @private
19860      */
19861     b4Drag: function(e) { },
19862
19863     /**
19864      * Abstract method called during the onMouseMove event while dragging an
19865      * object.
19866      * @method onDrag
19867      * @param {Event} e the mousemove event
19868      */
19869     onDrag: function(e) { /* override this */ },
19870
19871     /**
19872      * Abstract method called when this element fist begins hovering over
19873      * another DragDrop obj
19874      * @method onDragEnter
19875      * @param {Event} e the mousemove event
19876      * @param {String|DragDrop[]} id In POINT mode, the element
19877      * id this is hovering over.  In INTERSECT mode, an array of one or more
19878      * dragdrop items being hovered over.
19879      */
19880     onDragEnter: function(e, id) { /* override this */ },
19881
19882     /**
19883      * Code that executes immediately before the onDragOver event
19884      * @method b4DragOver
19885      * @private
19886      */
19887     b4DragOver: function(e) { },
19888
19889     /**
19890      * Abstract method called when this element is hovering over another
19891      * DragDrop obj
19892      * @method onDragOver
19893      * @param {Event} e the mousemove event
19894      * @param {String|DragDrop[]} id In POINT mode, the element
19895      * id this is hovering over.  In INTERSECT mode, an array of dd items
19896      * being hovered over.
19897      */
19898     onDragOver: function(e, id) { /* override this */ },
19899
19900     /**
19901      * Code that executes immediately before the onDragOut event
19902      * @method b4DragOut
19903      * @private
19904      */
19905     b4DragOut: function(e) { },
19906
19907     /**
19908      * Abstract method called when we are no longer hovering over an element
19909      * @method onDragOut
19910      * @param {Event} e the mousemove event
19911      * @param {String|DragDrop[]} id In POINT mode, the element
19912      * id this was hovering over.  In INTERSECT mode, an array of dd items
19913      * that the mouse is no longer over.
19914      */
19915     onDragOut: function(e, id) { /* override this */ },
19916
19917     /**
19918      * Code that executes immediately before the onDragDrop event
19919      * @method b4DragDrop
19920      * @private
19921      */
19922     b4DragDrop: function(e) { },
19923
19924     /**
19925      * Abstract method called when this item is dropped on another DragDrop
19926      * obj
19927      * @method onDragDrop
19928      * @param {Event} e the mouseup event
19929      * @param {String|DragDrop[]} id In POINT mode, the element
19930      * id this was dropped on.  In INTERSECT mode, an array of dd items this
19931      * was dropped on.
19932      */
19933     onDragDrop: function(e, id) { /* override this */ },
19934
19935     /**
19936      * Abstract method called when this item is dropped on an area with no
19937      * drop target
19938      * @method onInvalidDrop
19939      * @param {Event} e the mouseup event
19940      */
19941     onInvalidDrop: function(e) { /* override this */ },
19942
19943     /**
19944      * Code that executes immediately before the endDrag event
19945      * @method b4EndDrag
19946      * @private
19947      */
19948     b4EndDrag: function(e) { },
19949
19950     /**
19951      * Fired when we are done dragging the object
19952      * @method endDrag
19953      * @param {Event} e the mouseup event
19954      */
19955     endDrag: function(e) { /* override this */ },
19956
19957     /**
19958      * Code executed immediately before the onMouseDown event
19959      * @method b4MouseDown
19960      * @param {Event} e the mousedown event
19961      * @private
19962      */
19963     b4MouseDown: function(e) {  },
19964
19965     /**
19966      * Event handler that fires when a drag/drop obj gets a mousedown
19967      * @method onMouseDown
19968      * @param {Event} e the mousedown event
19969      */
19970     onMouseDown: function(e) { /* override this */ },
19971
19972     /**
19973      * Event handler that fires when a drag/drop obj gets a mouseup
19974      * @method onMouseUp
19975      * @param {Event} e the mouseup event
19976      */
19977     onMouseUp: function(e) { /* override this */ },
19978
19979     /**
19980      * Override the onAvailable method to do what is needed after the initial
19981      * position was determined.
19982      * @method onAvailable
19983      */
19984     onAvailable: function () {
19985     },
19986
19987     /*
19988      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
19989      * @type Object
19990      */
19991     defaultPadding : {left:0, right:0, top:0, bottom:0},
19992
19993     /*
19994      * Initializes the drag drop object's constraints to restrict movement to a certain element.
19995  *
19996  * Usage:
19997  <pre><code>
19998  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
19999                 { dragElId: "existingProxyDiv" });
20000  dd.startDrag = function(){
20001      this.constrainTo("parent-id");
20002  };
20003  </code></pre>
20004  * Or you can initalize it using the {@link Roo.Element} object:
20005  <pre><code>
20006  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20007      startDrag : function(){
20008          this.constrainTo("parent-id");
20009      }
20010  });
20011  </code></pre>
20012      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20013      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20014      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20015      * an object containing the sides to pad. For example: {right:10, bottom:10}
20016      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20017      */
20018     constrainTo : function(constrainTo, pad, inContent){
20019         if(typeof pad == "number"){
20020             pad = {left: pad, right:pad, top:pad, bottom:pad};
20021         }
20022         pad = pad || this.defaultPadding;
20023         var b = Roo.get(this.getEl()).getBox();
20024         var ce = Roo.get(constrainTo);
20025         var s = ce.getScroll();
20026         var c, cd = ce.dom;
20027         if(cd == document.body){
20028             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20029         }else{
20030             xy = ce.getXY();
20031             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20032         }
20033
20034
20035         var topSpace = b.y - c.y;
20036         var leftSpace = b.x - c.x;
20037
20038         this.resetConstraints();
20039         this.setXConstraint(leftSpace - (pad.left||0), // left
20040                 c.width - leftSpace - b.width - (pad.right||0) //right
20041         );
20042         this.setYConstraint(topSpace - (pad.top||0), //top
20043                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20044         );
20045     },
20046
20047     /**
20048      * Returns a reference to the linked element
20049      * @method getEl
20050      * @return {HTMLElement} the html element
20051      */
20052     getEl: function() {
20053         if (!this._domRef) {
20054             this._domRef = Roo.getDom(this.id);
20055         }
20056
20057         return this._domRef;
20058     },
20059
20060     /**
20061      * Returns a reference to the actual element to drag.  By default this is
20062      * the same as the html element, but it can be assigned to another
20063      * element. An example of this can be found in Roo.dd.DDProxy
20064      * @method getDragEl
20065      * @return {HTMLElement} the html element
20066      */
20067     getDragEl: function() {
20068         return Roo.getDom(this.dragElId);
20069     },
20070
20071     /**
20072      * Sets up the DragDrop object.  Must be called in the constructor of any
20073      * Roo.dd.DragDrop subclass
20074      * @method init
20075      * @param id the id of the linked element
20076      * @param {String} sGroup the group of related items
20077      * @param {object} config configuration attributes
20078      */
20079     init: function(id, sGroup, config) {
20080         this.initTarget(id, sGroup, config);
20081         if (!Roo.isTouch) {
20082             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20083         }
20084         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20085         // Event.on(this.id, "selectstart", Event.preventDefault);
20086     },
20087
20088     /**
20089      * Initializes Targeting functionality only... the object does not
20090      * get a mousedown handler.
20091      * @method initTarget
20092      * @param id the id of the linked element
20093      * @param {String} sGroup the group of related items
20094      * @param {object} config configuration attributes
20095      */
20096     initTarget: function(id, sGroup, config) {
20097
20098         // configuration attributes
20099         this.config = config || {};
20100
20101         // create a local reference to the drag and drop manager
20102         this.DDM = Roo.dd.DDM;
20103         // initialize the groups array
20104         this.groups = {};
20105
20106         // assume that we have an element reference instead of an id if the
20107         // parameter is not a string
20108         if (typeof id !== "string") {
20109             id = Roo.id(id);
20110         }
20111
20112         // set the id
20113         this.id = id;
20114
20115         // add to an interaction group
20116         this.addToGroup((sGroup) ? sGroup : "default");
20117
20118         // We don't want to register this as the handle with the manager
20119         // so we just set the id rather than calling the setter.
20120         this.handleElId = id;
20121
20122         // the linked element is the element that gets dragged by default
20123         this.setDragElId(id);
20124
20125         // by default, clicked anchors will not start drag operations.
20126         this.invalidHandleTypes = { A: "A" };
20127         this.invalidHandleIds = {};
20128         this.invalidHandleClasses = [];
20129
20130         this.applyConfig();
20131
20132         this.handleOnAvailable();
20133     },
20134
20135     /**
20136      * Applies the configuration parameters that were passed into the constructor.
20137      * This is supposed to happen at each level through the inheritance chain.  So
20138      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20139      * DragDrop in order to get all of the parameters that are available in
20140      * each object.
20141      * @method applyConfig
20142      */
20143     applyConfig: function() {
20144
20145         // configurable properties:
20146         //    padding, isTarget, maintainOffset, primaryButtonOnly
20147         this.padding           = this.config.padding || [0, 0, 0, 0];
20148         this.isTarget          = (this.config.isTarget !== false);
20149         this.maintainOffset    = (this.config.maintainOffset);
20150         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20151
20152     },
20153
20154     /**
20155      * Executed when the linked element is available
20156      * @method handleOnAvailable
20157      * @private
20158      */
20159     handleOnAvailable: function() {
20160         this.available = true;
20161         this.resetConstraints();
20162         this.onAvailable();
20163     },
20164
20165      /**
20166      * Configures the padding for the target zone in px.  Effectively expands
20167      * (or reduces) the virtual object size for targeting calculations.
20168      * Supports css-style shorthand; if only one parameter is passed, all sides
20169      * will have that padding, and if only two are passed, the top and bottom
20170      * will have the first param, the left and right the second.
20171      * @method setPadding
20172      * @param {int} iTop    Top pad
20173      * @param {int} iRight  Right pad
20174      * @param {int} iBot    Bot pad
20175      * @param {int} iLeft   Left pad
20176      */
20177     setPadding: function(iTop, iRight, iBot, iLeft) {
20178         // this.padding = [iLeft, iRight, iTop, iBot];
20179         if (!iRight && 0 !== iRight) {
20180             this.padding = [iTop, iTop, iTop, iTop];
20181         } else if (!iBot && 0 !== iBot) {
20182             this.padding = [iTop, iRight, iTop, iRight];
20183         } else {
20184             this.padding = [iTop, iRight, iBot, iLeft];
20185         }
20186     },
20187
20188     /**
20189      * Stores the initial placement of the linked element.
20190      * @method setInitialPosition
20191      * @param {int} diffX   the X offset, default 0
20192      * @param {int} diffY   the Y offset, default 0
20193      */
20194     setInitPosition: function(diffX, diffY) {
20195         var el = this.getEl();
20196
20197         if (!this.DDM.verifyEl(el)) {
20198             return;
20199         }
20200
20201         var dx = diffX || 0;
20202         var dy = diffY || 0;
20203
20204         var p = Dom.getXY( el );
20205
20206         this.initPageX = p[0] - dx;
20207         this.initPageY = p[1] - dy;
20208
20209         this.lastPageX = p[0];
20210         this.lastPageY = p[1];
20211
20212
20213         this.setStartPosition(p);
20214     },
20215
20216     /**
20217      * Sets the start position of the element.  This is set when the obj
20218      * is initialized, the reset when a drag is started.
20219      * @method setStartPosition
20220      * @param pos current position (from previous lookup)
20221      * @private
20222      */
20223     setStartPosition: function(pos) {
20224         var p = pos || Dom.getXY( this.getEl() );
20225         this.deltaSetXY = null;
20226
20227         this.startPageX = p[0];
20228         this.startPageY = p[1];
20229     },
20230
20231     /**
20232      * Add this instance to a group of related drag/drop objects.  All
20233      * instances belong to at least one group, and can belong to as many
20234      * groups as needed.
20235      * @method addToGroup
20236      * @param sGroup {string} the name of the group
20237      */
20238     addToGroup: function(sGroup) {
20239         this.groups[sGroup] = true;
20240         this.DDM.regDragDrop(this, sGroup);
20241     },
20242
20243     /**
20244      * Remove's this instance from the supplied interaction group
20245      * @method removeFromGroup
20246      * @param {string}  sGroup  The group to drop
20247      */
20248     removeFromGroup: function(sGroup) {
20249         if (this.groups[sGroup]) {
20250             delete this.groups[sGroup];
20251         }
20252
20253         this.DDM.removeDDFromGroup(this, sGroup);
20254     },
20255
20256     /**
20257      * Allows you to specify that an element other than the linked element
20258      * will be moved with the cursor during a drag
20259      * @method setDragElId
20260      * @param id {string} the id of the element that will be used to initiate the drag
20261      */
20262     setDragElId: function(id) {
20263         this.dragElId = id;
20264     },
20265
20266     /**
20267      * Allows you to specify a child of the linked element that should be
20268      * used to initiate the drag operation.  An example of this would be if
20269      * you have a content div with text and links.  Clicking anywhere in the
20270      * content area would normally start the drag operation.  Use this method
20271      * to specify that an element inside of the content div is the element
20272      * that starts the drag operation.
20273      * @method setHandleElId
20274      * @param id {string} the id of the element that will be used to
20275      * initiate the drag.
20276      */
20277     setHandleElId: function(id) {
20278         if (typeof id !== "string") {
20279             id = Roo.id(id);
20280         }
20281         this.handleElId = id;
20282         this.DDM.regHandle(this.id, id);
20283     },
20284
20285     /**
20286      * Allows you to set an element outside of the linked element as a drag
20287      * handle
20288      * @method setOuterHandleElId
20289      * @param id the id of the element that will be used to initiate the drag
20290      */
20291     setOuterHandleElId: function(id) {
20292         if (typeof id !== "string") {
20293             id = Roo.id(id);
20294         }
20295         Event.on(id, "mousedown",
20296                 this.handleMouseDown, this);
20297         this.setHandleElId(id);
20298
20299         this.hasOuterHandles = true;
20300     },
20301
20302     /**
20303      * Remove all drag and drop hooks for this element
20304      * @method unreg
20305      */
20306     unreg: function() {
20307         Event.un(this.id, "mousedown",
20308                 this.handleMouseDown);
20309         Event.un(this.id, "touchstart",
20310                 this.handleMouseDown);
20311         this._domRef = null;
20312         this.DDM._remove(this);
20313     },
20314
20315     destroy : function(){
20316         this.unreg();
20317     },
20318
20319     /**
20320      * Returns true if this instance is locked, or the drag drop mgr is locked
20321      * (meaning that all drag/drop is disabled on the page.)
20322      * @method isLocked
20323      * @return {boolean} true if this obj or all drag/drop is locked, else
20324      * false
20325      */
20326     isLocked: function() {
20327         return (this.DDM.isLocked() || this.locked);
20328     },
20329
20330     /**
20331      * Fired when this object is clicked
20332      * @method handleMouseDown
20333      * @param {Event} e
20334      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20335      * @private
20336      */
20337     handleMouseDown: function(e, oDD){
20338      
20339         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20340             //Roo.log('not touch/ button !=0');
20341             return;
20342         }
20343         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20344             return; // double touch..
20345         }
20346         
20347
20348         if (this.isLocked()) {
20349             //Roo.log('locked');
20350             return;
20351         }
20352
20353         this.DDM.refreshCache(this.groups);
20354 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20355         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20356         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20357             //Roo.log('no outer handes or not over target');
20358                 // do nothing.
20359         } else {
20360 //            Roo.log('check validator');
20361             if (this.clickValidator(e)) {
20362 //                Roo.log('validate success');
20363                 // set the initial element position
20364                 this.setStartPosition();
20365
20366
20367                 this.b4MouseDown(e);
20368                 this.onMouseDown(e);
20369
20370                 this.DDM.handleMouseDown(e, this);
20371
20372                 this.DDM.stopEvent(e);
20373             } else {
20374
20375
20376             }
20377         }
20378     },
20379
20380     clickValidator: function(e) {
20381         var target = e.getTarget();
20382         return ( this.isValidHandleChild(target) &&
20383                     (this.id == this.handleElId ||
20384                         this.DDM.handleWasClicked(target, this.id)) );
20385     },
20386
20387     /**
20388      * Allows you to specify a tag name that should not start a drag operation
20389      * when clicked.  This is designed to facilitate embedding links within a
20390      * drag handle that do something other than start the drag.
20391      * @method addInvalidHandleType
20392      * @param {string} tagName the type of element to exclude
20393      */
20394     addInvalidHandleType: function(tagName) {
20395         var type = tagName.toUpperCase();
20396         this.invalidHandleTypes[type] = type;
20397     },
20398
20399     /**
20400      * Lets you to specify an element id for a child of a drag handle
20401      * that should not initiate a drag
20402      * @method addInvalidHandleId
20403      * @param {string} id the element id of the element you wish to ignore
20404      */
20405     addInvalidHandleId: function(id) {
20406         if (typeof id !== "string") {
20407             id = Roo.id(id);
20408         }
20409         this.invalidHandleIds[id] = id;
20410     },
20411
20412     /**
20413      * Lets you specify a css class of elements that will not initiate a drag
20414      * @method addInvalidHandleClass
20415      * @param {string} cssClass the class of the elements you wish to ignore
20416      */
20417     addInvalidHandleClass: function(cssClass) {
20418         this.invalidHandleClasses.push(cssClass);
20419     },
20420
20421     /**
20422      * Unsets an excluded tag name set by addInvalidHandleType
20423      * @method removeInvalidHandleType
20424      * @param {string} tagName the type of element to unexclude
20425      */
20426     removeInvalidHandleType: function(tagName) {
20427         var type = tagName.toUpperCase();
20428         // this.invalidHandleTypes[type] = null;
20429         delete this.invalidHandleTypes[type];
20430     },
20431
20432     /**
20433      * Unsets an invalid handle id
20434      * @method removeInvalidHandleId
20435      * @param {string} id the id of the element to re-enable
20436      */
20437     removeInvalidHandleId: function(id) {
20438         if (typeof id !== "string") {
20439             id = Roo.id(id);
20440         }
20441         delete this.invalidHandleIds[id];
20442     },
20443
20444     /**
20445      * Unsets an invalid css class
20446      * @method removeInvalidHandleClass
20447      * @param {string} cssClass the class of the element(s) you wish to
20448      * re-enable
20449      */
20450     removeInvalidHandleClass: function(cssClass) {
20451         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20452             if (this.invalidHandleClasses[i] == cssClass) {
20453                 delete this.invalidHandleClasses[i];
20454             }
20455         }
20456     },
20457
20458     /**
20459      * Checks the tag exclusion list to see if this click should be ignored
20460      * @method isValidHandleChild
20461      * @param {HTMLElement} node the HTMLElement to evaluate
20462      * @return {boolean} true if this is a valid tag type, false if not
20463      */
20464     isValidHandleChild: function(node) {
20465
20466         var valid = true;
20467         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20468         var nodeName;
20469         try {
20470             nodeName = node.nodeName.toUpperCase();
20471         } catch(e) {
20472             nodeName = node.nodeName;
20473         }
20474         valid = valid && !this.invalidHandleTypes[nodeName];
20475         valid = valid && !this.invalidHandleIds[node.id];
20476
20477         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20478             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20479         }
20480
20481
20482         return valid;
20483
20484     },
20485
20486     /**
20487      * Create the array of horizontal tick marks if an interval was specified
20488      * in setXConstraint().
20489      * @method setXTicks
20490      * @private
20491      */
20492     setXTicks: function(iStartX, iTickSize) {
20493         this.xTicks = [];
20494         this.xTickSize = iTickSize;
20495
20496         var tickMap = {};
20497
20498         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20499             if (!tickMap[i]) {
20500                 this.xTicks[this.xTicks.length] = i;
20501                 tickMap[i] = true;
20502             }
20503         }
20504
20505         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20506             if (!tickMap[i]) {
20507                 this.xTicks[this.xTicks.length] = i;
20508                 tickMap[i] = true;
20509             }
20510         }
20511
20512         this.xTicks.sort(this.DDM.numericSort) ;
20513     },
20514
20515     /**
20516      * Create the array of vertical tick marks if an interval was specified in
20517      * setYConstraint().
20518      * @method setYTicks
20519      * @private
20520      */
20521     setYTicks: function(iStartY, iTickSize) {
20522         this.yTicks = [];
20523         this.yTickSize = iTickSize;
20524
20525         var tickMap = {};
20526
20527         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20528             if (!tickMap[i]) {
20529                 this.yTicks[this.yTicks.length] = i;
20530                 tickMap[i] = true;
20531             }
20532         }
20533
20534         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20535             if (!tickMap[i]) {
20536                 this.yTicks[this.yTicks.length] = i;
20537                 tickMap[i] = true;
20538             }
20539         }
20540
20541         this.yTicks.sort(this.DDM.numericSort) ;
20542     },
20543
20544     /**
20545      * By default, the element can be dragged any place on the screen.  Use
20546      * this method to limit the horizontal travel of the element.  Pass in
20547      * 0,0 for the parameters if you want to lock the drag to the y axis.
20548      * @method setXConstraint
20549      * @param {int} iLeft the number of pixels the element can move to the left
20550      * @param {int} iRight the number of pixels the element can move to the
20551      * right
20552      * @param {int} iTickSize optional parameter for specifying that the
20553      * element
20554      * should move iTickSize pixels at a time.
20555      */
20556     setXConstraint: function(iLeft, iRight, iTickSize) {
20557         this.leftConstraint = iLeft;
20558         this.rightConstraint = iRight;
20559
20560         this.minX = this.initPageX - iLeft;
20561         this.maxX = this.initPageX + iRight;
20562         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20563
20564         this.constrainX = true;
20565     },
20566
20567     /**
20568      * Clears any constraints applied to this instance.  Also clears ticks
20569      * since they can't exist independent of a constraint at this time.
20570      * @method clearConstraints
20571      */
20572     clearConstraints: function() {
20573         this.constrainX = false;
20574         this.constrainY = false;
20575         this.clearTicks();
20576     },
20577
20578     /**
20579      * Clears any tick interval defined for this instance
20580      * @method clearTicks
20581      */
20582     clearTicks: function() {
20583         this.xTicks = null;
20584         this.yTicks = null;
20585         this.xTickSize = 0;
20586         this.yTickSize = 0;
20587     },
20588
20589     /**
20590      * By default, the element can be dragged any place on the screen.  Set
20591      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20592      * parameters if you want to lock the drag to the x axis.
20593      * @method setYConstraint
20594      * @param {int} iUp the number of pixels the element can move up
20595      * @param {int} iDown the number of pixels the element can move down
20596      * @param {int} iTickSize optional parameter for specifying that the
20597      * element should move iTickSize pixels at a time.
20598      */
20599     setYConstraint: function(iUp, iDown, iTickSize) {
20600         this.topConstraint = iUp;
20601         this.bottomConstraint = iDown;
20602
20603         this.minY = this.initPageY - iUp;
20604         this.maxY = this.initPageY + iDown;
20605         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20606
20607         this.constrainY = true;
20608
20609     },
20610
20611     /**
20612      * resetConstraints must be called if you manually reposition a dd element.
20613      * @method resetConstraints
20614      * @param {boolean} maintainOffset
20615      */
20616     resetConstraints: function() {
20617
20618
20619         // Maintain offsets if necessary
20620         if (this.initPageX || this.initPageX === 0) {
20621             // figure out how much this thing has moved
20622             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20623             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20624
20625             this.setInitPosition(dx, dy);
20626
20627         // This is the first time we have detected the element's position
20628         } else {
20629             this.setInitPosition();
20630         }
20631
20632         if (this.constrainX) {
20633             this.setXConstraint( this.leftConstraint,
20634                                  this.rightConstraint,
20635                                  this.xTickSize        );
20636         }
20637
20638         if (this.constrainY) {
20639             this.setYConstraint( this.topConstraint,
20640                                  this.bottomConstraint,
20641                                  this.yTickSize         );
20642         }
20643     },
20644
20645     /**
20646      * Normally the drag element is moved pixel by pixel, but we can specify
20647      * that it move a number of pixels at a time.  This method resolves the
20648      * location when we have it set up like this.
20649      * @method getTick
20650      * @param {int} val where we want to place the object
20651      * @param {int[]} tickArray sorted array of valid points
20652      * @return {int} the closest tick
20653      * @private
20654      */
20655     getTick: function(val, tickArray) {
20656
20657         if (!tickArray) {
20658             // If tick interval is not defined, it is effectively 1 pixel,
20659             // so we return the value passed to us.
20660             return val;
20661         } else if (tickArray[0] >= val) {
20662             // The value is lower than the first tick, so we return the first
20663             // tick.
20664             return tickArray[0];
20665         } else {
20666             for (var i=0, len=tickArray.length; i<len; ++i) {
20667                 var next = i + 1;
20668                 if (tickArray[next] && tickArray[next] >= val) {
20669                     var diff1 = val - tickArray[i];
20670                     var diff2 = tickArray[next] - val;
20671                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20672                 }
20673             }
20674
20675             // The value is larger than the last tick, so we return the last
20676             // tick.
20677             return tickArray[tickArray.length - 1];
20678         }
20679     },
20680
20681     /**
20682      * toString method
20683      * @method toString
20684      * @return {string} string representation of the dd obj
20685      */
20686     toString: function() {
20687         return ("DragDrop " + this.id);
20688     }
20689
20690 });
20691
20692 })();
20693 /*
20694  * Based on:
20695  * Ext JS Library 1.1.1
20696  * Copyright(c) 2006-2007, Ext JS, LLC.
20697  *
20698  * Originally Released Under LGPL - original licence link has changed is not relivant.
20699  *
20700  * Fork - LGPL
20701  * <script type="text/javascript">
20702  */
20703
20704
20705 /**
20706  * The drag and drop utility provides a framework for building drag and drop
20707  * applications.  In addition to enabling drag and drop for specific elements,
20708  * the drag and drop elements are tracked by the manager class, and the
20709  * interactions between the various elements are tracked during the drag and
20710  * the implementing code is notified about these important moments.
20711  */
20712
20713 // Only load the library once.  Rewriting the manager class would orphan
20714 // existing drag and drop instances.
20715 if (!Roo.dd.DragDropMgr) {
20716
20717 /**
20718  * @class Roo.dd.DragDropMgr
20719  * DragDropMgr is a singleton that tracks the element interaction for
20720  * all DragDrop items in the window.  Generally, you will not call
20721  * this class directly, but it does have helper methods that could
20722  * be useful in your DragDrop implementations.
20723  * @static
20724  */
20725 Roo.dd.DragDropMgr = function() {
20726
20727     var Event = Roo.EventManager;
20728
20729     return {
20730
20731         /**
20732          * Two dimensional Array of registered DragDrop objects.  The first
20733          * dimension is the DragDrop item group, the second the DragDrop
20734          * object.
20735          * @property ids
20736          * @type {string: string}
20737          * @private
20738          * @static
20739          */
20740         ids: {},
20741
20742         /**
20743          * Array of element ids defined as drag handles.  Used to determine
20744          * if the element that generated the mousedown event is actually the
20745          * handle and not the html element itself.
20746          * @property handleIds
20747          * @type {string: string}
20748          * @private
20749          * @static
20750          */
20751         handleIds: {},
20752
20753         /**
20754          * the DragDrop object that is currently being dragged
20755          * @property dragCurrent
20756          * @type DragDrop
20757          * @private
20758          * @static
20759          **/
20760         dragCurrent: null,
20761
20762         /**
20763          * the DragDrop object(s) that are being hovered over
20764          * @property dragOvers
20765          * @type Array
20766          * @private
20767          * @static
20768          */
20769         dragOvers: {},
20770
20771         /**
20772          * the X distance between the cursor and the object being dragged
20773          * @property deltaX
20774          * @type int
20775          * @private
20776          * @static
20777          */
20778         deltaX: 0,
20779
20780         /**
20781          * the Y distance between the cursor and the object being dragged
20782          * @property deltaY
20783          * @type int
20784          * @private
20785          * @static
20786          */
20787         deltaY: 0,
20788
20789         /**
20790          * Flag to determine if we should prevent the default behavior of the
20791          * events we define. By default this is true, but this can be set to
20792          * false if you need the default behavior (not recommended)
20793          * @property preventDefault
20794          * @type boolean
20795          * @static
20796          */
20797         preventDefault: true,
20798
20799         /**
20800          * Flag to determine if we should stop the propagation of the events
20801          * we generate. This is true by default but you may want to set it to
20802          * false if the html element contains other features that require the
20803          * mouse click.
20804          * @property stopPropagation
20805          * @type boolean
20806          * @static
20807          */
20808         stopPropagation: true,
20809
20810         /**
20811          * Internal flag that is set to true when drag and drop has been
20812          * intialized
20813          * @property initialized
20814          * @private
20815          * @static
20816          */
20817         initalized: false,
20818
20819         /**
20820          * All drag and drop can be disabled.
20821          * @property locked
20822          * @private
20823          * @static
20824          */
20825         locked: false,
20826
20827         /**
20828          * Called the first time an element is registered.
20829          * @method init
20830          * @private
20831          * @static
20832          */
20833         init: function() {
20834             this.initialized = true;
20835         },
20836
20837         /**
20838          * In point mode, drag and drop interaction is defined by the
20839          * location of the cursor during the drag/drop
20840          * @property POINT
20841          * @type int
20842          * @static
20843          */
20844         POINT: 0,
20845
20846         /**
20847          * In intersect mode, drag and drop interactio nis defined by the
20848          * overlap of two or more drag and drop objects.
20849          * @property INTERSECT
20850          * @type int
20851          * @static
20852          */
20853         INTERSECT: 1,
20854
20855         /**
20856          * The current drag and drop mode.  Default: POINT
20857          * @property mode
20858          * @type int
20859          * @static
20860          */
20861         mode: 0,
20862
20863         /**
20864          * Runs method on all drag and drop objects
20865          * @method _execOnAll
20866          * @private
20867          * @static
20868          */
20869         _execOnAll: function(sMethod, args) {
20870             for (var i in this.ids) {
20871                 for (var j in this.ids[i]) {
20872                     var oDD = this.ids[i][j];
20873                     if (! this.isTypeOfDD(oDD)) {
20874                         continue;
20875                     }
20876                     oDD[sMethod].apply(oDD, args);
20877                 }
20878             }
20879         },
20880
20881         /**
20882          * Drag and drop initialization.  Sets up the global event handlers
20883          * @method _onLoad
20884          * @private
20885          * @static
20886          */
20887         _onLoad: function() {
20888
20889             this.init();
20890
20891             if (!Roo.isTouch) {
20892                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20893                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20894             }
20895             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20896             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20897             
20898             Event.on(window,   "unload",    this._onUnload, this, true);
20899             Event.on(window,   "resize",    this._onResize, this, true);
20900             // Event.on(window,   "mouseout",    this._test);
20901
20902         },
20903
20904         /**
20905          * Reset constraints on all drag and drop objs
20906          * @method _onResize
20907          * @private
20908          * @static
20909          */
20910         _onResize: function(e) {
20911             this._execOnAll("resetConstraints", []);
20912         },
20913
20914         /**
20915          * Lock all drag and drop functionality
20916          * @method lock
20917          * @static
20918          */
20919         lock: function() { this.locked = true; },
20920
20921         /**
20922          * Unlock all drag and drop functionality
20923          * @method unlock
20924          * @static
20925          */
20926         unlock: function() { this.locked = false; },
20927
20928         /**
20929          * Is drag and drop locked?
20930          * @method isLocked
20931          * @return {boolean} True if drag and drop is locked, false otherwise.
20932          * @static
20933          */
20934         isLocked: function() { return this.locked; },
20935
20936         /**
20937          * Location cache that is set for all drag drop objects when a drag is
20938          * initiated, cleared when the drag is finished.
20939          * @property locationCache
20940          * @private
20941          * @static
20942          */
20943         locationCache: {},
20944
20945         /**
20946          * Set useCache to false if you want to force object the lookup of each
20947          * drag and drop linked element constantly during a drag.
20948          * @property useCache
20949          * @type boolean
20950          * @static
20951          */
20952         useCache: true,
20953
20954         /**
20955          * The number of pixels that the mouse needs to move after the
20956          * mousedown before the drag is initiated.  Default=3;
20957          * @property clickPixelThresh
20958          * @type int
20959          * @static
20960          */
20961         clickPixelThresh: 3,
20962
20963         /**
20964          * The number of milliseconds after the mousedown event to initiate the
20965          * drag if we don't get a mouseup event. Default=1000
20966          * @property clickTimeThresh
20967          * @type int
20968          * @static
20969          */
20970         clickTimeThresh: 350,
20971
20972         /**
20973          * Flag that indicates that either the drag pixel threshold or the
20974          * mousdown time threshold has been met
20975          * @property dragThreshMet
20976          * @type boolean
20977          * @private
20978          * @static
20979          */
20980         dragThreshMet: false,
20981
20982         /**
20983          * Timeout used for the click time threshold
20984          * @property clickTimeout
20985          * @type Object
20986          * @private
20987          * @static
20988          */
20989         clickTimeout: null,
20990
20991         /**
20992          * The X position of the mousedown event stored for later use when a
20993          * drag threshold is met.
20994          * @property startX
20995          * @type int
20996          * @private
20997          * @static
20998          */
20999         startX: 0,
21000
21001         /**
21002          * The Y position of the mousedown event stored for later use when a
21003          * drag threshold is met.
21004          * @property startY
21005          * @type int
21006          * @private
21007          * @static
21008          */
21009         startY: 0,
21010
21011         /**
21012          * Each DragDrop instance must be registered with the DragDropMgr.
21013          * This is executed in DragDrop.init()
21014          * @method regDragDrop
21015          * @param {DragDrop} oDD the DragDrop object to register
21016          * @param {String} sGroup the name of the group this element belongs to
21017          * @static
21018          */
21019         regDragDrop: function(oDD, sGroup) {
21020             if (!this.initialized) { this.init(); }
21021
21022             if (!this.ids[sGroup]) {
21023                 this.ids[sGroup] = {};
21024             }
21025             this.ids[sGroup][oDD.id] = oDD;
21026         },
21027
21028         /**
21029          * Removes the supplied dd instance from the supplied group. Executed
21030          * by DragDrop.removeFromGroup, so don't call this function directly.
21031          * @method removeDDFromGroup
21032          * @private
21033          * @static
21034          */
21035         removeDDFromGroup: function(oDD, sGroup) {
21036             if (!this.ids[sGroup]) {
21037                 this.ids[sGroup] = {};
21038             }
21039
21040             var obj = this.ids[sGroup];
21041             if (obj && obj[oDD.id]) {
21042                 delete obj[oDD.id];
21043             }
21044         },
21045
21046         /**
21047          * Unregisters a drag and drop item.  This is executed in
21048          * DragDrop.unreg, use that method instead of calling this directly.
21049          * @method _remove
21050          * @private
21051          * @static
21052          */
21053         _remove: function(oDD) {
21054             for (var g in oDD.groups) {
21055                 if (g && this.ids[g][oDD.id]) {
21056                     delete this.ids[g][oDD.id];
21057                 }
21058             }
21059             delete this.handleIds[oDD.id];
21060         },
21061
21062         /**
21063          * Each DragDrop handle element must be registered.  This is done
21064          * automatically when executing DragDrop.setHandleElId()
21065          * @method regHandle
21066          * @param {String} sDDId the DragDrop id this element is a handle for
21067          * @param {String} sHandleId the id of the element that is the drag
21068          * handle
21069          * @static
21070          */
21071         regHandle: function(sDDId, sHandleId) {
21072             if (!this.handleIds[sDDId]) {
21073                 this.handleIds[sDDId] = {};
21074             }
21075             this.handleIds[sDDId][sHandleId] = sHandleId;
21076         },
21077
21078         /**
21079          * Utility function to determine if a given element has been
21080          * registered as a drag drop item.
21081          * @method isDragDrop
21082          * @param {String} id the element id to check
21083          * @return {boolean} true if this element is a DragDrop item,
21084          * false otherwise
21085          * @static
21086          */
21087         isDragDrop: function(id) {
21088             return ( this.getDDById(id) ) ? true : false;
21089         },
21090
21091         /**
21092          * Returns the drag and drop instances that are in all groups the
21093          * passed in instance belongs to.
21094          * @method getRelated
21095          * @param {DragDrop} p_oDD the obj to get related data for
21096          * @param {boolean} bTargetsOnly if true, only return targetable objs
21097          * @return {DragDrop[]} the related instances
21098          * @static
21099          */
21100         getRelated: function(p_oDD, bTargetsOnly) {
21101             var oDDs = [];
21102             for (var i in p_oDD.groups) {
21103                 for (j in this.ids[i]) {
21104                     var dd = this.ids[i][j];
21105                     if (! this.isTypeOfDD(dd)) {
21106                         continue;
21107                     }
21108                     if (!bTargetsOnly || dd.isTarget) {
21109                         oDDs[oDDs.length] = dd;
21110                     }
21111                 }
21112             }
21113
21114             return oDDs;
21115         },
21116
21117         /**
21118          * Returns true if the specified dd target is a legal target for
21119          * the specifice drag obj
21120          * @method isLegalTarget
21121          * @param {DragDrop} the drag obj
21122          * @param {DragDrop} the target
21123          * @return {boolean} true if the target is a legal target for the
21124          * dd obj
21125          * @static
21126          */
21127         isLegalTarget: function (oDD, oTargetDD) {
21128             var targets = this.getRelated(oDD, true);
21129             for (var i=0, len=targets.length;i<len;++i) {
21130                 if (targets[i].id == oTargetDD.id) {
21131                     return true;
21132                 }
21133             }
21134
21135             return false;
21136         },
21137
21138         /**
21139          * My goal is to be able to transparently determine if an object is
21140          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21141          * returns "object", oDD.constructor.toString() always returns
21142          * "DragDrop" and not the name of the subclass.  So for now it just
21143          * evaluates a well-known variable in DragDrop.
21144          * @method isTypeOfDD
21145          * @param {Object} the object to evaluate
21146          * @return {boolean} true if typeof oDD = DragDrop
21147          * @static
21148          */
21149         isTypeOfDD: function (oDD) {
21150             return (oDD && oDD.__ygDragDrop);
21151         },
21152
21153         /**
21154          * Utility function to determine if a given element has been
21155          * registered as a drag drop handle for the given Drag Drop object.
21156          * @method isHandle
21157          * @param {String} id the element id to check
21158          * @return {boolean} true if this element is a DragDrop handle, false
21159          * otherwise
21160          * @static
21161          */
21162         isHandle: function(sDDId, sHandleId) {
21163             return ( this.handleIds[sDDId] &&
21164                             this.handleIds[sDDId][sHandleId] );
21165         },
21166
21167         /**
21168          * Returns the DragDrop instance for a given id
21169          * @method getDDById
21170          * @param {String} id the id of the DragDrop object
21171          * @return {DragDrop} the drag drop object, null if it is not found
21172          * @static
21173          */
21174         getDDById: function(id) {
21175             for (var i in this.ids) {
21176                 if (this.ids[i][id]) {
21177                     return this.ids[i][id];
21178                 }
21179             }
21180             return null;
21181         },
21182
21183         /**
21184          * Fired after a registered DragDrop object gets the mousedown event.
21185          * Sets up the events required to track the object being dragged
21186          * @method handleMouseDown
21187          * @param {Event} e the event
21188          * @param oDD the DragDrop object being dragged
21189          * @private
21190          * @static
21191          */
21192         handleMouseDown: function(e, oDD) {
21193             if(Roo.QuickTips){
21194                 Roo.QuickTips.disable();
21195             }
21196             this.currentTarget = e.getTarget();
21197
21198             this.dragCurrent = oDD;
21199
21200             var el = oDD.getEl();
21201
21202             // track start position
21203             this.startX = e.getPageX();
21204             this.startY = e.getPageY();
21205
21206             this.deltaX = this.startX - el.offsetLeft;
21207             this.deltaY = this.startY - el.offsetTop;
21208
21209             this.dragThreshMet = false;
21210
21211             this.clickTimeout = setTimeout(
21212                     function() {
21213                         var DDM = Roo.dd.DDM;
21214                         DDM.startDrag(DDM.startX, DDM.startY);
21215                     },
21216                     this.clickTimeThresh );
21217         },
21218
21219         /**
21220          * Fired when either the drag pixel threshol or the mousedown hold
21221          * time threshold has been met.
21222          * @method startDrag
21223          * @param x {int} the X position of the original mousedown
21224          * @param y {int} the Y position of the original mousedown
21225          * @static
21226          */
21227         startDrag: function(x, y) {
21228             clearTimeout(this.clickTimeout);
21229             if (this.dragCurrent) {
21230                 this.dragCurrent.b4StartDrag(x, y);
21231                 this.dragCurrent.startDrag(x, y);
21232             }
21233             this.dragThreshMet = true;
21234         },
21235
21236         /**
21237          * Internal function to handle the mouseup event.  Will be invoked
21238          * from the context of the document.
21239          * @method handleMouseUp
21240          * @param {Event} e the event
21241          * @private
21242          * @static
21243          */
21244         handleMouseUp: function(e) {
21245
21246             if(Roo.QuickTips){
21247                 Roo.QuickTips.enable();
21248             }
21249             if (! this.dragCurrent) {
21250                 return;
21251             }
21252
21253             clearTimeout(this.clickTimeout);
21254
21255             if (this.dragThreshMet) {
21256                 this.fireEvents(e, true);
21257             } else {
21258             }
21259
21260             this.stopDrag(e);
21261
21262             this.stopEvent(e);
21263         },
21264
21265         /**
21266          * Utility to stop event propagation and event default, if these
21267          * features are turned on.
21268          * @method stopEvent
21269          * @param {Event} e the event as returned by this.getEvent()
21270          * @static
21271          */
21272         stopEvent: function(e){
21273             if(this.stopPropagation) {
21274                 e.stopPropagation();
21275             }
21276
21277             if (this.preventDefault) {
21278                 e.preventDefault();
21279             }
21280         },
21281
21282         /**
21283          * Internal function to clean up event handlers after the drag
21284          * operation is complete
21285          * @method stopDrag
21286          * @param {Event} e the event
21287          * @private
21288          * @static
21289          */
21290         stopDrag: function(e) {
21291             // Fire the drag end event for the item that was dragged
21292             if (this.dragCurrent) {
21293                 if (this.dragThreshMet) {
21294                     this.dragCurrent.b4EndDrag(e);
21295                     this.dragCurrent.endDrag(e);
21296                 }
21297
21298                 this.dragCurrent.onMouseUp(e);
21299             }
21300
21301             this.dragCurrent = null;
21302             this.dragOvers = {};
21303         },
21304
21305         /**
21306          * Internal function to handle the mousemove event.  Will be invoked
21307          * from the context of the html element.
21308          *
21309          * @TODO figure out what we can do about mouse events lost when the
21310          * user drags objects beyond the window boundary.  Currently we can
21311          * detect this in internet explorer by verifying that the mouse is
21312          * down during the mousemove event.  Firefox doesn't give us the
21313          * button state on the mousemove event.
21314          * @method handleMouseMove
21315          * @param {Event} e the event
21316          * @private
21317          * @static
21318          */
21319         handleMouseMove: function(e) {
21320             if (! this.dragCurrent) {
21321                 return true;
21322             }
21323
21324             // var button = e.which || e.button;
21325
21326             // check for IE mouseup outside of page boundary
21327             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21328                 this.stopEvent(e);
21329                 return this.handleMouseUp(e);
21330             }
21331
21332             if (!this.dragThreshMet) {
21333                 var diffX = Math.abs(this.startX - e.getPageX());
21334                 var diffY = Math.abs(this.startY - e.getPageY());
21335                 if (diffX > this.clickPixelThresh ||
21336                             diffY > this.clickPixelThresh) {
21337                     this.startDrag(this.startX, this.startY);
21338                 }
21339             }
21340
21341             if (this.dragThreshMet) {
21342                 this.dragCurrent.b4Drag(e);
21343                 this.dragCurrent.onDrag(e);
21344                 if(!this.dragCurrent.moveOnly){
21345                     this.fireEvents(e, false);
21346                 }
21347             }
21348
21349             this.stopEvent(e);
21350
21351             return true;
21352         },
21353
21354         /**
21355          * Iterates over all of the DragDrop elements to find ones we are
21356          * hovering over or dropping on
21357          * @method fireEvents
21358          * @param {Event} e the event
21359          * @param {boolean} isDrop is this a drop op or a mouseover op?
21360          * @private
21361          * @static
21362          */
21363         fireEvents: function(e, isDrop) {
21364             var dc = this.dragCurrent;
21365
21366             // If the user did the mouse up outside of the window, we could
21367             // get here even though we have ended the drag.
21368             if (!dc || dc.isLocked()) {
21369                 return;
21370             }
21371
21372             var pt = e.getPoint();
21373
21374             // cache the previous dragOver array
21375             var oldOvers = [];
21376
21377             var outEvts   = [];
21378             var overEvts  = [];
21379             var dropEvts  = [];
21380             var enterEvts = [];
21381
21382             // Check to see if the object(s) we were hovering over is no longer
21383             // being hovered over so we can fire the onDragOut event
21384             for (var i in this.dragOvers) {
21385
21386                 var ddo = this.dragOvers[i];
21387
21388                 if (! this.isTypeOfDD(ddo)) {
21389                     continue;
21390                 }
21391
21392                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21393                     outEvts.push( ddo );
21394                 }
21395
21396                 oldOvers[i] = true;
21397                 delete this.dragOvers[i];
21398             }
21399
21400             for (var sGroup in dc.groups) {
21401
21402                 if ("string" != typeof sGroup) {
21403                     continue;
21404                 }
21405
21406                 for (i in this.ids[sGroup]) {
21407                     var oDD = this.ids[sGroup][i];
21408                     if (! this.isTypeOfDD(oDD)) {
21409                         continue;
21410                     }
21411
21412                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21413                         if (this.isOverTarget(pt, oDD, this.mode)) {
21414                             // look for drop interactions
21415                             if (isDrop) {
21416                                 dropEvts.push( oDD );
21417                             // look for drag enter and drag over interactions
21418                             } else {
21419
21420                                 // initial drag over: dragEnter fires
21421                                 if (!oldOvers[oDD.id]) {
21422                                     enterEvts.push( oDD );
21423                                 // subsequent drag overs: dragOver fires
21424                                 } else {
21425                                     overEvts.push( oDD );
21426                                 }
21427
21428                                 this.dragOvers[oDD.id] = oDD;
21429                             }
21430                         }
21431                     }
21432                 }
21433             }
21434
21435             if (this.mode) {
21436                 if (outEvts.length) {
21437                     dc.b4DragOut(e, outEvts);
21438                     dc.onDragOut(e, outEvts);
21439                 }
21440
21441                 if (enterEvts.length) {
21442                     dc.onDragEnter(e, enterEvts);
21443                 }
21444
21445                 if (overEvts.length) {
21446                     dc.b4DragOver(e, overEvts);
21447                     dc.onDragOver(e, overEvts);
21448                 }
21449
21450                 if (dropEvts.length) {
21451                     dc.b4DragDrop(e, dropEvts);
21452                     dc.onDragDrop(e, dropEvts);
21453                 }
21454
21455             } else {
21456                 // fire dragout events
21457                 var len = 0;
21458                 for (i=0, len=outEvts.length; i<len; ++i) {
21459                     dc.b4DragOut(e, outEvts[i].id);
21460                     dc.onDragOut(e, outEvts[i].id);
21461                 }
21462
21463                 // fire enter events
21464                 for (i=0,len=enterEvts.length; i<len; ++i) {
21465                     // dc.b4DragEnter(e, oDD.id);
21466                     dc.onDragEnter(e, enterEvts[i].id);
21467                 }
21468
21469                 // fire over events
21470                 for (i=0,len=overEvts.length; i<len; ++i) {
21471                     dc.b4DragOver(e, overEvts[i].id);
21472                     dc.onDragOver(e, overEvts[i].id);
21473                 }
21474
21475                 // fire drop events
21476                 for (i=0, len=dropEvts.length; i<len; ++i) {
21477                     dc.b4DragDrop(e, dropEvts[i].id);
21478                     dc.onDragDrop(e, dropEvts[i].id);
21479                 }
21480
21481             }
21482
21483             // notify about a drop that did not find a target
21484             if (isDrop && !dropEvts.length) {
21485                 dc.onInvalidDrop(e);
21486             }
21487
21488         },
21489
21490         /**
21491          * Helper function for getting the best match from the list of drag
21492          * and drop objects returned by the drag and drop events when we are
21493          * in INTERSECT mode.  It returns either the first object that the
21494          * cursor is over, or the object that has the greatest overlap with
21495          * the dragged element.
21496          * @method getBestMatch
21497          * @param  {DragDrop[]} dds The array of drag and drop objects
21498          * targeted
21499          * @return {DragDrop}       The best single match
21500          * @static
21501          */
21502         getBestMatch: function(dds) {
21503             var winner = null;
21504             // Return null if the input is not what we expect
21505             //if (!dds || !dds.length || dds.length == 0) {
21506                // winner = null;
21507             // If there is only one item, it wins
21508             //} else if (dds.length == 1) {
21509
21510             var len = dds.length;
21511
21512             if (len == 1) {
21513                 winner = dds[0];
21514             } else {
21515                 // Loop through the targeted items
21516                 for (var i=0; i<len; ++i) {
21517                     var dd = dds[i];
21518                     // If the cursor is over the object, it wins.  If the
21519                     // cursor is over multiple matches, the first one we come
21520                     // to wins.
21521                     if (dd.cursorIsOver) {
21522                         winner = dd;
21523                         break;
21524                     // Otherwise the object with the most overlap wins
21525                     } else {
21526                         if (!winner ||
21527                             winner.overlap.getArea() < dd.overlap.getArea()) {
21528                             winner = dd;
21529                         }
21530                     }
21531                 }
21532             }
21533
21534             return winner;
21535         },
21536
21537         /**
21538          * Refreshes the cache of the top-left and bottom-right points of the
21539          * drag and drop objects in the specified group(s).  This is in the
21540          * format that is stored in the drag and drop instance, so typical
21541          * usage is:
21542          * <code>
21543          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21544          * </code>
21545          * Alternatively:
21546          * <code>
21547          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21548          * </code>
21549          * @TODO this really should be an indexed array.  Alternatively this
21550          * method could accept both.
21551          * @method refreshCache
21552          * @param {Object} groups an associative array of groups to refresh
21553          * @static
21554          */
21555         refreshCache: function(groups) {
21556             for (var sGroup in groups) {
21557                 if ("string" != typeof sGroup) {
21558                     continue;
21559                 }
21560                 for (var i in this.ids[sGroup]) {
21561                     var oDD = this.ids[sGroup][i];
21562
21563                     if (this.isTypeOfDD(oDD)) {
21564                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21565                         var loc = this.getLocation(oDD);
21566                         if (loc) {
21567                             this.locationCache[oDD.id] = loc;
21568                         } else {
21569                             delete this.locationCache[oDD.id];
21570                             // this will unregister the drag and drop object if
21571                             // the element is not in a usable state
21572                             // oDD.unreg();
21573                         }
21574                     }
21575                 }
21576             }
21577         },
21578
21579         /**
21580          * This checks to make sure an element exists and is in the DOM.  The
21581          * main purpose is to handle cases where innerHTML is used to remove
21582          * drag and drop objects from the DOM.  IE provides an 'unspecified
21583          * error' when trying to access the offsetParent of such an element
21584          * @method verifyEl
21585          * @param {HTMLElement} el the element to check
21586          * @return {boolean} true if the element looks usable
21587          * @static
21588          */
21589         verifyEl: function(el) {
21590             if (el) {
21591                 var parent;
21592                 if(Roo.isIE){
21593                     try{
21594                         parent = el.offsetParent;
21595                     }catch(e){}
21596                 }else{
21597                     parent = el.offsetParent;
21598                 }
21599                 if (parent) {
21600                     return true;
21601                 }
21602             }
21603
21604             return false;
21605         },
21606
21607         /**
21608          * Returns a Region object containing the drag and drop element's position
21609          * and size, including the padding configured for it
21610          * @method getLocation
21611          * @param {DragDrop} oDD the drag and drop object to get the
21612          *                       location for
21613          * @return {Roo.lib.Region} a Region object representing the total area
21614          *                             the element occupies, including any padding
21615          *                             the instance is configured for.
21616          * @static
21617          */
21618         getLocation: function(oDD) {
21619             if (! this.isTypeOfDD(oDD)) {
21620                 return null;
21621             }
21622
21623             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21624
21625             try {
21626                 pos= Roo.lib.Dom.getXY(el);
21627             } catch (e) { }
21628
21629             if (!pos) {
21630                 return null;
21631             }
21632
21633             x1 = pos[0];
21634             x2 = x1 + el.offsetWidth;
21635             y1 = pos[1];
21636             y2 = y1 + el.offsetHeight;
21637
21638             t = y1 - oDD.padding[0];
21639             r = x2 + oDD.padding[1];
21640             b = y2 + oDD.padding[2];
21641             l = x1 - oDD.padding[3];
21642
21643             return new Roo.lib.Region( t, r, b, l );
21644         },
21645
21646         /**
21647          * Checks the cursor location to see if it over the target
21648          * @method isOverTarget
21649          * @param {Roo.lib.Point} pt The point to evaluate
21650          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21651          * @return {boolean} true if the mouse is over the target
21652          * @private
21653          * @static
21654          */
21655         isOverTarget: function(pt, oTarget, intersect) {
21656             // use cache if available
21657             var loc = this.locationCache[oTarget.id];
21658             if (!loc || !this.useCache) {
21659                 loc = this.getLocation(oTarget);
21660                 this.locationCache[oTarget.id] = loc;
21661
21662             }
21663
21664             if (!loc) {
21665                 return false;
21666             }
21667
21668             oTarget.cursorIsOver = loc.contains( pt );
21669
21670             // DragDrop is using this as a sanity check for the initial mousedown
21671             // in this case we are done.  In POINT mode, if the drag obj has no
21672             // contraints, we are also done. Otherwise we need to evaluate the
21673             // location of the target as related to the actual location of the
21674             // dragged element.
21675             var dc = this.dragCurrent;
21676             if (!dc || !dc.getTargetCoord ||
21677                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21678                 return oTarget.cursorIsOver;
21679             }
21680
21681             oTarget.overlap = null;
21682
21683             // Get the current location of the drag element, this is the
21684             // location of the mouse event less the delta that represents
21685             // where the original mousedown happened on the element.  We
21686             // need to consider constraints and ticks as well.
21687             var pos = dc.getTargetCoord(pt.x, pt.y);
21688
21689             var el = dc.getDragEl();
21690             var curRegion = new Roo.lib.Region( pos.y,
21691                                                    pos.x + el.offsetWidth,
21692                                                    pos.y + el.offsetHeight,
21693                                                    pos.x );
21694
21695             var overlap = curRegion.intersect(loc);
21696
21697             if (overlap) {
21698                 oTarget.overlap = overlap;
21699                 return (intersect) ? true : oTarget.cursorIsOver;
21700             } else {
21701                 return false;
21702             }
21703         },
21704
21705         /**
21706          * unload event handler
21707          * @method _onUnload
21708          * @private
21709          * @static
21710          */
21711         _onUnload: function(e, me) {
21712             Roo.dd.DragDropMgr.unregAll();
21713         },
21714
21715         /**
21716          * Cleans up the drag and drop events and objects.
21717          * @method unregAll
21718          * @private
21719          * @static
21720          */
21721         unregAll: function() {
21722
21723             if (this.dragCurrent) {
21724                 this.stopDrag();
21725                 this.dragCurrent = null;
21726             }
21727
21728             this._execOnAll("unreg", []);
21729
21730             for (i in this.elementCache) {
21731                 delete this.elementCache[i];
21732             }
21733
21734             this.elementCache = {};
21735             this.ids = {};
21736         },
21737
21738         /**
21739          * A cache of DOM elements
21740          * @property elementCache
21741          * @private
21742          * @static
21743          */
21744         elementCache: {},
21745
21746         /**
21747          * Get the wrapper for the DOM element specified
21748          * @method getElWrapper
21749          * @param {String} id the id of the element to get
21750          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21751          * @private
21752          * @deprecated This wrapper isn't that useful
21753          * @static
21754          */
21755         getElWrapper: function(id) {
21756             var oWrapper = this.elementCache[id];
21757             if (!oWrapper || !oWrapper.el) {
21758                 oWrapper = this.elementCache[id] =
21759                     new this.ElementWrapper(Roo.getDom(id));
21760             }
21761             return oWrapper;
21762         },
21763
21764         /**
21765          * Returns the actual DOM element
21766          * @method getElement
21767          * @param {String} id the id of the elment to get
21768          * @return {Object} The element
21769          * @deprecated use Roo.getDom instead
21770          * @static
21771          */
21772         getElement: function(id) {
21773             return Roo.getDom(id);
21774         },
21775
21776         /**
21777          * Returns the style property for the DOM element (i.e.,
21778          * document.getElById(id).style)
21779          * @method getCss
21780          * @param {String} id the id of the elment to get
21781          * @return {Object} The style property of the element
21782          * @deprecated use Roo.getDom instead
21783          * @static
21784          */
21785         getCss: function(id) {
21786             var el = Roo.getDom(id);
21787             return (el) ? el.style : null;
21788         },
21789
21790         /**
21791          * Inner class for cached elements
21792          * @class DragDropMgr.ElementWrapper
21793          * @for DragDropMgr
21794          * @private
21795          * @deprecated
21796          */
21797         ElementWrapper: function(el) {
21798                 /**
21799                  * The element
21800                  * @property el
21801                  */
21802                 this.el = el || null;
21803                 /**
21804                  * The element id
21805                  * @property id
21806                  */
21807                 this.id = this.el && el.id;
21808                 /**
21809                  * A reference to the style property
21810                  * @property css
21811                  */
21812                 this.css = this.el && el.style;
21813             },
21814
21815         /**
21816          * Returns the X position of an html element
21817          * @method getPosX
21818          * @param el the element for which to get the position
21819          * @return {int} the X coordinate
21820          * @for DragDropMgr
21821          * @deprecated use Roo.lib.Dom.getX instead
21822          * @static
21823          */
21824         getPosX: function(el) {
21825             return Roo.lib.Dom.getX(el);
21826         },
21827
21828         /**
21829          * Returns the Y position of an html element
21830          * @method getPosY
21831          * @param el the element for which to get the position
21832          * @return {int} the Y coordinate
21833          * @deprecated use Roo.lib.Dom.getY instead
21834          * @static
21835          */
21836         getPosY: function(el) {
21837             return Roo.lib.Dom.getY(el);
21838         },
21839
21840         /**
21841          * Swap two nodes.  In IE, we use the native method, for others we
21842          * emulate the IE behavior
21843          * @method swapNode
21844          * @param n1 the first node to swap
21845          * @param n2 the other node to swap
21846          * @static
21847          */
21848         swapNode: function(n1, n2) {
21849             if (n1.swapNode) {
21850                 n1.swapNode(n2);
21851             } else {
21852                 var p = n2.parentNode;
21853                 var s = n2.nextSibling;
21854
21855                 if (s == n1) {
21856                     p.insertBefore(n1, n2);
21857                 } else if (n2 == n1.nextSibling) {
21858                     p.insertBefore(n2, n1);
21859                 } else {
21860                     n1.parentNode.replaceChild(n2, n1);
21861                     p.insertBefore(n1, s);
21862                 }
21863             }
21864         },
21865
21866         /**
21867          * Returns the current scroll position
21868          * @method getScroll
21869          * @private
21870          * @static
21871          */
21872         getScroll: function () {
21873             var t, l, dde=document.documentElement, db=document.body;
21874             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21875                 t = dde.scrollTop;
21876                 l = dde.scrollLeft;
21877             } else if (db) {
21878                 t = db.scrollTop;
21879                 l = db.scrollLeft;
21880             } else {
21881
21882             }
21883             return { top: t, left: l };
21884         },
21885
21886         /**
21887          * Returns the specified element style property
21888          * @method getStyle
21889          * @param {HTMLElement} el          the element
21890          * @param {string}      styleProp   the style property
21891          * @return {string} The value of the style property
21892          * @deprecated use Roo.lib.Dom.getStyle
21893          * @static
21894          */
21895         getStyle: function(el, styleProp) {
21896             return Roo.fly(el).getStyle(styleProp);
21897         },
21898
21899         /**
21900          * Gets the scrollTop
21901          * @method getScrollTop
21902          * @return {int} the document's scrollTop
21903          * @static
21904          */
21905         getScrollTop: function () { return this.getScroll().top; },
21906
21907         /**
21908          * Gets the scrollLeft
21909          * @method getScrollLeft
21910          * @return {int} the document's scrollTop
21911          * @static
21912          */
21913         getScrollLeft: function () { return this.getScroll().left; },
21914
21915         /**
21916          * Sets the x/y position of an element to the location of the
21917          * target element.
21918          * @method moveToEl
21919          * @param {HTMLElement} moveEl      The element to move
21920          * @param {HTMLElement} targetEl    The position reference element
21921          * @static
21922          */
21923         moveToEl: function (moveEl, targetEl) {
21924             var aCoord = Roo.lib.Dom.getXY(targetEl);
21925             Roo.lib.Dom.setXY(moveEl, aCoord);
21926         },
21927
21928         /**
21929          * Numeric array sort function
21930          * @method numericSort
21931          * @static
21932          */
21933         numericSort: function(a, b) { return (a - b); },
21934
21935         /**
21936          * Internal counter
21937          * @property _timeoutCount
21938          * @private
21939          * @static
21940          */
21941         _timeoutCount: 0,
21942
21943         /**
21944          * Trying to make the load order less important.  Without this we get
21945          * an error if this file is loaded before the Event Utility.
21946          * @method _addListeners
21947          * @private
21948          * @static
21949          */
21950         _addListeners: function() {
21951             var DDM = Roo.dd.DDM;
21952             if ( Roo.lib.Event && document ) {
21953                 DDM._onLoad();
21954             } else {
21955                 if (DDM._timeoutCount > 2000) {
21956                 } else {
21957                     setTimeout(DDM._addListeners, 10);
21958                     if (document && document.body) {
21959                         DDM._timeoutCount += 1;
21960                     }
21961                 }
21962             }
21963         },
21964
21965         /**
21966          * Recursively searches the immediate parent and all child nodes for
21967          * the handle element in order to determine wheter or not it was
21968          * clicked.
21969          * @method handleWasClicked
21970          * @param node the html element to inspect
21971          * @static
21972          */
21973         handleWasClicked: function(node, id) {
21974             if (this.isHandle(id, node.id)) {
21975                 return true;
21976             } else {
21977                 // check to see if this is a text node child of the one we want
21978                 var p = node.parentNode;
21979
21980                 while (p) {
21981                     if (this.isHandle(id, p.id)) {
21982                         return true;
21983                     } else {
21984                         p = p.parentNode;
21985                     }
21986                 }
21987             }
21988
21989             return false;
21990         }
21991
21992     };
21993
21994 }();
21995
21996 // shorter alias, save a few bytes
21997 Roo.dd.DDM = Roo.dd.DragDropMgr;
21998 Roo.dd.DDM._addListeners();
21999
22000 }/*
22001  * Based on:
22002  * Ext JS Library 1.1.1
22003  * Copyright(c) 2006-2007, Ext JS, LLC.
22004  *
22005  * Originally Released Under LGPL - original licence link has changed is not relivant.
22006  *
22007  * Fork - LGPL
22008  * <script type="text/javascript">
22009  */
22010
22011 /**
22012  * @class Roo.dd.DD
22013  * A DragDrop implementation where the linked element follows the
22014  * mouse cursor during a drag.
22015  * @extends Roo.dd.DragDrop
22016  * @constructor
22017  * @param {String} id the id of the linked element
22018  * @param {String} sGroup the group of related DragDrop items
22019  * @param {object} config an object containing configurable attributes
22020  *                Valid properties for DD:
22021  *                    scroll
22022  */
22023 Roo.dd.DD = function(id, sGroup, config) {
22024     if (id) {
22025         this.init(id, sGroup, config);
22026     }
22027 };
22028
22029 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22030
22031     /**
22032      * When set to true, the utility automatically tries to scroll the browser
22033      * window wehn a drag and drop element is dragged near the viewport boundary.
22034      * Defaults to true.
22035      * @property scroll
22036      * @type boolean
22037      */
22038     scroll: true,
22039
22040     /**
22041      * Sets the pointer offset to the distance between the linked element's top
22042      * left corner and the location the element was clicked
22043      * @method autoOffset
22044      * @param {int} iPageX the X coordinate of the click
22045      * @param {int} iPageY the Y coordinate of the click
22046      */
22047     autoOffset: function(iPageX, iPageY) {
22048         var x = iPageX - this.startPageX;
22049         var y = iPageY - this.startPageY;
22050         this.setDelta(x, y);
22051     },
22052
22053     /**
22054      * Sets the pointer offset.  You can call this directly to force the
22055      * offset to be in a particular location (e.g., pass in 0,0 to set it
22056      * to the center of the object)
22057      * @method setDelta
22058      * @param {int} iDeltaX the distance from the left
22059      * @param {int} iDeltaY the distance from the top
22060      */
22061     setDelta: function(iDeltaX, iDeltaY) {
22062         this.deltaX = iDeltaX;
22063         this.deltaY = iDeltaY;
22064     },
22065
22066     /**
22067      * Sets the drag element to the location of the mousedown or click event,
22068      * maintaining the cursor location relative to the location on the element
22069      * that was clicked.  Override this if you want to place the element in a
22070      * location other than where the cursor is.
22071      * @method setDragElPos
22072      * @param {int} iPageX the X coordinate of the mousedown or drag event
22073      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22074      */
22075     setDragElPos: function(iPageX, iPageY) {
22076         // the first time we do this, we are going to check to make sure
22077         // the element has css positioning
22078
22079         var el = this.getDragEl();
22080         this.alignElWithMouse(el, iPageX, iPageY);
22081     },
22082
22083     /**
22084      * Sets the element to the location of the mousedown or click event,
22085      * maintaining the cursor location relative to the location on the element
22086      * that was clicked.  Override this if you want to place the element in a
22087      * location other than where the cursor is.
22088      * @method alignElWithMouse
22089      * @param {HTMLElement} el the element to move
22090      * @param {int} iPageX the X coordinate of the mousedown or drag event
22091      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22092      */
22093     alignElWithMouse: function(el, iPageX, iPageY) {
22094         var oCoord = this.getTargetCoord(iPageX, iPageY);
22095         var fly = el.dom ? el : Roo.fly(el);
22096         if (!this.deltaSetXY) {
22097             var aCoord = [oCoord.x, oCoord.y];
22098             fly.setXY(aCoord);
22099             var newLeft = fly.getLeft(true);
22100             var newTop  = fly.getTop(true);
22101             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22102         } else {
22103             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22104         }
22105
22106         this.cachePosition(oCoord.x, oCoord.y);
22107         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22108         return oCoord;
22109     },
22110
22111     /**
22112      * Saves the most recent position so that we can reset the constraints and
22113      * tick marks on-demand.  We need to know this so that we can calculate the
22114      * number of pixels the element is offset from its original position.
22115      * @method cachePosition
22116      * @param iPageX the current x position (optional, this just makes it so we
22117      * don't have to look it up again)
22118      * @param iPageY the current y position (optional, this just makes it so we
22119      * don't have to look it up again)
22120      */
22121     cachePosition: function(iPageX, iPageY) {
22122         if (iPageX) {
22123             this.lastPageX = iPageX;
22124             this.lastPageY = iPageY;
22125         } else {
22126             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22127             this.lastPageX = aCoord[0];
22128             this.lastPageY = aCoord[1];
22129         }
22130     },
22131
22132     /**
22133      * Auto-scroll the window if the dragged object has been moved beyond the
22134      * visible window boundary.
22135      * @method autoScroll
22136      * @param {int} x the drag element's x position
22137      * @param {int} y the drag element's y position
22138      * @param {int} h the height of the drag element
22139      * @param {int} w the width of the drag element
22140      * @private
22141      */
22142     autoScroll: function(x, y, h, w) {
22143
22144         if (this.scroll) {
22145             // The client height
22146             var clientH = Roo.lib.Dom.getViewWidth();
22147
22148             // The client width
22149             var clientW = Roo.lib.Dom.getViewHeight();
22150
22151             // The amt scrolled down
22152             var st = this.DDM.getScrollTop();
22153
22154             // The amt scrolled right
22155             var sl = this.DDM.getScrollLeft();
22156
22157             // Location of the bottom of the element
22158             var bot = h + y;
22159
22160             // Location of the right of the element
22161             var right = w + x;
22162
22163             // The distance from the cursor to the bottom of the visible area,
22164             // adjusted so that we don't scroll if the cursor is beyond the
22165             // element drag constraints
22166             var toBot = (clientH + st - y - this.deltaY);
22167
22168             // The distance from the cursor to the right of the visible area
22169             var toRight = (clientW + sl - x - this.deltaX);
22170
22171
22172             // How close to the edge the cursor must be before we scroll
22173             // var thresh = (document.all) ? 100 : 40;
22174             var thresh = 40;
22175
22176             // How many pixels to scroll per autoscroll op.  This helps to reduce
22177             // clunky scrolling. IE is more sensitive about this ... it needs this
22178             // value to be higher.
22179             var scrAmt = (document.all) ? 80 : 30;
22180
22181             // Scroll down if we are near the bottom of the visible page and the
22182             // obj extends below the crease
22183             if ( bot > clientH && toBot < thresh ) {
22184                 window.scrollTo(sl, st + scrAmt);
22185             }
22186
22187             // Scroll up if the window is scrolled down and the top of the object
22188             // goes above the top border
22189             if ( y < st && st > 0 && y - st < thresh ) {
22190                 window.scrollTo(sl, st - scrAmt);
22191             }
22192
22193             // Scroll right if the obj is beyond the right border and the cursor is
22194             // near the border.
22195             if ( right > clientW && toRight < thresh ) {
22196                 window.scrollTo(sl + scrAmt, st);
22197             }
22198
22199             // Scroll left if the window has been scrolled to the right and the obj
22200             // extends past the left border
22201             if ( x < sl && sl > 0 && x - sl < thresh ) {
22202                 window.scrollTo(sl - scrAmt, st);
22203             }
22204         }
22205     },
22206
22207     /**
22208      * Finds the location the element should be placed if we want to move
22209      * it to where the mouse location less the click offset would place us.
22210      * @method getTargetCoord
22211      * @param {int} iPageX the X coordinate of the click
22212      * @param {int} iPageY the Y coordinate of the click
22213      * @return an object that contains the coordinates (Object.x and Object.y)
22214      * @private
22215      */
22216     getTargetCoord: function(iPageX, iPageY) {
22217
22218
22219         var x = iPageX - this.deltaX;
22220         var y = iPageY - this.deltaY;
22221
22222         if (this.constrainX) {
22223             if (x < this.minX) { x = this.minX; }
22224             if (x > this.maxX) { x = this.maxX; }
22225         }
22226
22227         if (this.constrainY) {
22228             if (y < this.minY) { y = this.minY; }
22229             if (y > this.maxY) { y = this.maxY; }
22230         }
22231
22232         x = this.getTick(x, this.xTicks);
22233         y = this.getTick(y, this.yTicks);
22234
22235
22236         return {x:x, y:y};
22237     },
22238
22239     /*
22240      * Sets up config options specific to this class. Overrides
22241      * Roo.dd.DragDrop, but all versions of this method through the
22242      * inheritance chain are called
22243      */
22244     applyConfig: function() {
22245         Roo.dd.DD.superclass.applyConfig.call(this);
22246         this.scroll = (this.config.scroll !== false);
22247     },
22248
22249     /*
22250      * Event that fires prior to the onMouseDown event.  Overrides
22251      * Roo.dd.DragDrop.
22252      */
22253     b4MouseDown: function(e) {
22254         // this.resetConstraints();
22255         this.autoOffset(e.getPageX(),
22256                             e.getPageY());
22257     },
22258
22259     /*
22260      * Event that fires prior to the onDrag event.  Overrides
22261      * Roo.dd.DragDrop.
22262      */
22263     b4Drag: function(e) {
22264         this.setDragElPos(e.getPageX(),
22265                             e.getPageY());
22266     },
22267
22268     toString: function() {
22269         return ("DD " + this.id);
22270     }
22271
22272     //////////////////////////////////////////////////////////////////////////
22273     // Debugging ygDragDrop events that can be overridden
22274     //////////////////////////////////////////////////////////////////////////
22275     /*
22276     startDrag: function(x, y) {
22277     },
22278
22279     onDrag: function(e) {
22280     },
22281
22282     onDragEnter: function(e, id) {
22283     },
22284
22285     onDragOver: function(e, id) {
22286     },
22287
22288     onDragOut: function(e, id) {
22289     },
22290
22291     onDragDrop: function(e, id) {
22292     },
22293
22294     endDrag: function(e) {
22295     }
22296
22297     */
22298
22299 });/*
22300  * Based on:
22301  * Ext JS Library 1.1.1
22302  * Copyright(c) 2006-2007, Ext JS, LLC.
22303  *
22304  * Originally Released Under LGPL - original licence link has changed is not relivant.
22305  *
22306  * Fork - LGPL
22307  * <script type="text/javascript">
22308  */
22309
22310 /**
22311  * @class Roo.dd.DDProxy
22312  * A DragDrop implementation that inserts an empty, bordered div into
22313  * the document that follows the cursor during drag operations.  At the time of
22314  * the click, the frame div is resized to the dimensions of the linked html
22315  * element, and moved to the exact location of the linked element.
22316  *
22317  * References to the "frame" element refer to the single proxy element that
22318  * was created to be dragged in place of all DDProxy elements on the
22319  * page.
22320  *
22321  * @extends Roo.dd.DD
22322  * @constructor
22323  * @param {String} id the id of the linked html element
22324  * @param {String} sGroup the group of related DragDrop objects
22325  * @param {object} config an object containing configurable attributes
22326  *                Valid properties for DDProxy in addition to those in DragDrop:
22327  *                   resizeFrame, centerFrame, dragElId
22328  */
22329 Roo.dd.DDProxy = function(id, sGroup, config) {
22330     if (id) {
22331         this.init(id, sGroup, config);
22332         this.initFrame();
22333     }
22334 };
22335
22336 /**
22337  * The default drag frame div id
22338  * @property Roo.dd.DDProxy.dragElId
22339  * @type String
22340  * @static
22341  */
22342 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22343
22344 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22345
22346     /**
22347      * By default we resize the drag frame to be the same size as the element
22348      * we want to drag (this is to get the frame effect).  We can turn it off
22349      * if we want a different behavior.
22350      * @property resizeFrame
22351      * @type boolean
22352      */
22353     resizeFrame: true,
22354
22355     /**
22356      * By default the frame is positioned exactly where the drag element is, so
22357      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22358      * you do not have constraints on the obj is to have the drag frame centered
22359      * around the cursor.  Set centerFrame to true for this effect.
22360      * @property centerFrame
22361      * @type boolean
22362      */
22363     centerFrame: false,
22364
22365     /**
22366      * Creates the proxy element if it does not yet exist
22367      * @method createFrame
22368      */
22369     createFrame: function() {
22370         var self = this;
22371         var body = document.body;
22372
22373         if (!body || !body.firstChild) {
22374             setTimeout( function() { self.createFrame(); }, 50 );
22375             return;
22376         }
22377
22378         var div = this.getDragEl();
22379
22380         if (!div) {
22381             div    = document.createElement("div");
22382             div.id = this.dragElId;
22383             var s  = div.style;
22384
22385             s.position   = "absolute";
22386             s.visibility = "hidden";
22387             s.cursor     = "move";
22388             s.border     = "2px solid #aaa";
22389             s.zIndex     = 999;
22390
22391             // appendChild can blow up IE if invoked prior to the window load event
22392             // while rendering a table.  It is possible there are other scenarios
22393             // that would cause this to happen as well.
22394             body.insertBefore(div, body.firstChild);
22395         }
22396     },
22397
22398     /**
22399      * Initialization for the drag frame element.  Must be called in the
22400      * constructor of all subclasses
22401      * @method initFrame
22402      */
22403     initFrame: function() {
22404         this.createFrame();
22405     },
22406
22407     applyConfig: function() {
22408         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22409
22410         this.resizeFrame = (this.config.resizeFrame !== false);
22411         this.centerFrame = (this.config.centerFrame);
22412         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22413     },
22414
22415     /**
22416      * Resizes the drag frame to the dimensions of the clicked object, positions
22417      * it over the object, and finally displays it
22418      * @method showFrame
22419      * @param {int} iPageX X click position
22420      * @param {int} iPageY Y click position
22421      * @private
22422      */
22423     showFrame: function(iPageX, iPageY) {
22424         var el = this.getEl();
22425         var dragEl = this.getDragEl();
22426         var s = dragEl.style;
22427
22428         this._resizeProxy();
22429
22430         if (this.centerFrame) {
22431             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22432                            Math.round(parseInt(s.height, 10)/2) );
22433         }
22434
22435         this.setDragElPos(iPageX, iPageY);
22436
22437         Roo.fly(dragEl).show();
22438     },
22439
22440     /**
22441      * The proxy is automatically resized to the dimensions of the linked
22442      * element when a drag is initiated, unless resizeFrame is set to false
22443      * @method _resizeProxy
22444      * @private
22445      */
22446     _resizeProxy: function() {
22447         if (this.resizeFrame) {
22448             var el = this.getEl();
22449             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22450         }
22451     },
22452
22453     // overrides Roo.dd.DragDrop
22454     b4MouseDown: function(e) {
22455         var x = e.getPageX();
22456         var y = e.getPageY();
22457         this.autoOffset(x, y);
22458         this.setDragElPos(x, y);
22459     },
22460
22461     // overrides Roo.dd.DragDrop
22462     b4StartDrag: function(x, y) {
22463         // show the drag frame
22464         this.showFrame(x, y);
22465     },
22466
22467     // overrides Roo.dd.DragDrop
22468     b4EndDrag: function(e) {
22469         Roo.fly(this.getDragEl()).hide();
22470     },
22471
22472     // overrides Roo.dd.DragDrop
22473     // By default we try to move the element to the last location of the frame.
22474     // This is so that the default behavior mirrors that of Roo.dd.DD.
22475     endDrag: function(e) {
22476
22477         var lel = this.getEl();
22478         var del = this.getDragEl();
22479
22480         // Show the drag frame briefly so we can get its position
22481         del.style.visibility = "";
22482
22483         this.beforeMove();
22484         // Hide the linked element before the move to get around a Safari
22485         // rendering bug.
22486         lel.style.visibility = "hidden";
22487         Roo.dd.DDM.moveToEl(lel, del);
22488         del.style.visibility = "hidden";
22489         lel.style.visibility = "";
22490
22491         this.afterDrag();
22492     },
22493
22494     beforeMove : function(){
22495
22496     },
22497
22498     afterDrag : function(){
22499
22500     },
22501
22502     toString: function() {
22503         return ("DDProxy " + this.id);
22504     }
22505
22506 });
22507 /*
22508  * Based on:
22509  * Ext JS Library 1.1.1
22510  * Copyright(c) 2006-2007, Ext JS, LLC.
22511  *
22512  * Originally Released Under LGPL - original licence link has changed is not relivant.
22513  *
22514  * Fork - LGPL
22515  * <script type="text/javascript">
22516  */
22517
22518  /**
22519  * @class Roo.dd.DDTarget
22520  * A DragDrop implementation that does not move, but can be a drop
22521  * target.  You would get the same result by simply omitting implementation
22522  * for the event callbacks, but this way we reduce the processing cost of the
22523  * event listener and the callbacks.
22524  * @extends Roo.dd.DragDrop
22525  * @constructor
22526  * @param {String} id the id of the element that is a drop target
22527  * @param {String} sGroup the group of related DragDrop objects
22528  * @param {object} config an object containing configurable attributes
22529  *                 Valid properties for DDTarget in addition to those in
22530  *                 DragDrop:
22531  *                    none
22532  */
22533 Roo.dd.DDTarget = function(id, sGroup, config) {
22534     if (id) {
22535         this.initTarget(id, sGroup, config);
22536     }
22537     if (config && (config.listeners || config.events)) { 
22538         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22539             listeners : config.listeners || {}, 
22540             events : config.events || {} 
22541         });    
22542     }
22543 };
22544
22545 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22546 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22547     toString: function() {
22548         return ("DDTarget " + this.id);
22549     }
22550 });
22551 /*
22552  * Based on:
22553  * Ext JS Library 1.1.1
22554  * Copyright(c) 2006-2007, Ext JS, LLC.
22555  *
22556  * Originally Released Under LGPL - original licence link has changed is not relivant.
22557  *
22558  * Fork - LGPL
22559  * <script type="text/javascript">
22560  */
22561  
22562
22563 /**
22564  * @class Roo.dd.ScrollManager
22565  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22566  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22567  * @static
22568  */
22569 Roo.dd.ScrollManager = function(){
22570     var ddm = Roo.dd.DragDropMgr;
22571     var els = {};
22572     var dragEl = null;
22573     var proc = {};
22574     
22575     
22576     
22577     var onStop = function(e){
22578         dragEl = null;
22579         clearProc();
22580     };
22581     
22582     var triggerRefresh = function(){
22583         if(ddm.dragCurrent){
22584              ddm.refreshCache(ddm.dragCurrent.groups);
22585         }
22586     };
22587     
22588     var doScroll = function(){
22589         if(ddm.dragCurrent){
22590             var dds = Roo.dd.ScrollManager;
22591             if(!dds.animate){
22592                 if(proc.el.scroll(proc.dir, dds.increment)){
22593                     triggerRefresh();
22594                 }
22595             }else{
22596                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22597             }
22598         }
22599     };
22600     
22601     var clearProc = function(){
22602         if(proc.id){
22603             clearInterval(proc.id);
22604         }
22605         proc.id = 0;
22606         proc.el = null;
22607         proc.dir = "";
22608     };
22609     
22610     var startProc = function(el, dir){
22611          Roo.log('scroll startproc');
22612         clearProc();
22613         proc.el = el;
22614         proc.dir = dir;
22615         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22616     };
22617     
22618     var onFire = function(e, isDrop){
22619        
22620         if(isDrop || !ddm.dragCurrent){ return; }
22621         var dds = Roo.dd.ScrollManager;
22622         if(!dragEl || dragEl != ddm.dragCurrent){
22623             dragEl = ddm.dragCurrent;
22624             // refresh regions on drag start
22625             dds.refreshCache();
22626         }
22627         
22628         var xy = Roo.lib.Event.getXY(e);
22629         var pt = new Roo.lib.Point(xy[0], xy[1]);
22630         for(var id in els){
22631             var el = els[id], r = el._region;
22632             if(r && r.contains(pt) && el.isScrollable()){
22633                 if(r.bottom - pt.y <= dds.thresh){
22634                     if(proc.el != el){
22635                         startProc(el, "down");
22636                     }
22637                     return;
22638                 }else if(r.right - pt.x <= dds.thresh){
22639                     if(proc.el != el){
22640                         startProc(el, "left");
22641                     }
22642                     return;
22643                 }else if(pt.y - r.top <= dds.thresh){
22644                     if(proc.el != el){
22645                         startProc(el, "up");
22646                     }
22647                     return;
22648                 }else if(pt.x - r.left <= dds.thresh){
22649                     if(proc.el != el){
22650                         startProc(el, "right");
22651                     }
22652                     return;
22653                 }
22654             }
22655         }
22656         clearProc();
22657     };
22658     
22659     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22660     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22661     
22662     return {
22663         /**
22664          * Registers new overflow element(s) to auto scroll
22665          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22666          */
22667         register : function(el){
22668             if(el instanceof Array){
22669                 for(var i = 0, len = el.length; i < len; i++) {
22670                         this.register(el[i]);
22671                 }
22672             }else{
22673                 el = Roo.get(el);
22674                 els[el.id] = el;
22675             }
22676             Roo.dd.ScrollManager.els = els;
22677         },
22678         
22679         /**
22680          * Unregisters overflow element(s) so they are no longer scrolled
22681          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22682          */
22683         unregister : function(el){
22684             if(el instanceof Array){
22685                 for(var i = 0, len = el.length; i < len; i++) {
22686                         this.unregister(el[i]);
22687                 }
22688             }else{
22689                 el = Roo.get(el);
22690                 delete els[el.id];
22691             }
22692         },
22693         
22694         /**
22695          * The number of pixels from the edge of a container the pointer needs to be to 
22696          * trigger scrolling (defaults to 25)
22697          * @type Number
22698          */
22699         thresh : 25,
22700         
22701         /**
22702          * The number of pixels to scroll in each scroll increment (defaults to 50)
22703          * @type Number
22704          */
22705         increment : 100,
22706         
22707         /**
22708          * The frequency of scrolls in milliseconds (defaults to 500)
22709          * @type Number
22710          */
22711         frequency : 500,
22712         
22713         /**
22714          * True to animate the scroll (defaults to true)
22715          * @type Boolean
22716          */
22717         animate: true,
22718         
22719         /**
22720          * The animation duration in seconds - 
22721          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22722          * @type Number
22723          */
22724         animDuration: .4,
22725         
22726         /**
22727          * Manually trigger a cache refresh.
22728          */
22729         refreshCache : function(){
22730             for(var id in els){
22731                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22732                     els[id]._region = els[id].getRegion();
22733                 }
22734             }
22735         }
22736     };
22737 }();/*
22738  * Based on:
22739  * Ext JS Library 1.1.1
22740  * Copyright(c) 2006-2007, Ext JS, LLC.
22741  *
22742  * Originally Released Under LGPL - original licence link has changed is not relivant.
22743  *
22744  * Fork - LGPL
22745  * <script type="text/javascript">
22746  */
22747  
22748
22749 /**
22750  * @class Roo.dd.Registry
22751  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22752  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22753  * @static
22754  */
22755 Roo.dd.Registry = function(){
22756     var elements = {}; 
22757     var handles = {}; 
22758     var autoIdSeed = 0;
22759
22760     var getId = function(el, autogen){
22761         if(typeof el == "string"){
22762             return el;
22763         }
22764         var id = el.id;
22765         if(!id && autogen !== false){
22766             id = "roodd-" + (++autoIdSeed);
22767             el.id = id;
22768         }
22769         return id;
22770     };
22771     
22772     return {
22773     /**
22774      * Register a drag drop element
22775      * @param {String|HTMLElement} element The id or DOM node to register
22776      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22777      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22778      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22779      * populated in the data object (if applicable):
22780      * <pre>
22781 Value      Description<br />
22782 ---------  ------------------------------------------<br />
22783 handles    Array of DOM nodes that trigger dragging<br />
22784            for the element being registered<br />
22785 isHandle   True if the element passed in triggers<br />
22786            dragging itself, else false
22787 </pre>
22788      */
22789         register : function(el, data){
22790             data = data || {};
22791             if(typeof el == "string"){
22792                 el = document.getElementById(el);
22793             }
22794             data.ddel = el;
22795             elements[getId(el)] = data;
22796             if(data.isHandle !== false){
22797                 handles[data.ddel.id] = data;
22798             }
22799             if(data.handles){
22800                 var hs = data.handles;
22801                 for(var i = 0, len = hs.length; i < len; i++){
22802                         handles[getId(hs[i])] = data;
22803                 }
22804             }
22805         },
22806
22807     /**
22808      * Unregister a drag drop element
22809      * @param {String|HTMLElement}  element The id or DOM node to unregister
22810      */
22811         unregister : function(el){
22812             var id = getId(el, false);
22813             var data = elements[id];
22814             if(data){
22815                 delete elements[id];
22816                 if(data.handles){
22817                     var hs = data.handles;
22818                     for(var i = 0, len = hs.length; i < len; i++){
22819                         delete handles[getId(hs[i], false)];
22820                     }
22821                 }
22822             }
22823         },
22824
22825     /**
22826      * Returns the handle registered for a DOM Node by id
22827      * @param {String|HTMLElement} id The DOM node or id to look up
22828      * @return {Object} handle The custom handle data
22829      */
22830         getHandle : function(id){
22831             if(typeof id != "string"){ // must be element?
22832                 id = id.id;
22833             }
22834             return handles[id];
22835         },
22836
22837     /**
22838      * Returns the handle that is registered for the DOM node that is the target of the event
22839      * @param {Event} e The event
22840      * @return {Object} handle The custom handle data
22841      */
22842         getHandleFromEvent : function(e){
22843             var t = Roo.lib.Event.getTarget(e);
22844             return t ? handles[t.id] : null;
22845         },
22846
22847     /**
22848      * Returns a custom data object that is registered for a DOM node by id
22849      * @param {String|HTMLElement} id The DOM node or id to look up
22850      * @return {Object} data The custom data
22851      */
22852         getTarget : function(id){
22853             if(typeof id != "string"){ // must be element?
22854                 id = id.id;
22855             }
22856             return elements[id];
22857         },
22858
22859     /**
22860      * Returns a custom data object that is registered for the DOM node that is the target of the event
22861      * @param {Event} e The event
22862      * @return {Object} data The custom data
22863      */
22864         getTargetFromEvent : function(e){
22865             var t = Roo.lib.Event.getTarget(e);
22866             return t ? elements[t.id] || handles[t.id] : null;
22867         }
22868     };
22869 }();/*
22870  * Based on:
22871  * Ext JS Library 1.1.1
22872  * Copyright(c) 2006-2007, Ext JS, LLC.
22873  *
22874  * Originally Released Under LGPL - original licence link has changed is not relivant.
22875  *
22876  * Fork - LGPL
22877  * <script type="text/javascript">
22878  */
22879  
22880
22881 /**
22882  * @class Roo.dd.StatusProxy
22883  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22884  * default drag proxy used by all Roo.dd components.
22885  * @constructor
22886  * @param {Object} config
22887  */
22888 Roo.dd.StatusProxy = function(config){
22889     Roo.apply(this, config);
22890     this.id = this.id || Roo.id();
22891     this.el = new Roo.Layer({
22892         dh: {
22893             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22894                 {tag: "div", cls: "x-dd-drop-icon"},
22895                 {tag: "div", cls: "x-dd-drag-ghost"}
22896             ]
22897         }, 
22898         shadow: !config || config.shadow !== false
22899     });
22900     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22901     this.dropStatus = this.dropNotAllowed;
22902 };
22903
22904 Roo.dd.StatusProxy.prototype = {
22905     /**
22906      * @cfg {String} dropAllowed
22907      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22908      */
22909     dropAllowed : "x-dd-drop-ok",
22910     /**
22911      * @cfg {String} dropNotAllowed
22912      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22913      */
22914     dropNotAllowed : "x-dd-drop-nodrop",
22915
22916     /**
22917      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22918      * over the current target element.
22919      * @param {String} cssClass The css class for the new drop status indicator image
22920      */
22921     setStatus : function(cssClass){
22922         cssClass = cssClass || this.dropNotAllowed;
22923         if(this.dropStatus != cssClass){
22924             this.el.replaceClass(this.dropStatus, cssClass);
22925             this.dropStatus = cssClass;
22926         }
22927     },
22928
22929     /**
22930      * Resets the status indicator to the default dropNotAllowed value
22931      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
22932      */
22933     reset : function(clearGhost){
22934         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
22935         this.dropStatus = this.dropNotAllowed;
22936         if(clearGhost){
22937             this.ghost.update("");
22938         }
22939     },
22940
22941     /**
22942      * Updates the contents of the ghost element
22943      * @param {String} html The html that will replace the current innerHTML of the ghost element
22944      */
22945     update : function(html){
22946         if(typeof html == "string"){
22947             this.ghost.update(html);
22948         }else{
22949             this.ghost.update("");
22950             html.style.margin = "0";
22951             this.ghost.dom.appendChild(html);
22952         }
22953         // ensure float = none set?? cant remember why though.
22954         var el = this.ghost.dom.firstChild;
22955                 if(el){
22956                         Roo.fly(el).setStyle('float', 'none');
22957                 }
22958     },
22959     
22960     /**
22961      * Returns the underlying proxy {@link Roo.Layer}
22962      * @return {Roo.Layer} el
22963     */
22964     getEl : function(){
22965         return this.el;
22966     },
22967
22968     /**
22969      * Returns the ghost element
22970      * @return {Roo.Element} el
22971      */
22972     getGhost : function(){
22973         return this.ghost;
22974     },
22975
22976     /**
22977      * Hides the proxy
22978      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
22979      */
22980     hide : function(clear){
22981         this.el.hide();
22982         if(clear){
22983             this.reset(true);
22984         }
22985     },
22986
22987     /**
22988      * Stops the repair animation if it's currently running
22989      */
22990     stop : function(){
22991         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
22992             this.anim.stop();
22993         }
22994     },
22995
22996     /**
22997      * Displays this proxy
22998      */
22999     show : function(){
23000         this.el.show();
23001     },
23002
23003     /**
23004      * Force the Layer to sync its shadow and shim positions to the element
23005      */
23006     sync : function(){
23007         this.el.sync();
23008     },
23009
23010     /**
23011      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23012      * invalid drop operation by the item being dragged.
23013      * @param {Array} xy The XY position of the element ([x, y])
23014      * @param {Function} callback The function to call after the repair is complete
23015      * @param {Object} scope The scope in which to execute the callback
23016      */
23017     repair : function(xy, callback, scope){
23018         this.callback = callback;
23019         this.scope = scope;
23020         if(xy && this.animRepair !== false){
23021             this.el.addClass("x-dd-drag-repair");
23022             this.el.hideUnders(true);
23023             this.anim = this.el.shift({
23024                 duration: this.repairDuration || .5,
23025                 easing: 'easeOut',
23026                 xy: xy,
23027                 stopFx: true,
23028                 callback: this.afterRepair,
23029                 scope: this
23030             });
23031         }else{
23032             this.afterRepair();
23033         }
23034     },
23035
23036     // private
23037     afterRepair : function(){
23038         this.hide(true);
23039         if(typeof this.callback == "function"){
23040             this.callback.call(this.scope || this);
23041         }
23042         this.callback = null;
23043         this.scope = null;
23044     }
23045 };/*
23046  * Based on:
23047  * Ext JS Library 1.1.1
23048  * Copyright(c) 2006-2007, Ext JS, LLC.
23049  *
23050  * Originally Released Under LGPL - original licence link has changed is not relivant.
23051  *
23052  * Fork - LGPL
23053  * <script type="text/javascript">
23054  */
23055
23056 /**
23057  * @class Roo.dd.DragSource
23058  * @extends Roo.dd.DDProxy
23059  * A simple class that provides the basic implementation needed to make any element draggable.
23060  * @constructor
23061  * @param {String/HTMLElement/Element} el The container element
23062  * @param {Object} config
23063  */
23064 Roo.dd.DragSource = function(el, config){
23065     this.el = Roo.get(el);
23066     this.dragData = {};
23067     
23068     Roo.apply(this, config);
23069     
23070     if(!this.proxy){
23071         this.proxy = new Roo.dd.StatusProxy();
23072     }
23073
23074     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23075           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23076     
23077     this.dragging = false;
23078 };
23079
23080 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23081     /**
23082      * @cfg {String} dropAllowed
23083      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23084      */
23085     dropAllowed : "x-dd-drop-ok",
23086     /**
23087      * @cfg {String} dropNotAllowed
23088      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23089      */
23090     dropNotAllowed : "x-dd-drop-nodrop",
23091
23092     /**
23093      * Returns the data object associated with this drag source
23094      * @return {Object} data An object containing arbitrary data
23095      */
23096     getDragData : function(e){
23097         return this.dragData;
23098     },
23099
23100     // private
23101     onDragEnter : function(e, id){
23102         var target = Roo.dd.DragDropMgr.getDDById(id);
23103         this.cachedTarget = target;
23104         if(this.beforeDragEnter(target, e, id) !== false){
23105             if(target.isNotifyTarget){
23106                 var status = target.notifyEnter(this, e, this.dragData);
23107                 this.proxy.setStatus(status);
23108             }else{
23109                 this.proxy.setStatus(this.dropAllowed);
23110             }
23111             
23112             if(this.afterDragEnter){
23113                 /**
23114                  * An empty function by default, but provided so that you can perform a custom action
23115                  * when the dragged item enters the drop target by providing an implementation.
23116                  * @param {Roo.dd.DragDrop} target The drop target
23117                  * @param {Event} e The event object
23118                  * @param {String} id The id of the dragged element
23119                  * @method afterDragEnter
23120                  */
23121                 this.afterDragEnter(target, e, id);
23122             }
23123         }
23124     },
23125
23126     /**
23127      * An empty function by default, but provided so that you can perform a custom action
23128      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23129      * @param {Roo.dd.DragDrop} target The drop target
23130      * @param {Event} e The event object
23131      * @param {String} id The id of the dragged element
23132      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23133      */
23134     beforeDragEnter : function(target, e, id){
23135         return true;
23136     },
23137
23138     // private
23139     alignElWithMouse: function() {
23140         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23141         this.proxy.sync();
23142     },
23143
23144     // private
23145     onDragOver : function(e, id){
23146         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23147         if(this.beforeDragOver(target, e, id) !== false){
23148             if(target.isNotifyTarget){
23149                 var status = target.notifyOver(this, e, this.dragData);
23150                 this.proxy.setStatus(status);
23151             }
23152
23153             if(this.afterDragOver){
23154                 /**
23155                  * An empty function by default, but provided so that you can perform a custom action
23156                  * while the dragged item is over the drop target by providing an implementation.
23157                  * @param {Roo.dd.DragDrop} target The drop target
23158                  * @param {Event} e The event object
23159                  * @param {String} id The id of the dragged element
23160                  * @method afterDragOver
23161                  */
23162                 this.afterDragOver(target, e, id);
23163             }
23164         }
23165     },
23166
23167     /**
23168      * An empty function by default, but provided so that you can perform a custom action
23169      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23170      * @param {Roo.dd.DragDrop} target The drop target
23171      * @param {Event} e The event object
23172      * @param {String} id The id of the dragged element
23173      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23174      */
23175     beforeDragOver : function(target, e, id){
23176         return true;
23177     },
23178
23179     // private
23180     onDragOut : function(e, id){
23181         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23182         if(this.beforeDragOut(target, e, id) !== false){
23183             if(target.isNotifyTarget){
23184                 target.notifyOut(this, e, this.dragData);
23185             }
23186             this.proxy.reset();
23187             if(this.afterDragOut){
23188                 /**
23189                  * An empty function by default, but provided so that you can perform a custom action
23190                  * after the dragged item is dragged out of the target without dropping.
23191                  * @param {Roo.dd.DragDrop} target The drop target
23192                  * @param {Event} e The event object
23193                  * @param {String} id The id of the dragged element
23194                  * @method afterDragOut
23195                  */
23196                 this.afterDragOut(target, e, id);
23197             }
23198         }
23199         this.cachedTarget = null;
23200     },
23201
23202     /**
23203      * An empty function by default, but provided so that you can perform a custom action before the dragged
23204      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23205      * @param {Roo.dd.DragDrop} target The drop target
23206      * @param {Event} e The event object
23207      * @param {String} id The id of the dragged element
23208      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23209      */
23210     beforeDragOut : function(target, e, id){
23211         return true;
23212     },
23213     
23214     // private
23215     onDragDrop : function(e, id){
23216         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23217         if(this.beforeDragDrop(target, e, id) !== false){
23218             if(target.isNotifyTarget){
23219                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23220                     this.onValidDrop(target, e, id);
23221                 }else{
23222                     this.onInvalidDrop(target, e, id);
23223                 }
23224             }else{
23225                 this.onValidDrop(target, e, id);
23226             }
23227             
23228             if(this.afterDragDrop){
23229                 /**
23230                  * An empty function by default, but provided so that you can perform a custom action
23231                  * after a valid drag drop has occurred by providing an implementation.
23232                  * @param {Roo.dd.DragDrop} target The drop target
23233                  * @param {Event} e The event object
23234                  * @param {String} id The id of the dropped element
23235                  * @method afterDragDrop
23236                  */
23237                 this.afterDragDrop(target, e, id);
23238             }
23239         }
23240         delete this.cachedTarget;
23241     },
23242
23243     /**
23244      * An empty function by default, but provided so that you can perform a custom action before the dragged
23245      * item is dropped onto the target and optionally cancel the onDragDrop.
23246      * @param {Roo.dd.DragDrop} target The drop target
23247      * @param {Event} e The event object
23248      * @param {String} id The id of the dragged element
23249      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23250      */
23251     beforeDragDrop : function(target, e, id){
23252         return true;
23253     },
23254
23255     // private
23256     onValidDrop : function(target, e, id){
23257         this.hideProxy();
23258         if(this.afterValidDrop){
23259             /**
23260              * An empty function by default, but provided so that you can perform a custom action
23261              * after a valid drop has occurred by providing an implementation.
23262              * @param {Object} target The target DD 
23263              * @param {Event} e The event object
23264              * @param {String} id The id of the dropped element
23265              * @method afterInvalidDrop
23266              */
23267             this.afterValidDrop(target, e, id);
23268         }
23269     },
23270
23271     // private
23272     getRepairXY : function(e, data){
23273         return this.el.getXY();  
23274     },
23275
23276     // private
23277     onInvalidDrop : function(target, e, id){
23278         this.beforeInvalidDrop(target, e, id);
23279         if(this.cachedTarget){
23280             if(this.cachedTarget.isNotifyTarget){
23281                 this.cachedTarget.notifyOut(this, e, this.dragData);
23282             }
23283             this.cacheTarget = null;
23284         }
23285         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23286
23287         if(this.afterInvalidDrop){
23288             /**
23289              * An empty function by default, but provided so that you can perform a custom action
23290              * after an invalid drop has occurred by providing an implementation.
23291              * @param {Event} e The event object
23292              * @param {String} id The id of the dropped element
23293              * @method afterInvalidDrop
23294              */
23295             this.afterInvalidDrop(e, id);
23296         }
23297     },
23298
23299     // private
23300     afterRepair : function(){
23301         if(Roo.enableFx){
23302             this.el.highlight(this.hlColor || "c3daf9");
23303         }
23304         this.dragging = false;
23305     },
23306
23307     /**
23308      * An empty function by default, but provided so that you can perform a custom action after an invalid
23309      * drop has occurred.
23310      * @param {Roo.dd.DragDrop} target The drop target
23311      * @param {Event} e The event object
23312      * @param {String} id The id of the dragged element
23313      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23314      */
23315     beforeInvalidDrop : function(target, e, id){
23316         return true;
23317     },
23318
23319     // private
23320     handleMouseDown : function(e){
23321         if(this.dragging) {
23322             return;
23323         }
23324         var data = this.getDragData(e);
23325         if(data && this.onBeforeDrag(data, e) !== false){
23326             this.dragData = data;
23327             this.proxy.stop();
23328             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23329         } 
23330     },
23331
23332     /**
23333      * An empty function by default, but provided so that you can perform a custom action before the initial
23334      * drag event begins and optionally cancel it.
23335      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23336      * @param {Event} e The event object
23337      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23338      */
23339     onBeforeDrag : function(data, e){
23340         return true;
23341     },
23342
23343     /**
23344      * An empty function by default, but provided so that you can perform a custom action once the initial
23345      * drag event has begun.  The drag cannot be canceled from this function.
23346      * @param {Number} x The x position of the click on the dragged object
23347      * @param {Number} y The y position of the click on the dragged object
23348      */
23349     onStartDrag : Roo.emptyFn,
23350
23351     // private - YUI override
23352     startDrag : function(x, y){
23353         this.proxy.reset();
23354         this.dragging = true;
23355         this.proxy.update("");
23356         this.onInitDrag(x, y);
23357         this.proxy.show();
23358     },
23359
23360     // private
23361     onInitDrag : function(x, y){
23362         var clone = this.el.dom.cloneNode(true);
23363         clone.id = Roo.id(); // prevent duplicate ids
23364         this.proxy.update(clone);
23365         this.onStartDrag(x, y);
23366         return true;
23367     },
23368
23369     /**
23370      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23371      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23372      */
23373     getProxy : function(){
23374         return this.proxy;  
23375     },
23376
23377     /**
23378      * Hides the drag source's {@link Roo.dd.StatusProxy}
23379      */
23380     hideProxy : function(){
23381         this.proxy.hide();  
23382         this.proxy.reset(true);
23383         this.dragging = false;
23384     },
23385
23386     // private
23387     triggerCacheRefresh : function(){
23388         Roo.dd.DDM.refreshCache(this.groups);
23389     },
23390
23391     // private - override to prevent hiding
23392     b4EndDrag: function(e) {
23393     },
23394
23395     // private - override to prevent moving
23396     endDrag : function(e){
23397         this.onEndDrag(this.dragData, e);
23398     },
23399
23400     // private
23401     onEndDrag : function(data, e){
23402     },
23403     
23404     // private - pin to cursor
23405     autoOffset : function(x, y) {
23406         this.setDelta(-12, -20);
23407     }    
23408 });/*
23409  * Based on:
23410  * Ext JS Library 1.1.1
23411  * Copyright(c) 2006-2007, Ext JS, LLC.
23412  *
23413  * Originally Released Under LGPL - original licence link has changed is not relivant.
23414  *
23415  * Fork - LGPL
23416  * <script type="text/javascript">
23417  */
23418
23419
23420 /**
23421  * @class Roo.dd.DropTarget
23422  * @extends Roo.dd.DDTarget
23423  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23424  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23425  * @constructor
23426  * @param {String/HTMLElement/Element} el The container element
23427  * @param {Object} config
23428  */
23429 Roo.dd.DropTarget = function(el, config){
23430     this.el = Roo.get(el);
23431     
23432     var listeners = false; ;
23433     if (config && config.listeners) {
23434         listeners= config.listeners;
23435         delete config.listeners;
23436     }
23437     Roo.apply(this, config);
23438     
23439     if(this.containerScroll){
23440         Roo.dd.ScrollManager.register(this.el);
23441     }
23442     this.addEvents( {
23443          /**
23444          * @scope Roo.dd.DropTarget
23445          */
23446          
23447          /**
23448          * @event enter
23449          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23450          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23451          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23452          * 
23453          * IMPORTANT : it should set  this.valid to true|false
23454          * 
23455          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23456          * @param {Event} e The event
23457          * @param {Object} data An object containing arbitrary data supplied by the drag source
23458          */
23459         "enter" : true,
23460         
23461          /**
23462          * @event over
23463          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23464          * This method will be called on every mouse movement while the drag source is over the drop target.
23465          * This default implementation simply returns the dropAllowed config value.
23466          * 
23467          * IMPORTANT : it should set  this.valid to true|false
23468          * 
23469          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23470          * @param {Event} e The event
23471          * @param {Object} data An object containing arbitrary data supplied by the drag source
23472          
23473          */
23474         "over" : true,
23475         /**
23476          * @event out
23477          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23478          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23479          * overClass (if any) from the drop element.
23480          * 
23481          * 
23482          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23483          * @param {Event} e The event
23484          * @param {Object} data An object containing arbitrary data supplied by the drag source
23485          */
23486          "out" : true,
23487          
23488         /**
23489          * @event drop
23490          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23491          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23492          * implementation that does something to process the drop event and returns true so that the drag source's
23493          * repair action does not run.
23494          * 
23495          * IMPORTANT : it should set this.success
23496          * 
23497          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23498          * @param {Event} e The event
23499          * @param {Object} data An object containing arbitrary data supplied by the drag source
23500         */
23501          "drop" : true
23502     });
23503             
23504      
23505     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23506         this.el.dom, 
23507         this.ddGroup || this.group,
23508         {
23509             isTarget: true,
23510             listeners : listeners || {} 
23511            
23512         
23513         }
23514     );
23515
23516 };
23517
23518 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23519     /**
23520      * @cfg {String} overClass
23521      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23522      */
23523      /**
23524      * @cfg {String} ddGroup
23525      * The drag drop group to handle drop events for
23526      */
23527      
23528     /**
23529      * @cfg {String} dropAllowed
23530      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23531      */
23532     dropAllowed : "x-dd-drop-ok",
23533     /**
23534      * @cfg {String} dropNotAllowed
23535      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23536      */
23537     dropNotAllowed : "x-dd-drop-nodrop",
23538     /**
23539      * @cfg {boolean} success
23540      * set this after drop listener.. 
23541      */
23542     success : false,
23543     /**
23544      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23545      * if the drop point is valid for over/enter..
23546      */
23547     valid : false,
23548     // private
23549     isTarget : true,
23550
23551     // private
23552     isNotifyTarget : true,
23553     
23554     /**
23555      * @hide
23556      */
23557     notifyEnter : function(dd, e, data)
23558     {
23559         this.valid = true;
23560         this.fireEvent('enter', dd, e, data);
23561         if(this.overClass){
23562             this.el.addClass(this.overClass);
23563         }
23564         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23565             this.valid ? this.dropAllowed : this.dropNotAllowed
23566         );
23567     },
23568
23569     /**
23570      * @hide
23571      */
23572     notifyOver : function(dd, e, data)
23573     {
23574         this.valid = true;
23575         this.fireEvent('over', dd, e, data);
23576         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23577             this.valid ? this.dropAllowed : this.dropNotAllowed
23578         );
23579     },
23580
23581     /**
23582      * @hide
23583      */
23584     notifyOut : function(dd, e, data)
23585     {
23586         this.fireEvent('out', dd, e, data);
23587         if(this.overClass){
23588             this.el.removeClass(this.overClass);
23589         }
23590     },
23591
23592     /**
23593      * @hide
23594      */
23595     notifyDrop : function(dd, e, data)
23596     {
23597         this.success = false;
23598         this.fireEvent('drop', dd, e, data);
23599         return this.success;
23600     }
23601 });/*
23602  * Based on:
23603  * Ext JS Library 1.1.1
23604  * Copyright(c) 2006-2007, Ext JS, LLC.
23605  *
23606  * Originally Released Under LGPL - original licence link has changed is not relivant.
23607  *
23608  * Fork - LGPL
23609  * <script type="text/javascript">
23610  */
23611
23612
23613 /**
23614  * @class Roo.dd.DragZone
23615  * @extends Roo.dd.DragSource
23616  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23617  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23618  * @constructor
23619  * @param {String/HTMLElement/Element} el The container element
23620  * @param {Object} config
23621  */
23622 Roo.dd.DragZone = function(el, config){
23623     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23624     if(this.containerScroll){
23625         Roo.dd.ScrollManager.register(this.el);
23626     }
23627 };
23628
23629 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23630     /**
23631      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23632      * for auto scrolling during drag operations.
23633      */
23634     /**
23635      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23636      * method after a failed drop (defaults to "c3daf9" - light blue)
23637      */
23638
23639     /**
23640      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23641      * for a valid target to drag based on the mouse down. Override this method
23642      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23643      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23644      * @param {EventObject} e The mouse down event
23645      * @return {Object} The dragData
23646      */
23647     getDragData : function(e){
23648         return Roo.dd.Registry.getHandleFromEvent(e);
23649     },
23650     
23651     /**
23652      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23653      * this.dragData.ddel
23654      * @param {Number} x The x position of the click on the dragged object
23655      * @param {Number} y The y position of the click on the dragged object
23656      * @return {Boolean} true to continue the drag, false to cancel
23657      */
23658     onInitDrag : function(x, y){
23659         this.proxy.update(this.dragData.ddel.cloneNode(true));
23660         this.onStartDrag(x, y);
23661         return true;
23662     },
23663     
23664     /**
23665      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23666      */
23667     afterRepair : function(){
23668         if(Roo.enableFx){
23669             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23670         }
23671         this.dragging = false;
23672     },
23673
23674     /**
23675      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23676      * the XY of this.dragData.ddel
23677      * @param {EventObject} e The mouse up event
23678      * @return {Array} The xy location (e.g. [100, 200])
23679      */
23680     getRepairXY : function(e){
23681         return Roo.Element.fly(this.dragData.ddel).getXY();  
23682     }
23683 });/*
23684  * Based on:
23685  * Ext JS Library 1.1.1
23686  * Copyright(c) 2006-2007, Ext JS, LLC.
23687  *
23688  * Originally Released Under LGPL - original licence link has changed is not relivant.
23689  *
23690  * Fork - LGPL
23691  * <script type="text/javascript">
23692  */
23693 /**
23694  * @class Roo.dd.DropZone
23695  * @extends Roo.dd.DropTarget
23696  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23697  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23698  * @constructor
23699  * @param {String/HTMLElement/Element} el The container element
23700  * @param {Object} config
23701  */
23702 Roo.dd.DropZone = function(el, config){
23703     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23704 };
23705
23706 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23707     /**
23708      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23709      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23710      * provide your own custom lookup.
23711      * @param {Event} e The event
23712      * @return {Object} data The custom data
23713      */
23714     getTargetFromEvent : function(e){
23715         return Roo.dd.Registry.getTargetFromEvent(e);
23716     },
23717
23718     /**
23719      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23720      * that it has registered.  This method has no default implementation and should be overridden to provide
23721      * node-specific processing if necessary.
23722      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23723      * {@link #getTargetFromEvent} for this node)
23724      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23725      * @param {Event} e The event
23726      * @param {Object} data An object containing arbitrary data supplied by the drag source
23727      */
23728     onNodeEnter : function(n, dd, e, data){
23729         
23730     },
23731
23732     /**
23733      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23734      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23735      * overridden to provide the proper feedback.
23736      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23737      * {@link #getTargetFromEvent} for this node)
23738      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23739      * @param {Event} e The event
23740      * @param {Object} data An object containing arbitrary data supplied by the drag source
23741      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23742      * underlying {@link Roo.dd.StatusProxy} can be updated
23743      */
23744     onNodeOver : function(n, dd, e, data){
23745         return this.dropAllowed;
23746     },
23747
23748     /**
23749      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23750      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23751      * node-specific processing if necessary.
23752      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23753      * {@link #getTargetFromEvent} for this node)
23754      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23755      * @param {Event} e The event
23756      * @param {Object} data An object containing arbitrary data supplied by the drag source
23757      */
23758     onNodeOut : function(n, dd, e, data){
23759         
23760     },
23761
23762     /**
23763      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23764      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23765      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23766      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23767      * {@link #getTargetFromEvent} for this node)
23768      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23769      * @param {Event} e The event
23770      * @param {Object} data An object containing arbitrary data supplied by the drag source
23771      * @return {Boolean} True if the drop was valid, else false
23772      */
23773     onNodeDrop : function(n, dd, e, data){
23774         return false;
23775     },
23776
23777     /**
23778      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23779      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23780      * it should be overridden to provide the proper feedback if necessary.
23781      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23782      * @param {Event} e The event
23783      * @param {Object} data An object containing arbitrary data supplied by the drag source
23784      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23785      * underlying {@link Roo.dd.StatusProxy} can be updated
23786      */
23787     onContainerOver : function(dd, e, data){
23788         return this.dropNotAllowed;
23789     },
23790
23791     /**
23792      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23793      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23794      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23795      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23796      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23797      * @param {Event} e The event
23798      * @param {Object} data An object containing arbitrary data supplied by the drag source
23799      * @return {Boolean} True if the drop was valid, else false
23800      */
23801     onContainerDrop : function(dd, e, data){
23802         return false;
23803     },
23804
23805     /**
23806      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23807      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23808      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23809      * you should override this method and provide a custom implementation.
23810      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23811      * @param {Event} e The event
23812      * @param {Object} data An object containing arbitrary data supplied by the drag source
23813      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23814      * underlying {@link Roo.dd.StatusProxy} can be updated
23815      */
23816     notifyEnter : function(dd, e, data){
23817         return this.dropNotAllowed;
23818     },
23819
23820     /**
23821      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23822      * This method will be called on every mouse movement while the drag source is over the drop zone.
23823      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23824      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23825      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23826      * registered node, it will call {@link #onContainerOver}.
23827      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23828      * @param {Event} e The event
23829      * @param {Object} data An object containing arbitrary data supplied by the drag source
23830      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23831      * underlying {@link Roo.dd.StatusProxy} can be updated
23832      */
23833     notifyOver : function(dd, e, data){
23834         var n = this.getTargetFromEvent(e);
23835         if(!n){ // not over valid drop target
23836             if(this.lastOverNode){
23837                 this.onNodeOut(this.lastOverNode, dd, e, data);
23838                 this.lastOverNode = null;
23839             }
23840             return this.onContainerOver(dd, e, data);
23841         }
23842         if(this.lastOverNode != n){
23843             if(this.lastOverNode){
23844                 this.onNodeOut(this.lastOverNode, dd, e, data);
23845             }
23846             this.onNodeEnter(n, dd, e, data);
23847             this.lastOverNode = n;
23848         }
23849         return this.onNodeOver(n, dd, e, data);
23850     },
23851
23852     /**
23853      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23854      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23855      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23856      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23857      * @param {Event} e The event
23858      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23859      */
23860     notifyOut : function(dd, e, data){
23861         if(this.lastOverNode){
23862             this.onNodeOut(this.lastOverNode, dd, e, data);
23863             this.lastOverNode = null;
23864         }
23865     },
23866
23867     /**
23868      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23869      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23870      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23871      * otherwise it will call {@link #onContainerDrop}.
23872      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23873      * @param {Event} e The event
23874      * @param {Object} data An object containing arbitrary data supplied by the drag source
23875      * @return {Boolean} True if the drop was valid, else false
23876      */
23877     notifyDrop : function(dd, e, data){
23878         if(this.lastOverNode){
23879             this.onNodeOut(this.lastOverNode, dd, e, data);
23880             this.lastOverNode = null;
23881         }
23882         var n = this.getTargetFromEvent(e);
23883         return n ?
23884             this.onNodeDrop(n, dd, e, data) :
23885             this.onContainerDrop(dd, e, data);
23886     },
23887
23888     // private
23889     triggerCacheRefresh : function(){
23890         Roo.dd.DDM.refreshCache(this.groups);
23891     }  
23892 });/*
23893  * Based on:
23894  * Ext JS Library 1.1.1
23895  * Copyright(c) 2006-2007, Ext JS, LLC.
23896  *
23897  * Originally Released Under LGPL - original licence link has changed is not relivant.
23898  *
23899  * Fork - LGPL
23900  * <script type="text/javascript">
23901  */
23902
23903
23904 /**
23905  * @class Roo.data.SortTypes
23906  * @static
23907  * Defines the default sorting (casting?) comparison functions used when sorting data.
23908  */
23909 Roo.data.SortTypes = {
23910     /**
23911      * Default sort that does nothing
23912      * @param {Mixed} s The value being converted
23913      * @return {Mixed} The comparison value
23914      */
23915     none : function(s){
23916         return s;
23917     },
23918     
23919     /**
23920      * The regular expression used to strip tags
23921      * @type {RegExp}
23922      * @property
23923      */
23924     stripTagsRE : /<\/?[^>]+>/gi,
23925     
23926     /**
23927      * Strips all HTML tags to sort on text only
23928      * @param {Mixed} s The value being converted
23929      * @return {String} The comparison value
23930      */
23931     asText : function(s){
23932         return String(s).replace(this.stripTagsRE, "");
23933     },
23934     
23935     /**
23936      * Strips all HTML tags to sort on text only - Case insensitive
23937      * @param {Mixed} s The value being converted
23938      * @return {String} The comparison value
23939      */
23940     asUCText : function(s){
23941         return String(s).toUpperCase().replace(this.stripTagsRE, "");
23942     },
23943     
23944     /**
23945      * Case insensitive string
23946      * @param {Mixed} s The value being converted
23947      * @return {String} The comparison value
23948      */
23949     asUCString : function(s) {
23950         return String(s).toUpperCase();
23951     },
23952     
23953     /**
23954      * Date sorting
23955      * @param {Mixed} s The value being converted
23956      * @return {Number} The comparison value
23957      */
23958     asDate : function(s) {
23959         if(!s){
23960             return 0;
23961         }
23962         if(s instanceof Date){
23963             return s.getTime();
23964         }
23965         return Date.parse(String(s));
23966     },
23967     
23968     /**
23969      * Float sorting
23970      * @param {Mixed} s The value being converted
23971      * @return {Float} The comparison value
23972      */
23973     asFloat : function(s) {
23974         var val = parseFloat(String(s).replace(/,/g, ""));
23975         if(isNaN(val)) {
23976             val = 0;
23977         }
23978         return val;
23979     },
23980     
23981     /**
23982      * Integer sorting
23983      * @param {Mixed} s The value being converted
23984      * @return {Number} The comparison value
23985      */
23986     asInt : function(s) {
23987         var val = parseInt(String(s).replace(/,/g, ""));
23988         if(isNaN(val)) {
23989             val = 0;
23990         }
23991         return val;
23992     }
23993 };/*
23994  * Based on:
23995  * Ext JS Library 1.1.1
23996  * Copyright(c) 2006-2007, Ext JS, LLC.
23997  *
23998  * Originally Released Under LGPL - original licence link has changed is not relivant.
23999  *
24000  * Fork - LGPL
24001  * <script type="text/javascript">
24002  */
24003
24004 /**
24005 * @class Roo.data.Record
24006  * Instances of this class encapsulate both record <em>definition</em> information, and record
24007  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24008  * to access Records cached in an {@link Roo.data.Store} object.<br>
24009  * <p>
24010  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24011  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24012  * objects.<br>
24013  * <p>
24014  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24015  * @constructor
24016  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24017  * {@link #create}. The parameters are the same.
24018  * @param {Array} data An associative Array of data values keyed by the field name.
24019  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24020  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24021  * not specified an integer id is generated.
24022  */
24023 Roo.data.Record = function(data, id){
24024     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24025     this.data = data;
24026 };
24027
24028 /**
24029  * Generate a constructor for a specific record layout.
24030  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24031  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24032  * Each field definition object may contain the following properties: <ul>
24033  * <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,
24034  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24035  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24036  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24037  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24038  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24039  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24040  * this may be omitted.</p></li>
24041  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24042  * <ul><li>auto (Default, implies no conversion)</li>
24043  * <li>string</li>
24044  * <li>int</li>
24045  * <li>float</li>
24046  * <li>boolean</li>
24047  * <li>date</li></ul></p></li>
24048  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24049  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24050  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24051  * by the Reader into an object that will be stored in the Record. It is passed the
24052  * following parameters:<ul>
24053  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24054  * </ul></p></li>
24055  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24056  * </ul>
24057  * <br>usage:<br><pre><code>
24058 var TopicRecord = Roo.data.Record.create(
24059     {name: 'title', mapping: 'topic_title'},
24060     {name: 'author', mapping: 'username'},
24061     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24062     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24063     {name: 'lastPoster', mapping: 'user2'},
24064     {name: 'excerpt', mapping: 'post_text'}
24065 );
24066
24067 var myNewRecord = new TopicRecord({
24068     title: 'Do my job please',
24069     author: 'noobie',
24070     totalPosts: 1,
24071     lastPost: new Date(),
24072     lastPoster: 'Animal',
24073     excerpt: 'No way dude!'
24074 });
24075 myStore.add(myNewRecord);
24076 </code></pre>
24077  * @method create
24078  * @static
24079  */
24080 Roo.data.Record.create = function(o){
24081     var f = function(){
24082         f.superclass.constructor.apply(this, arguments);
24083     };
24084     Roo.extend(f, Roo.data.Record);
24085     var p = f.prototype;
24086     p.fields = new Roo.util.MixedCollection(false, function(field){
24087         return field.name;
24088     });
24089     for(var i = 0, len = o.length; i < len; i++){
24090         p.fields.add(new Roo.data.Field(o[i]));
24091     }
24092     f.getField = function(name){
24093         return p.fields.get(name);  
24094     };
24095     return f;
24096 };
24097
24098 Roo.data.Record.AUTO_ID = 1000;
24099 Roo.data.Record.EDIT = 'edit';
24100 Roo.data.Record.REJECT = 'reject';
24101 Roo.data.Record.COMMIT = 'commit';
24102
24103 Roo.data.Record.prototype = {
24104     /**
24105      * Readonly flag - true if this record has been modified.
24106      * @type Boolean
24107      */
24108     dirty : false,
24109     editing : false,
24110     error: null,
24111     modified: null,
24112
24113     // private
24114     join : function(store){
24115         this.store = store;
24116     },
24117
24118     /**
24119      * Set the named field to the specified value.
24120      * @param {String} name The name of the field to set.
24121      * @param {Object} value The value to set the field to.
24122      */
24123     set : function(name, value){
24124         if(this.data[name] == value){
24125             return;
24126         }
24127         this.dirty = true;
24128         if(!this.modified){
24129             this.modified = {};
24130         }
24131         if(typeof this.modified[name] == 'undefined'){
24132             this.modified[name] = this.data[name];
24133         }
24134         this.data[name] = value;
24135         if(!this.editing && this.store){
24136             this.store.afterEdit(this);
24137         }       
24138     },
24139
24140     /**
24141      * Get the value of the named field.
24142      * @param {String} name The name of the field to get the value of.
24143      * @return {Object} The value of the field.
24144      */
24145     get : function(name){
24146         return this.data[name]; 
24147     },
24148
24149     // private
24150     beginEdit : function(){
24151         this.editing = true;
24152         this.modified = {}; 
24153     },
24154
24155     // private
24156     cancelEdit : function(){
24157         this.editing = false;
24158         delete this.modified;
24159     },
24160
24161     // private
24162     endEdit : function(){
24163         this.editing = false;
24164         if(this.dirty && this.store){
24165             this.store.afterEdit(this);
24166         }
24167     },
24168
24169     /**
24170      * Usually called by the {@link Roo.data.Store} which owns the Record.
24171      * Rejects all changes made to the Record since either creation, or the last commit operation.
24172      * Modified fields are reverted to their original values.
24173      * <p>
24174      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24175      * of reject operations.
24176      */
24177     reject : function(){
24178         var m = this.modified;
24179         for(var n in m){
24180             if(typeof m[n] != "function"){
24181                 this.data[n] = m[n];
24182             }
24183         }
24184         this.dirty = false;
24185         delete this.modified;
24186         this.editing = false;
24187         if(this.store){
24188             this.store.afterReject(this);
24189         }
24190     },
24191
24192     /**
24193      * Usually called by the {@link Roo.data.Store} which owns the Record.
24194      * Commits all changes made to the Record since either creation, or the last commit operation.
24195      * <p>
24196      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24197      * of commit operations.
24198      */
24199     commit : function(){
24200         this.dirty = false;
24201         delete this.modified;
24202         this.editing = false;
24203         if(this.store){
24204             this.store.afterCommit(this);
24205         }
24206     },
24207
24208     // private
24209     hasError : function(){
24210         return this.error != null;
24211     },
24212
24213     // private
24214     clearError : function(){
24215         this.error = null;
24216     },
24217
24218     /**
24219      * Creates a copy of this record.
24220      * @param {String} id (optional) A new record id if you don't want to use this record's id
24221      * @return {Record}
24222      */
24223     copy : function(newId) {
24224         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24225     }
24226 };/*
24227  * Based on:
24228  * Ext JS Library 1.1.1
24229  * Copyright(c) 2006-2007, Ext JS, LLC.
24230  *
24231  * Originally Released Under LGPL - original licence link has changed is not relivant.
24232  *
24233  * Fork - LGPL
24234  * <script type="text/javascript">
24235  */
24236
24237
24238
24239 /**
24240  * @class Roo.data.Store
24241  * @extends Roo.util.Observable
24242  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24243  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24244  * <p>
24245  * 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
24246  * has no knowledge of the format of the data returned by the Proxy.<br>
24247  * <p>
24248  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24249  * instances from the data object. These records are cached and made available through accessor functions.
24250  * @constructor
24251  * Creates a new Store.
24252  * @param {Object} config A config object containing the objects needed for the Store to access data,
24253  * and read the data into Records.
24254  */
24255 Roo.data.Store = function(config){
24256     this.data = new Roo.util.MixedCollection(false);
24257     this.data.getKey = function(o){
24258         return o.id;
24259     };
24260     this.baseParams = {};
24261     // private
24262     this.paramNames = {
24263         "start" : "start",
24264         "limit" : "limit",
24265         "sort" : "sort",
24266         "dir" : "dir",
24267         "multisort" : "_multisort"
24268     };
24269
24270     if(config && config.data){
24271         this.inlineData = config.data;
24272         delete config.data;
24273     }
24274
24275     Roo.apply(this, config);
24276     
24277     if(this.reader){ // reader passed
24278         this.reader = Roo.factory(this.reader, Roo.data);
24279         this.reader.xmodule = this.xmodule || false;
24280         if(!this.recordType){
24281             this.recordType = this.reader.recordType;
24282         }
24283         if(this.reader.onMetaChange){
24284             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24285         }
24286     }
24287
24288     if(this.recordType){
24289         this.fields = this.recordType.prototype.fields;
24290     }
24291     this.modified = [];
24292
24293     this.addEvents({
24294         /**
24295          * @event datachanged
24296          * Fires when the data cache has changed, and a widget which is using this Store
24297          * as a Record cache should refresh its view.
24298          * @param {Store} this
24299          */
24300         datachanged : true,
24301         /**
24302          * @event metachange
24303          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24304          * @param {Store} this
24305          * @param {Object} meta The JSON metadata
24306          */
24307         metachange : true,
24308         /**
24309          * @event add
24310          * Fires when Records have been added to the Store
24311          * @param {Store} this
24312          * @param {Roo.data.Record[]} records The array of Records added
24313          * @param {Number} index The index at which the record(s) were added
24314          */
24315         add : true,
24316         /**
24317          * @event remove
24318          * Fires when a Record has been removed from the Store
24319          * @param {Store} this
24320          * @param {Roo.data.Record} record The Record that was removed
24321          * @param {Number} index The index at which the record was removed
24322          */
24323         remove : true,
24324         /**
24325          * @event update
24326          * Fires when a Record has been updated
24327          * @param {Store} this
24328          * @param {Roo.data.Record} record The Record that was updated
24329          * @param {String} operation The update operation being performed.  Value may be one of:
24330          * <pre><code>
24331  Roo.data.Record.EDIT
24332  Roo.data.Record.REJECT
24333  Roo.data.Record.COMMIT
24334          * </code></pre>
24335          */
24336         update : true,
24337         /**
24338          * @event clear
24339          * Fires when the data cache has been cleared.
24340          * @param {Store} this
24341          */
24342         clear : true,
24343         /**
24344          * @event beforeload
24345          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24346          * the load action will be canceled.
24347          * @param {Store} this
24348          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24349          */
24350         beforeload : true,
24351         /**
24352          * @event beforeloadadd
24353          * Fires after a new set of Records has been loaded.
24354          * @param {Store} this
24355          * @param {Roo.data.Record[]} records The Records that were loaded
24356          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24357          */
24358         beforeloadadd : true,
24359         /**
24360          * @event load
24361          * Fires after a new set of Records has been loaded, before they are added to the store.
24362          * @param {Store} this
24363          * @param {Roo.data.Record[]} records The Records that were loaded
24364          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24365          * @params {Object} return from reader
24366          */
24367         load : true,
24368         /**
24369          * @event loadexception
24370          * Fires if an exception occurs in the Proxy during loading.
24371          * Called with the signature of the Proxy's "loadexception" event.
24372          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24373          * 
24374          * @param {Proxy} 
24375          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24376          * @param {Object} load options 
24377          * @param {Object} jsonData from your request (normally this contains the Exception)
24378          */
24379         loadexception : true
24380     });
24381     
24382     if(this.proxy){
24383         this.proxy = Roo.factory(this.proxy, Roo.data);
24384         this.proxy.xmodule = this.xmodule || false;
24385         this.relayEvents(this.proxy,  ["loadexception"]);
24386     }
24387     this.sortToggle = {};
24388     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24389
24390     Roo.data.Store.superclass.constructor.call(this);
24391
24392     if(this.inlineData){
24393         this.loadData(this.inlineData);
24394         delete this.inlineData;
24395     }
24396 };
24397
24398 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24399      /**
24400     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24401     * without a remote query - used by combo/forms at present.
24402     */
24403     
24404     /**
24405     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24406     */
24407     /**
24408     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24409     */
24410     /**
24411     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24412     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24413     */
24414     /**
24415     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24416     * on any HTTP request
24417     */
24418     /**
24419     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24420     */
24421     /**
24422     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24423     */
24424     multiSort: false,
24425     /**
24426     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24427     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24428     */
24429     remoteSort : false,
24430
24431     /**
24432     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24433      * loaded or when a record is removed. (defaults to false).
24434     */
24435     pruneModifiedRecords : false,
24436
24437     // private
24438     lastOptions : null,
24439
24440     /**
24441      * Add Records to the Store and fires the add event.
24442      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24443      */
24444     add : function(records){
24445         records = [].concat(records);
24446         for(var i = 0, len = records.length; i < len; i++){
24447             records[i].join(this);
24448         }
24449         var index = this.data.length;
24450         this.data.addAll(records);
24451         this.fireEvent("add", this, records, index);
24452     },
24453
24454     /**
24455      * Remove a Record from the Store and fires the remove event.
24456      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24457      */
24458     remove : function(record){
24459         var index = this.data.indexOf(record);
24460         this.data.removeAt(index);
24461  
24462         if(this.pruneModifiedRecords){
24463             this.modified.remove(record);
24464         }
24465         this.fireEvent("remove", this, record, index);
24466     },
24467
24468     /**
24469      * Remove all Records from the Store and fires the clear event.
24470      */
24471     removeAll : function(){
24472         this.data.clear();
24473         if(this.pruneModifiedRecords){
24474             this.modified = [];
24475         }
24476         this.fireEvent("clear", this);
24477     },
24478
24479     /**
24480      * Inserts Records to the Store at the given index and fires the add event.
24481      * @param {Number} index The start index at which to insert the passed Records.
24482      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24483      */
24484     insert : function(index, records){
24485         records = [].concat(records);
24486         for(var i = 0, len = records.length; i < len; i++){
24487             this.data.insert(index, records[i]);
24488             records[i].join(this);
24489         }
24490         this.fireEvent("add", this, records, index);
24491     },
24492
24493     /**
24494      * Get the index within the cache of the passed Record.
24495      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24496      * @return {Number} The index of the passed Record. Returns -1 if not found.
24497      */
24498     indexOf : function(record){
24499         return this.data.indexOf(record);
24500     },
24501
24502     /**
24503      * Get the index within the cache of the Record with the passed id.
24504      * @param {String} id The id of the Record to find.
24505      * @return {Number} The index of the Record. Returns -1 if not found.
24506      */
24507     indexOfId : function(id){
24508         return this.data.indexOfKey(id);
24509     },
24510
24511     /**
24512      * Get the Record with the specified id.
24513      * @param {String} id The id of the Record to find.
24514      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24515      */
24516     getById : function(id){
24517         return this.data.key(id);
24518     },
24519
24520     /**
24521      * Get the Record at the specified index.
24522      * @param {Number} index The index of the Record to find.
24523      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24524      */
24525     getAt : function(index){
24526         return this.data.itemAt(index);
24527     },
24528
24529     /**
24530      * Returns a range of Records between specified indices.
24531      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24532      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24533      * @return {Roo.data.Record[]} An array of Records
24534      */
24535     getRange : function(start, end){
24536         return this.data.getRange(start, end);
24537     },
24538
24539     // private
24540     storeOptions : function(o){
24541         o = Roo.apply({}, o);
24542         delete o.callback;
24543         delete o.scope;
24544         this.lastOptions = o;
24545     },
24546
24547     /**
24548      * Loads the Record cache from the configured Proxy using the configured Reader.
24549      * <p>
24550      * If using remote paging, then the first load call must specify the <em>start</em>
24551      * and <em>limit</em> properties in the options.params property to establish the initial
24552      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24553      * <p>
24554      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24555      * and this call will return before the new data has been loaded. Perform any post-processing
24556      * in a callback function, or in a "load" event handler.</strong>
24557      * <p>
24558      * @param {Object} options An object containing properties which control loading options:<ul>
24559      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24560      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24561      * passed the following arguments:<ul>
24562      * <li>r : Roo.data.Record[]</li>
24563      * <li>options: Options object from the load call</li>
24564      * <li>success: Boolean success indicator</li></ul></li>
24565      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24566      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24567      * </ul>
24568      */
24569     load : function(options){
24570         options = options || {};
24571         if(this.fireEvent("beforeload", this, options) !== false){
24572             this.storeOptions(options);
24573             var p = Roo.apply(options.params || {}, this.baseParams);
24574             // if meta was not loaded from remote source.. try requesting it.
24575             if (!this.reader.metaFromRemote) {
24576                 p._requestMeta = 1;
24577             }
24578             if(this.sortInfo && this.remoteSort){
24579                 var pn = this.paramNames;
24580                 p[pn["sort"]] = this.sortInfo.field;
24581                 p[pn["dir"]] = this.sortInfo.direction;
24582             }
24583             if (this.multiSort) {
24584                 var pn = this.paramNames;
24585                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24586             }
24587             
24588             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24589         }
24590     },
24591
24592     /**
24593      * Reloads the Record cache from the configured Proxy using the configured Reader and
24594      * the options from the last load operation performed.
24595      * @param {Object} options (optional) An object containing properties which may override the options
24596      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24597      * the most recently used options are reused).
24598      */
24599     reload : function(options){
24600         this.load(Roo.applyIf(options||{}, this.lastOptions));
24601     },
24602
24603     // private
24604     // Called as a callback by the Reader during a load operation.
24605     loadRecords : function(o, options, success){
24606          
24607         if(!o){
24608             if(success !== false){
24609                 this.fireEvent("load", this, [], options, o);
24610             }
24611             if(options.callback){
24612                 options.callback.call(options.scope || this, [], options, false);
24613             }
24614             return;
24615         }
24616         // if data returned failure - throw an exception.
24617         if (o.success === false) {
24618             // show a message if no listener is registered.
24619             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24620                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24621             }
24622             // loadmask wil be hooked into this..
24623             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24624             return;
24625         }
24626         var r = o.records, t = o.totalRecords || r.length;
24627         
24628         this.fireEvent("beforeloadadd", this, r, options, o);
24629         
24630         if(!options || options.add !== true){
24631             if(this.pruneModifiedRecords){
24632                 this.modified = [];
24633             }
24634             for(var i = 0, len = r.length; i < len; i++){
24635                 r[i].join(this);
24636             }
24637             if(this.snapshot){
24638                 this.data = this.snapshot;
24639                 delete this.snapshot;
24640             }
24641             this.data.clear();
24642             this.data.addAll(r);
24643             this.totalLength = t;
24644             this.applySort();
24645             this.fireEvent("datachanged", this);
24646         }else{
24647             this.totalLength = Math.max(t, this.data.length+r.length);
24648             this.add(r);
24649         }
24650         
24651         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24652                 
24653             var e = new Roo.data.Record({});
24654
24655             e.set(this.parent.displayField, this.parent.emptyTitle);
24656             e.set(this.parent.valueField, '');
24657
24658             this.insert(0, e);
24659         }
24660             
24661         this.fireEvent("load", this, r, options, o);
24662         if(options.callback){
24663             options.callback.call(options.scope || this, r, options, true);
24664         }
24665     },
24666
24667
24668     /**
24669      * Loads data from a passed data block. A Reader which understands the format of the data
24670      * must have been configured in the constructor.
24671      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24672      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24673      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24674      */
24675     loadData : function(o, append){
24676         var r = this.reader.readRecords(o);
24677         this.loadRecords(r, {add: append}, true);
24678     },
24679     
24680      /**
24681      * using 'cn' the nested child reader read the child array into it's child stores.
24682      * @param {Object} rec The record with a 'children array
24683      */
24684     loadDataFromChildren : function(rec)
24685     {
24686         this.loadData(this.reader.toLoadData(rec));
24687     },
24688     
24689
24690     /**
24691      * Gets the number of cached records.
24692      * <p>
24693      * <em>If using paging, this may not be the total size of the dataset. If the data object
24694      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24695      * the data set size</em>
24696      */
24697     getCount : function(){
24698         return this.data.length || 0;
24699     },
24700
24701     /**
24702      * Gets the total number of records in the dataset as returned by the server.
24703      * <p>
24704      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24705      * the dataset size</em>
24706      */
24707     getTotalCount : function(){
24708         return this.totalLength || 0;
24709     },
24710
24711     /**
24712      * Returns the sort state of the Store as an object with two properties:
24713      * <pre><code>
24714  field {String} The name of the field by which the Records are sorted
24715  direction {String} The sort order, "ASC" or "DESC"
24716      * </code></pre>
24717      */
24718     getSortState : function(){
24719         return this.sortInfo;
24720     },
24721
24722     // private
24723     applySort : function(){
24724         if(this.sortInfo && !this.remoteSort){
24725             var s = this.sortInfo, f = s.field;
24726             var st = this.fields.get(f).sortType;
24727             var fn = function(r1, r2){
24728                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24729                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24730             };
24731             this.data.sort(s.direction, fn);
24732             if(this.snapshot && this.snapshot != this.data){
24733                 this.snapshot.sort(s.direction, fn);
24734             }
24735         }
24736     },
24737
24738     /**
24739      * Sets the default sort column and order to be used by the next load operation.
24740      * @param {String} fieldName The name of the field to sort by.
24741      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24742      */
24743     setDefaultSort : function(field, dir){
24744         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24745     },
24746
24747     /**
24748      * Sort the Records.
24749      * If remote sorting is used, the sort is performed on the server, and the cache is
24750      * reloaded. If local sorting is used, the cache is sorted internally.
24751      * @param {String} fieldName The name of the field to sort by.
24752      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24753      */
24754     sort : function(fieldName, dir){
24755         var f = this.fields.get(fieldName);
24756         if(!dir){
24757             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24758             
24759             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24760                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24761             }else{
24762                 dir = f.sortDir;
24763             }
24764         }
24765         this.sortToggle[f.name] = dir;
24766         this.sortInfo = {field: f.name, direction: dir};
24767         if(!this.remoteSort){
24768             this.applySort();
24769             this.fireEvent("datachanged", this);
24770         }else{
24771             this.load(this.lastOptions);
24772         }
24773     },
24774
24775     /**
24776      * Calls the specified function for each of the Records in the cache.
24777      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24778      * Returning <em>false</em> aborts and exits the iteration.
24779      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24780      */
24781     each : function(fn, scope){
24782         this.data.each(fn, scope);
24783     },
24784
24785     /**
24786      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24787      * (e.g., during paging).
24788      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24789      */
24790     getModifiedRecords : function(){
24791         return this.modified;
24792     },
24793
24794     // private
24795     createFilterFn : function(property, value, anyMatch){
24796         if(!value.exec){ // not a regex
24797             value = String(value);
24798             if(value.length == 0){
24799                 return false;
24800             }
24801             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24802         }
24803         return function(r){
24804             return value.test(r.data[property]);
24805         };
24806     },
24807
24808     /**
24809      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24810      * @param {String} property A field on your records
24811      * @param {Number} start The record index to start at (defaults to 0)
24812      * @param {Number} end The last record index to include (defaults to length - 1)
24813      * @return {Number} The sum
24814      */
24815     sum : function(property, start, end){
24816         var rs = this.data.items, v = 0;
24817         start = start || 0;
24818         end = (end || end === 0) ? end : rs.length-1;
24819
24820         for(var i = start; i <= end; i++){
24821             v += (rs[i].data[property] || 0);
24822         }
24823         return v;
24824     },
24825
24826     /**
24827      * Filter the records by a specified property.
24828      * @param {String} field A field on your records
24829      * @param {String/RegExp} value Either a string that the field
24830      * should start with or a RegExp to test against the field
24831      * @param {Boolean} anyMatch True to match any part not just the beginning
24832      */
24833     filter : function(property, value, anyMatch){
24834         var fn = this.createFilterFn(property, value, anyMatch);
24835         return fn ? this.filterBy(fn) : this.clearFilter();
24836     },
24837
24838     /**
24839      * Filter by a function. The specified function will be called with each
24840      * record in this data source. If the function returns true the record is included,
24841      * otherwise it is filtered.
24842      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24843      * @param {Object} scope (optional) The scope of the function (defaults to this)
24844      */
24845     filterBy : function(fn, scope){
24846         this.snapshot = this.snapshot || this.data;
24847         this.data = this.queryBy(fn, scope||this);
24848         this.fireEvent("datachanged", this);
24849     },
24850
24851     /**
24852      * Query the records by a specified property.
24853      * @param {String} field A field on your records
24854      * @param {String/RegExp} value Either a string that the field
24855      * should start with or a RegExp to test against the field
24856      * @param {Boolean} anyMatch True to match any part not just the beginning
24857      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24858      */
24859     query : function(property, value, anyMatch){
24860         var fn = this.createFilterFn(property, value, anyMatch);
24861         return fn ? this.queryBy(fn) : this.data.clone();
24862     },
24863
24864     /**
24865      * Query by a function. The specified function will be called with each
24866      * record in this data source. If the function returns true the record is included
24867      * in the results.
24868      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24869      * @param {Object} scope (optional) The scope of the function (defaults to this)
24870       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24871      **/
24872     queryBy : function(fn, scope){
24873         var data = this.snapshot || this.data;
24874         return data.filterBy(fn, scope||this);
24875     },
24876
24877     /**
24878      * Collects unique values for a particular dataIndex from this store.
24879      * @param {String} dataIndex The property to collect
24880      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24881      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24882      * @return {Array} An array of the unique values
24883      **/
24884     collect : function(dataIndex, allowNull, bypassFilter){
24885         var d = (bypassFilter === true && this.snapshot) ?
24886                 this.snapshot.items : this.data.items;
24887         var v, sv, r = [], l = {};
24888         for(var i = 0, len = d.length; i < len; i++){
24889             v = d[i].data[dataIndex];
24890             sv = String(v);
24891             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24892                 l[sv] = true;
24893                 r[r.length] = v;
24894             }
24895         }
24896         return r;
24897     },
24898
24899     /**
24900      * Revert to a view of the Record cache with no filtering applied.
24901      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24902      */
24903     clearFilter : function(suppressEvent){
24904         if(this.snapshot && this.snapshot != this.data){
24905             this.data = this.snapshot;
24906             delete this.snapshot;
24907             if(suppressEvent !== true){
24908                 this.fireEvent("datachanged", this);
24909             }
24910         }
24911     },
24912
24913     // private
24914     afterEdit : function(record){
24915         if(this.modified.indexOf(record) == -1){
24916             this.modified.push(record);
24917         }
24918         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24919     },
24920     
24921     // private
24922     afterReject : function(record){
24923         this.modified.remove(record);
24924         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
24925     },
24926
24927     // private
24928     afterCommit : function(record){
24929         this.modified.remove(record);
24930         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
24931     },
24932
24933     /**
24934      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
24935      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
24936      */
24937     commitChanges : function(){
24938         var m = this.modified.slice(0);
24939         this.modified = [];
24940         for(var i = 0, len = m.length; i < len; i++){
24941             m[i].commit();
24942         }
24943     },
24944
24945     /**
24946      * Cancel outstanding changes on all changed records.
24947      */
24948     rejectChanges : function(){
24949         var m = this.modified.slice(0);
24950         this.modified = [];
24951         for(var i = 0, len = m.length; i < len; i++){
24952             m[i].reject();
24953         }
24954     },
24955
24956     onMetaChange : function(meta, rtype, o){
24957         this.recordType = rtype;
24958         this.fields = rtype.prototype.fields;
24959         delete this.snapshot;
24960         this.sortInfo = meta.sortInfo || this.sortInfo;
24961         this.modified = [];
24962         this.fireEvent('metachange', this, this.reader.meta);
24963     },
24964     
24965     moveIndex : function(data, type)
24966     {
24967         var index = this.indexOf(data);
24968         
24969         var newIndex = index + type;
24970         
24971         this.remove(data);
24972         
24973         this.insert(newIndex, data);
24974         
24975     }
24976 });/*
24977  * Based on:
24978  * Ext JS Library 1.1.1
24979  * Copyright(c) 2006-2007, Ext JS, LLC.
24980  *
24981  * Originally Released Under LGPL - original licence link has changed is not relivant.
24982  *
24983  * Fork - LGPL
24984  * <script type="text/javascript">
24985  */
24986
24987 /**
24988  * @class Roo.data.SimpleStore
24989  * @extends Roo.data.Store
24990  * Small helper class to make creating Stores from Array data easier.
24991  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
24992  * @cfg {Array} fields An array of field definition objects, or field name strings.
24993  * @cfg {Object} an existing reader (eg. copied from another store)
24994  * @cfg {Array} data The multi-dimensional array of data
24995  * @cfg {Roo.data.DataProxy} proxy [not-required]  
24996  * @cfg {Roo.data.Reader} reader  [not-required] 
24997  * @constructor
24998  * @param {Object} config
24999  */
25000 Roo.data.SimpleStore = function(config)
25001 {
25002     Roo.data.SimpleStore.superclass.constructor.call(this, {
25003         isLocal : true,
25004         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25005                 id: config.id
25006             },
25007             Roo.data.Record.create(config.fields)
25008         ),
25009         proxy : new Roo.data.MemoryProxy(config.data)
25010     });
25011     this.load();
25012 };
25013 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25014  * Based on:
25015  * Ext JS Library 1.1.1
25016  * Copyright(c) 2006-2007, Ext JS, LLC.
25017  *
25018  * Originally Released Under LGPL - original licence link has changed is not relivant.
25019  *
25020  * Fork - LGPL
25021  * <script type="text/javascript">
25022  */
25023
25024 /**
25025 /**
25026  * @extends Roo.data.Store
25027  * @class Roo.data.JsonStore
25028  * Small helper class to make creating Stores for JSON data easier. <br/>
25029 <pre><code>
25030 var store = new Roo.data.JsonStore({
25031     url: 'get-images.php',
25032     root: 'images',
25033     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25034 });
25035 </code></pre>
25036  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25037  * JsonReader and HttpProxy (unless inline data is provided).</b>
25038  * @cfg {Array} fields An array of field definition objects, or field name strings.
25039  * @constructor
25040  * @param {Object} config
25041  */
25042 Roo.data.JsonStore = function(c){
25043     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25044         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25045         reader: new Roo.data.JsonReader(c, c.fields)
25046     }));
25047 };
25048 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25049  * Based on:
25050  * Ext JS Library 1.1.1
25051  * Copyright(c) 2006-2007, Ext JS, LLC.
25052  *
25053  * Originally Released Under LGPL - original licence link has changed is not relivant.
25054  *
25055  * Fork - LGPL
25056  * <script type="text/javascript">
25057  */
25058
25059  
25060 Roo.data.Field = function(config){
25061     if(typeof config == "string"){
25062         config = {name: config};
25063     }
25064     Roo.apply(this, config);
25065     
25066     if(!this.type){
25067         this.type = "auto";
25068     }
25069     
25070     var st = Roo.data.SortTypes;
25071     // named sortTypes are supported, here we look them up
25072     if(typeof this.sortType == "string"){
25073         this.sortType = st[this.sortType];
25074     }
25075     
25076     // set default sortType for strings and dates
25077     if(!this.sortType){
25078         switch(this.type){
25079             case "string":
25080                 this.sortType = st.asUCString;
25081                 break;
25082             case "date":
25083                 this.sortType = st.asDate;
25084                 break;
25085             default:
25086                 this.sortType = st.none;
25087         }
25088     }
25089
25090     // define once
25091     var stripRe = /[\$,%]/g;
25092
25093     // prebuilt conversion function for this field, instead of
25094     // switching every time we're reading a value
25095     if(!this.convert){
25096         var cv, dateFormat = this.dateFormat;
25097         switch(this.type){
25098             case "":
25099             case "auto":
25100             case undefined:
25101                 cv = function(v){ return v; };
25102                 break;
25103             case "string":
25104                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25105                 break;
25106             case "int":
25107                 cv = function(v){
25108                     return v !== undefined && v !== null && v !== '' ?
25109                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25110                     };
25111                 break;
25112             case "float":
25113                 cv = function(v){
25114                     return v !== undefined && v !== null && v !== '' ?
25115                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25116                     };
25117                 break;
25118             case "bool":
25119             case "boolean":
25120                 cv = function(v){ return v === true || v === "true" || v == 1; };
25121                 break;
25122             case "date":
25123                 cv = function(v){
25124                     if(!v){
25125                         return '';
25126                     }
25127                     if(v instanceof Date){
25128                         return v;
25129                     }
25130                     if(dateFormat){
25131                         if(dateFormat == "timestamp"){
25132                             return new Date(v*1000);
25133                         }
25134                         return Date.parseDate(v, dateFormat);
25135                     }
25136                     var parsed = Date.parse(v);
25137                     return parsed ? new Date(parsed) : null;
25138                 };
25139              break;
25140             
25141         }
25142         this.convert = cv;
25143     }
25144 };
25145
25146 Roo.data.Field.prototype = {
25147     dateFormat: null,
25148     defaultValue: "",
25149     mapping: null,
25150     sortType : null,
25151     sortDir : "ASC"
25152 };/*
25153  * Based on:
25154  * Ext JS Library 1.1.1
25155  * Copyright(c) 2006-2007, Ext JS, LLC.
25156  *
25157  * Originally Released Under LGPL - original licence link has changed is not relivant.
25158  *
25159  * Fork - LGPL
25160  * <script type="text/javascript">
25161  */
25162  
25163 // Base class for reading structured data from a data source.  This class is intended to be
25164 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25165
25166 /**
25167  * @class Roo.data.DataReader
25168  * @abstract
25169  * Base class for reading structured data from a data source.  This class is intended to be
25170  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25171  */
25172
25173 Roo.data.DataReader = function(meta, recordType){
25174     
25175     this.meta = meta;
25176     
25177     this.recordType = recordType instanceof Array ? 
25178         Roo.data.Record.create(recordType) : recordType;
25179 };
25180
25181 Roo.data.DataReader.prototype = {
25182     
25183     
25184     readerType : 'Data',
25185      /**
25186      * Create an empty record
25187      * @param {Object} data (optional) - overlay some values
25188      * @return {Roo.data.Record} record created.
25189      */
25190     newRow :  function(d) {
25191         var da =  {};
25192         this.recordType.prototype.fields.each(function(c) {
25193             switch( c.type) {
25194                 case 'int' : da[c.name] = 0; break;
25195                 case 'date' : da[c.name] = new Date(); break;
25196                 case 'float' : da[c.name] = 0.0; break;
25197                 case 'boolean' : da[c.name] = false; break;
25198                 default : da[c.name] = ""; break;
25199             }
25200             
25201         });
25202         return new this.recordType(Roo.apply(da, d));
25203     }
25204     
25205     
25206 };/*
25207  * Based on:
25208  * Ext JS Library 1.1.1
25209  * Copyright(c) 2006-2007, Ext JS, LLC.
25210  *
25211  * Originally Released Under LGPL - original licence link has changed is not relivant.
25212  *
25213  * Fork - LGPL
25214  * <script type="text/javascript">
25215  */
25216
25217 /**
25218  * @class Roo.data.DataProxy
25219  * @extends Roo.util.Observable
25220  * @abstract
25221  * This class is an abstract base class for implementations which provide retrieval of
25222  * unformatted data objects.<br>
25223  * <p>
25224  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25225  * (of the appropriate type which knows how to parse the data object) to provide a block of
25226  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25227  * <p>
25228  * Custom implementations must implement the load method as described in
25229  * {@link Roo.data.HttpProxy#load}.
25230  */
25231 Roo.data.DataProxy = function(){
25232     this.addEvents({
25233         /**
25234          * @event beforeload
25235          * Fires before a network request is made to retrieve a data object.
25236          * @param {Object} This DataProxy object.
25237          * @param {Object} params The params parameter to the load function.
25238          */
25239         beforeload : true,
25240         /**
25241          * @event load
25242          * Fires before the load method's callback is called.
25243          * @param {Object} This DataProxy object.
25244          * @param {Object} o The data object.
25245          * @param {Object} arg The callback argument object passed to the load function.
25246          */
25247         load : true,
25248         /**
25249          * @event loadexception
25250          * Fires if an Exception occurs during data retrieval.
25251          * @param {Object} This DataProxy object.
25252          * @param {Object} o The data object.
25253          * @param {Object} arg The callback argument object passed to the load function.
25254          * @param {Object} e The Exception.
25255          */
25256         loadexception : true
25257     });
25258     Roo.data.DataProxy.superclass.constructor.call(this);
25259 };
25260
25261 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25262
25263     /**
25264      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25265      */
25266 /*
25267  * Based on:
25268  * Ext JS Library 1.1.1
25269  * Copyright(c) 2006-2007, Ext JS, LLC.
25270  *
25271  * Originally Released Under LGPL - original licence link has changed is not relivant.
25272  *
25273  * Fork - LGPL
25274  * <script type="text/javascript">
25275  */
25276 /**
25277  * @class Roo.data.MemoryProxy
25278  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25279  * to the Reader when its load method is called.
25280  * @constructor
25281  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25282  */
25283 Roo.data.MemoryProxy = function(data){
25284     if (data.data) {
25285         data = data.data;
25286     }
25287     Roo.data.MemoryProxy.superclass.constructor.call(this);
25288     this.data = data;
25289 };
25290
25291 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25292     
25293     /**
25294      * Load data from the requested source (in this case an in-memory
25295      * data object passed to the constructor), read the data object into
25296      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25297      * process that block using the passed callback.
25298      * @param {Object} params This parameter is not used by the MemoryProxy class.
25299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25300      * object into a block of Roo.data.Records.
25301      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25302      * The function must be passed <ul>
25303      * <li>The Record block object</li>
25304      * <li>The "arg" argument from the load function</li>
25305      * <li>A boolean success indicator</li>
25306      * </ul>
25307      * @param {Object} scope The scope in which to call the callback
25308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25309      */
25310     load : function(params, reader, callback, scope, arg){
25311         params = params || {};
25312         var result;
25313         try {
25314             result = reader.readRecords(params.data ? params.data :this.data);
25315         }catch(e){
25316             this.fireEvent("loadexception", this, arg, null, e);
25317             callback.call(scope, null, arg, false);
25318             return;
25319         }
25320         callback.call(scope, result, arg, true);
25321     },
25322     
25323     // private
25324     update : function(params, records){
25325         
25326     }
25327 });/*
25328  * Based on:
25329  * Ext JS Library 1.1.1
25330  * Copyright(c) 2006-2007, Ext JS, LLC.
25331  *
25332  * Originally Released Under LGPL - original licence link has changed is not relivant.
25333  *
25334  * Fork - LGPL
25335  * <script type="text/javascript">
25336  */
25337 /**
25338  * @class Roo.data.HttpProxy
25339  * @extends Roo.data.DataProxy
25340  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25341  * configured to reference a certain URL.<br><br>
25342  * <p>
25343  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25344  * from which the running page was served.<br><br>
25345  * <p>
25346  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25347  * <p>
25348  * Be aware that to enable the browser to parse an XML document, the server must set
25349  * the Content-Type header in the HTTP response to "text/xml".
25350  * @constructor
25351  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25352  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25353  * will be used to make the request.
25354  */
25355 Roo.data.HttpProxy = function(conn){
25356     Roo.data.HttpProxy.superclass.constructor.call(this);
25357     // is conn a conn config or a real conn?
25358     this.conn = conn;
25359     this.useAjax = !conn || !conn.events;
25360   
25361 };
25362
25363 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25364     // thse are take from connection...
25365     
25366     /**
25367      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25368      */
25369     /**
25370      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25371      * extra parameters to each request made by this object. (defaults to undefined)
25372      */
25373     /**
25374      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25375      *  to each request made by this object. (defaults to undefined)
25376      */
25377     /**
25378      * @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)
25379      */
25380     /**
25381      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25382      */
25383      /**
25384      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25385      * @type Boolean
25386      */
25387   
25388
25389     /**
25390      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25391      * @type Boolean
25392      */
25393     /**
25394      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25395      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25396      * a finer-grained basis than the DataProxy events.
25397      */
25398     getConnection : function(){
25399         return this.useAjax ? Roo.Ajax : this.conn;
25400     },
25401
25402     /**
25403      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25404      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25405      * process that block using the passed callback.
25406      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25407      * for the request to the remote server.
25408      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25409      * object into a block of Roo.data.Records.
25410      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25411      * The function must be passed <ul>
25412      * <li>The Record block object</li>
25413      * <li>The "arg" argument from the load function</li>
25414      * <li>A boolean success indicator</li>
25415      * </ul>
25416      * @param {Object} scope The scope in which to call the callback
25417      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25418      */
25419     load : function(params, reader, callback, scope, arg){
25420         if(this.fireEvent("beforeload", this, params) !== false){
25421             var  o = {
25422                 params : params || {},
25423                 request: {
25424                     callback : callback,
25425                     scope : scope,
25426                     arg : arg
25427                 },
25428                 reader: reader,
25429                 callback : this.loadResponse,
25430                 scope: this
25431             };
25432             if(this.useAjax){
25433                 Roo.applyIf(o, this.conn);
25434                 if(this.activeRequest){
25435                     Roo.Ajax.abort(this.activeRequest);
25436                 }
25437                 this.activeRequest = Roo.Ajax.request(o);
25438             }else{
25439                 this.conn.request(o);
25440             }
25441         }else{
25442             callback.call(scope||this, null, arg, false);
25443         }
25444     },
25445
25446     // private
25447     loadResponse : function(o, success, response){
25448         delete this.activeRequest;
25449         if(!success){
25450             this.fireEvent("loadexception", this, o, response);
25451             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25452             return;
25453         }
25454         var result;
25455         try {
25456             result = o.reader.read(response);
25457         }catch(e){
25458             o.success = false;
25459             o.raw = { errorMsg : response.responseText };
25460             this.fireEvent("loadexception", this, o, response, e);
25461             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25462             return;
25463         }
25464         
25465         this.fireEvent("load", this, o, o.request.arg);
25466         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25467     },
25468
25469     // private
25470     update : function(dataSet){
25471
25472     },
25473
25474     // private
25475     updateResponse : function(dataSet){
25476
25477     }
25478 });/*
25479  * Based on:
25480  * Ext JS Library 1.1.1
25481  * Copyright(c) 2006-2007, Ext JS, LLC.
25482  *
25483  * Originally Released Under LGPL - original licence link has changed is not relivant.
25484  *
25485  * Fork - LGPL
25486  * <script type="text/javascript">
25487  */
25488
25489 /**
25490  * @class Roo.data.ScriptTagProxy
25491  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25492  * other than the originating domain of the running page.<br><br>
25493  * <p>
25494  * <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
25495  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25496  * <p>
25497  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25498  * source code that is used as the source inside a &lt;script> tag.<br><br>
25499  * <p>
25500  * In order for the browser to process the returned data, the server must wrap the data object
25501  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25502  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25503  * depending on whether the callback name was passed:
25504  * <p>
25505  * <pre><code>
25506 boolean scriptTag = false;
25507 String cb = request.getParameter("callback");
25508 if (cb != null) {
25509     scriptTag = true;
25510     response.setContentType("text/javascript");
25511 } else {
25512     response.setContentType("application/x-json");
25513 }
25514 Writer out = response.getWriter();
25515 if (scriptTag) {
25516     out.write(cb + "(");
25517 }
25518 out.print(dataBlock.toJsonString());
25519 if (scriptTag) {
25520     out.write(");");
25521 }
25522 </pre></code>
25523  *
25524  * @constructor
25525  * @param {Object} config A configuration object.
25526  */
25527 Roo.data.ScriptTagProxy = function(config){
25528     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25529     Roo.apply(this, config);
25530     this.head = document.getElementsByTagName("head")[0];
25531 };
25532
25533 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25534
25535 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25536     /**
25537      * @cfg {String} url The URL from which to request the data object.
25538      */
25539     /**
25540      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25541      */
25542     timeout : 30000,
25543     /**
25544      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25545      * the server the name of the callback function set up by the load call to process the returned data object.
25546      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25547      * javascript output which calls this named function passing the data object as its only parameter.
25548      */
25549     callbackParam : "callback",
25550     /**
25551      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25552      * name to the request.
25553      */
25554     nocache : true,
25555
25556     /**
25557      * Load data from the configured URL, read the data object into
25558      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25559      * process that block using the passed callback.
25560      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25561      * for the request to the remote server.
25562      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25563      * object into a block of Roo.data.Records.
25564      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25565      * The function must be passed <ul>
25566      * <li>The Record block object</li>
25567      * <li>The "arg" argument from the load function</li>
25568      * <li>A boolean success indicator</li>
25569      * </ul>
25570      * @param {Object} scope The scope in which to call the callback
25571      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25572      */
25573     load : function(params, reader, callback, scope, arg){
25574         if(this.fireEvent("beforeload", this, params) !== false){
25575
25576             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25577
25578             var url = this.url;
25579             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25580             if(this.nocache){
25581                 url += "&_dc=" + (new Date().getTime());
25582             }
25583             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25584             var trans = {
25585                 id : transId,
25586                 cb : "stcCallback"+transId,
25587                 scriptId : "stcScript"+transId,
25588                 params : params,
25589                 arg : arg,
25590                 url : url,
25591                 callback : callback,
25592                 scope : scope,
25593                 reader : reader
25594             };
25595             var conn = this;
25596
25597             window[trans.cb] = function(o){
25598                 conn.handleResponse(o, trans);
25599             };
25600
25601             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25602
25603             if(this.autoAbort !== false){
25604                 this.abort();
25605             }
25606
25607             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25608
25609             var script = document.createElement("script");
25610             script.setAttribute("src", url);
25611             script.setAttribute("type", "text/javascript");
25612             script.setAttribute("id", trans.scriptId);
25613             this.head.appendChild(script);
25614
25615             this.trans = trans;
25616         }else{
25617             callback.call(scope||this, null, arg, false);
25618         }
25619     },
25620
25621     // private
25622     isLoading : function(){
25623         return this.trans ? true : false;
25624     },
25625
25626     /**
25627      * Abort the current server request.
25628      */
25629     abort : function(){
25630         if(this.isLoading()){
25631             this.destroyTrans(this.trans);
25632         }
25633     },
25634
25635     // private
25636     destroyTrans : function(trans, isLoaded){
25637         this.head.removeChild(document.getElementById(trans.scriptId));
25638         clearTimeout(trans.timeoutId);
25639         if(isLoaded){
25640             window[trans.cb] = undefined;
25641             try{
25642                 delete window[trans.cb];
25643             }catch(e){}
25644         }else{
25645             // if hasn't been loaded, wait for load to remove it to prevent script error
25646             window[trans.cb] = function(){
25647                 window[trans.cb] = undefined;
25648                 try{
25649                     delete window[trans.cb];
25650                 }catch(e){}
25651             };
25652         }
25653     },
25654
25655     // private
25656     handleResponse : function(o, trans){
25657         this.trans = false;
25658         this.destroyTrans(trans, true);
25659         var result;
25660         try {
25661             result = trans.reader.readRecords(o);
25662         }catch(e){
25663             this.fireEvent("loadexception", this, o, trans.arg, e);
25664             trans.callback.call(trans.scope||window, null, trans.arg, false);
25665             return;
25666         }
25667         this.fireEvent("load", this, o, trans.arg);
25668         trans.callback.call(trans.scope||window, result, trans.arg, true);
25669     },
25670
25671     // private
25672     handleFailure : function(trans){
25673         this.trans = false;
25674         this.destroyTrans(trans, false);
25675         this.fireEvent("loadexception", this, null, trans.arg);
25676         trans.callback.call(trans.scope||window, null, trans.arg, false);
25677     }
25678 });/*
25679  * Based on:
25680  * Ext JS Library 1.1.1
25681  * Copyright(c) 2006-2007, Ext JS, LLC.
25682  *
25683  * Originally Released Under LGPL - original licence link has changed is not relivant.
25684  *
25685  * Fork - LGPL
25686  * <script type="text/javascript">
25687  */
25688
25689 /**
25690  * @class Roo.data.JsonReader
25691  * @extends Roo.data.DataReader
25692  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25693  * based on mappings in a provided Roo.data.Record constructor.
25694  * 
25695  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25696  * in the reply previously. 
25697  * 
25698  * <p>
25699  * Example code:
25700  * <pre><code>
25701 var RecordDef = Roo.data.Record.create([
25702     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25703     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25704 ]);
25705 var myReader = new Roo.data.JsonReader({
25706     totalProperty: "results",    // The property which contains the total dataset size (optional)
25707     root: "rows",                // The property which contains an Array of row objects
25708     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25709 }, RecordDef);
25710 </code></pre>
25711  * <p>
25712  * This would consume a JSON file like this:
25713  * <pre><code>
25714 { 'results': 2, 'rows': [
25715     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25716     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25717 }
25718 </code></pre>
25719  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25720  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25721  * paged from the remote server.
25722  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25723  * @cfg {String} root name of the property which contains the Array of row objects.
25724  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25725  * @cfg {Array} fields Array of field definition objects
25726  * @constructor
25727  * Create a new JsonReader
25728  * @param {Object} meta Metadata configuration options
25729  * @param {Object} recordType Either an Array of field definition objects,
25730  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25731  */
25732 Roo.data.JsonReader = function(meta, recordType){
25733     
25734     meta = meta || {};
25735     // set some defaults:
25736     Roo.applyIf(meta, {
25737         totalProperty: 'total',
25738         successProperty : 'success',
25739         root : 'data',
25740         id : 'id'
25741     });
25742     
25743     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25744 };
25745 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25746     
25747     readerType : 'Json',
25748     
25749     /**
25750      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25751      * Used by Store query builder to append _requestMeta to params.
25752      * 
25753      */
25754     metaFromRemote : false,
25755     /**
25756      * This method is only used by a DataProxy which has retrieved data from a remote server.
25757      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25758      * @return {Object} data A data block which is used by an Roo.data.Store object as
25759      * a cache of Roo.data.Records.
25760      */
25761     read : function(response){
25762         var json = response.responseText;
25763        
25764         var o = /* eval:var:o */ eval("("+json+")");
25765         if(!o) {
25766             throw {message: "JsonReader.read: Json object not found"};
25767         }
25768         
25769         if(o.metaData){
25770             
25771             delete this.ef;
25772             this.metaFromRemote = true;
25773             this.meta = o.metaData;
25774             this.recordType = Roo.data.Record.create(o.metaData.fields);
25775             this.onMetaChange(this.meta, this.recordType, o);
25776         }
25777         return this.readRecords(o);
25778     },
25779
25780     // private function a store will implement
25781     onMetaChange : function(meta, recordType, o){
25782
25783     },
25784
25785     /**
25786          * @ignore
25787          */
25788     simpleAccess: function(obj, subsc) {
25789         return obj[subsc];
25790     },
25791
25792         /**
25793          * @ignore
25794          */
25795     getJsonAccessor: function(){
25796         var re = /[\[\.]/;
25797         return function(expr) {
25798             try {
25799                 return(re.test(expr))
25800                     ? new Function("obj", "return obj." + expr)
25801                     : function(obj){
25802                         return obj[expr];
25803                     };
25804             } catch(e){}
25805             return Roo.emptyFn;
25806         };
25807     }(),
25808
25809     /**
25810      * Create a data block containing Roo.data.Records from an XML document.
25811      * @param {Object} o An object which contains an Array of row objects in the property specified
25812      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25813      * which contains the total size of the dataset.
25814      * @return {Object} data A data block which is used by an Roo.data.Store object as
25815      * a cache of Roo.data.Records.
25816      */
25817     readRecords : function(o){
25818         /**
25819          * After any data loads, the raw JSON data is available for further custom processing.
25820          * @type Object
25821          */
25822         this.o = o;
25823         var s = this.meta, Record = this.recordType,
25824             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25825
25826 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25827         if (!this.ef) {
25828             if(s.totalProperty) {
25829                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25830                 }
25831                 if(s.successProperty) {
25832                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25833                 }
25834                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25835                 if (s.id) {
25836                         var g = this.getJsonAccessor(s.id);
25837                         this.getId = function(rec) {
25838                                 var r = g(rec);  
25839                                 return (r === undefined || r === "") ? null : r;
25840                         };
25841                 } else {
25842                         this.getId = function(){return null;};
25843                 }
25844             this.ef = [];
25845             for(var jj = 0; jj < fl; jj++){
25846                 f = fi[jj];
25847                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25848                 this.ef[jj] = this.getJsonAccessor(map);
25849             }
25850         }
25851
25852         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25853         if(s.totalProperty){
25854             var vt = parseInt(this.getTotal(o), 10);
25855             if(!isNaN(vt)){
25856                 totalRecords = vt;
25857             }
25858         }
25859         if(s.successProperty){
25860             var vs = this.getSuccess(o);
25861             if(vs === false || vs === 'false'){
25862                 success = false;
25863             }
25864         }
25865         var records = [];
25866         for(var i = 0; i < c; i++){
25867             var n = root[i];
25868             var values = {};
25869             var id = this.getId(n);
25870             for(var j = 0; j < fl; j++){
25871                 f = fi[j];
25872                                 var v = this.ef[j](n);
25873                                 if (!f.convert) {
25874                                         Roo.log('missing convert for ' + f.name);
25875                                         Roo.log(f);
25876                                         continue;
25877                                 }
25878                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25879             }
25880                         if (!Record) {
25881                                 return {
25882                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25883                                         success : false,
25884                                         records : [],
25885                                         totalRecords : 0
25886                                 };
25887                         }
25888             var record = new Record(values, id);
25889             record.json = n;
25890             records[i] = record;
25891         }
25892         return {
25893             raw : o,
25894             success : success,
25895             records : records,
25896             totalRecords : totalRecords
25897         };
25898     },
25899     // used when loading children.. @see loadDataFromChildren
25900     toLoadData: function(rec)
25901     {
25902         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25903         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25904         return { data : data, total : data.length };
25905         
25906     }
25907 });/*
25908  * Based on:
25909  * Ext JS Library 1.1.1
25910  * Copyright(c) 2006-2007, Ext JS, LLC.
25911  *
25912  * Originally Released Under LGPL - original licence link has changed is not relivant.
25913  *
25914  * Fork - LGPL
25915  * <script type="text/javascript">
25916  */
25917
25918 /**
25919  * @class Roo.data.XmlReader
25920  * @extends Roo.data.DataReader
25921  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25922  * based on mappings in a provided Roo.data.Record constructor.<br><br>
25923  * <p>
25924  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
25925  * header in the HTTP response must be set to "text/xml".</em>
25926  * <p>
25927  * Example code:
25928  * <pre><code>
25929 var RecordDef = Roo.data.Record.create([
25930    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25931    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25932 ]);
25933 var myReader = new Roo.data.XmlReader({
25934    totalRecords: "results", // The element which contains the total dataset size (optional)
25935    record: "row",           // The repeated element which contains row information
25936    id: "id"                 // The element within the row that provides an ID for the record (optional)
25937 }, RecordDef);
25938 </code></pre>
25939  * <p>
25940  * This would consume an XML file like this:
25941  * <pre><code>
25942 &lt;?xml?>
25943 &lt;dataset>
25944  &lt;results>2&lt;/results>
25945  &lt;row>
25946    &lt;id>1&lt;/id>
25947    &lt;name>Bill&lt;/name>
25948    &lt;occupation>Gardener&lt;/occupation>
25949  &lt;/row>
25950  &lt;row>
25951    &lt;id>2&lt;/id>
25952    &lt;name>Ben&lt;/name>
25953    &lt;occupation>Horticulturalist&lt;/occupation>
25954  &lt;/row>
25955 &lt;/dataset>
25956 </code></pre>
25957  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
25958  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25959  * paged from the remote server.
25960  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
25961  * @cfg {String} success The DomQuery path to the success attribute used by forms.
25962  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
25963  * a record identifier value.
25964  * @constructor
25965  * Create a new XmlReader
25966  * @param {Object} meta Metadata configuration options
25967  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
25968  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
25969  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
25970  */
25971 Roo.data.XmlReader = function(meta, recordType){
25972     meta = meta || {};
25973     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25974 };
25975 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
25976     
25977     readerType : 'Xml',
25978     
25979     /**
25980      * This method is only used by a DataProxy which has retrieved data from a remote server.
25981          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
25982          * to contain a method called 'responseXML' that returns an XML document object.
25983      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25984      * a cache of Roo.data.Records.
25985      */
25986     read : function(response){
25987         var doc = response.responseXML;
25988         if(!doc) {
25989             throw {message: "XmlReader.read: XML Document not available"};
25990         }
25991         return this.readRecords(doc);
25992     },
25993
25994     /**
25995      * Create a data block containing Roo.data.Records from an XML document.
25996          * @param {Object} doc A parsed XML document.
25997      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
25998      * a cache of Roo.data.Records.
25999      */
26000     readRecords : function(doc){
26001         /**
26002          * After any data loads/reads, the raw XML Document is available for further custom processing.
26003          * @type XMLDocument
26004          */
26005         this.xmlData = doc;
26006         var root = doc.documentElement || doc;
26007         var q = Roo.DomQuery;
26008         var recordType = this.recordType, fields = recordType.prototype.fields;
26009         var sid = this.meta.id;
26010         var totalRecords = 0, success = true;
26011         if(this.meta.totalRecords){
26012             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26013         }
26014         
26015         if(this.meta.success){
26016             var sv = q.selectValue(this.meta.success, root, true);
26017             success = sv !== false && sv !== 'false';
26018         }
26019         var records = [];
26020         var ns = q.select(this.meta.record, root);
26021         for(var i = 0, len = ns.length; i < len; i++) {
26022                 var n = ns[i];
26023                 var values = {};
26024                 var id = sid ? q.selectValue(sid, n) : undefined;
26025                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26026                     var f = fields.items[j];
26027                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26028                     v = f.convert(v);
26029                     values[f.name] = v;
26030                 }
26031                 var record = new recordType(values, id);
26032                 record.node = n;
26033                 records[records.length] = record;
26034             }
26035
26036             return {
26037                 success : success,
26038                 records : records,
26039                 totalRecords : totalRecords || records.length
26040             };
26041     }
26042 });/*
26043  * Based on:
26044  * Ext JS Library 1.1.1
26045  * Copyright(c) 2006-2007, Ext JS, LLC.
26046  *
26047  * Originally Released Under LGPL - original licence link has changed is not relivant.
26048  *
26049  * Fork - LGPL
26050  * <script type="text/javascript">
26051  */
26052
26053 /**
26054  * @class Roo.data.ArrayReader
26055  * @extends Roo.data.DataReader
26056  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26057  * Each element of that Array represents a row of data fields. The
26058  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26059  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26060  * <p>
26061  * Example code:.
26062  * <pre><code>
26063 var RecordDef = Roo.data.Record.create([
26064     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26065     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26066 ]);
26067 var myReader = new Roo.data.ArrayReader({
26068     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26069 }, RecordDef);
26070 </code></pre>
26071  * <p>
26072  * This would consume an Array like this:
26073  * <pre><code>
26074 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26075   </code></pre>
26076  
26077  * @constructor
26078  * Create a new JsonReader
26079  * @param {Object} meta Metadata configuration options.
26080  * @param {Object|Array} recordType Either an Array of field definition objects
26081  * 
26082  * @cfg {Array} fields Array of field definition objects
26083  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26084  * as specified to {@link Roo.data.Record#create},
26085  * or an {@link Roo.data.Record} object
26086  *
26087  * 
26088  * created using {@link Roo.data.Record#create}.
26089  */
26090 Roo.data.ArrayReader = function(meta, recordType)
26091 {    
26092     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26093 };
26094
26095 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26096     
26097       /**
26098      * Create a data block containing Roo.data.Records from an XML document.
26099      * @param {Object} o An Array of row objects which represents the dataset.
26100      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26101      * a cache of Roo.data.Records.
26102      */
26103     readRecords : function(o)
26104     {
26105         var sid = this.meta ? this.meta.id : null;
26106         var recordType = this.recordType, fields = recordType.prototype.fields;
26107         var records = [];
26108         var root = o;
26109         for(var i = 0; i < root.length; i++){
26110             var n = root[i];
26111             var values = {};
26112             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26113             for(var j = 0, jlen = fields.length; j < jlen; j++){
26114                 var f = fields.items[j];
26115                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26116                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26117                 v = f.convert(v);
26118                 values[f.name] = v;
26119             }
26120             var record = new recordType(values, id);
26121             record.json = n;
26122             records[records.length] = record;
26123         }
26124         return {
26125             records : records,
26126             totalRecords : records.length
26127         };
26128     },
26129     // used when loading children.. @see loadDataFromChildren
26130     toLoadData: function(rec)
26131     {
26132         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26133         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26134         
26135     }
26136     
26137     
26138 });/*
26139  * Based on:
26140  * Ext JS Library 1.1.1
26141  * Copyright(c) 2006-2007, Ext JS, LLC.
26142  *
26143  * Originally Released Under LGPL - original licence link has changed is not relivant.
26144  *
26145  * Fork - LGPL
26146  * <script type="text/javascript">
26147  */
26148
26149
26150 /**
26151  * @class Roo.data.Tree
26152  * @extends Roo.util.Observable
26153  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26154  * in the tree have most standard DOM functionality.
26155  * @constructor
26156  * @param {Node} root (optional) The root node
26157  */
26158 Roo.data.Tree = function(root){
26159    this.nodeHash = {};
26160    /**
26161     * The root node for this tree
26162     * @type Node
26163     */
26164    this.root = null;
26165    if(root){
26166        this.setRootNode(root);
26167    }
26168    this.addEvents({
26169        /**
26170         * @event append
26171         * Fires when a new child node is appended to a node in this tree.
26172         * @param {Tree} tree The owner tree
26173         * @param {Node} parent The parent node
26174         * @param {Node} node The newly appended node
26175         * @param {Number} index The index of the newly appended node
26176         */
26177        "append" : true,
26178        /**
26179         * @event remove
26180         * Fires when a child node is removed from a node in this tree.
26181         * @param {Tree} tree The owner tree
26182         * @param {Node} parent The parent node
26183         * @param {Node} node The child node removed
26184         */
26185        "remove" : true,
26186        /**
26187         * @event move
26188         * Fires when a node is moved to a new location in the tree
26189         * @param {Tree} tree The owner tree
26190         * @param {Node} node The node moved
26191         * @param {Node} oldParent The old parent of this node
26192         * @param {Node} newParent The new parent of this node
26193         * @param {Number} index The index it was moved to
26194         */
26195        "move" : true,
26196        /**
26197         * @event insert
26198         * Fires when a new child node is inserted in a node in this tree.
26199         * @param {Tree} tree The owner tree
26200         * @param {Node} parent The parent node
26201         * @param {Node} node The child node inserted
26202         * @param {Node} refNode The child node the node was inserted before
26203         */
26204        "insert" : true,
26205        /**
26206         * @event beforeappend
26207         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26208         * @param {Tree} tree The owner tree
26209         * @param {Node} parent The parent node
26210         * @param {Node} node The child node to be appended
26211         */
26212        "beforeappend" : true,
26213        /**
26214         * @event beforeremove
26215         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26216         * @param {Tree} tree The owner tree
26217         * @param {Node} parent The parent node
26218         * @param {Node} node The child node to be removed
26219         */
26220        "beforeremove" : true,
26221        /**
26222         * @event beforemove
26223         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26224         * @param {Tree} tree The owner tree
26225         * @param {Node} node The node being moved
26226         * @param {Node} oldParent The parent of the node
26227         * @param {Node} newParent The new parent the node is moving to
26228         * @param {Number} index The index it is being moved to
26229         */
26230        "beforemove" : true,
26231        /**
26232         * @event beforeinsert
26233         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26234         * @param {Tree} tree The owner tree
26235         * @param {Node} parent The parent node
26236         * @param {Node} node The child node to be inserted
26237         * @param {Node} refNode The child node the node is being inserted before
26238         */
26239        "beforeinsert" : true
26240    });
26241
26242     Roo.data.Tree.superclass.constructor.call(this);
26243 };
26244
26245 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26246     pathSeparator: "/",
26247
26248     proxyNodeEvent : function(){
26249         return this.fireEvent.apply(this, arguments);
26250     },
26251
26252     /**
26253      * Returns the root node for this tree.
26254      * @return {Node}
26255      */
26256     getRootNode : function(){
26257         return this.root;
26258     },
26259
26260     /**
26261      * Sets the root node for this tree.
26262      * @param {Node} node
26263      * @return {Node}
26264      */
26265     setRootNode : function(node){
26266         this.root = node;
26267         node.ownerTree = this;
26268         node.isRoot = true;
26269         this.registerNode(node);
26270         return node;
26271     },
26272
26273     /**
26274      * Gets a node in this tree by its id.
26275      * @param {String} id
26276      * @return {Node}
26277      */
26278     getNodeById : function(id){
26279         return this.nodeHash[id];
26280     },
26281
26282     registerNode : function(node){
26283         this.nodeHash[node.id] = node;
26284     },
26285
26286     unregisterNode : function(node){
26287         delete this.nodeHash[node.id];
26288     },
26289
26290     toString : function(){
26291         return "[Tree"+(this.id?" "+this.id:"")+"]";
26292     }
26293 });
26294
26295 /**
26296  * @class Roo.data.Node
26297  * @extends Roo.util.Observable
26298  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26299  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26300  * @constructor
26301  * @param {Object} attributes The attributes/config for the node
26302  */
26303 Roo.data.Node = function(attributes){
26304     /**
26305      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26306      * @type {Object}
26307      */
26308     this.attributes = attributes || {};
26309     this.leaf = this.attributes.leaf;
26310     /**
26311      * The node id. @type String
26312      */
26313     this.id = this.attributes.id;
26314     if(!this.id){
26315         this.id = Roo.id(null, "ynode-");
26316         this.attributes.id = this.id;
26317     }
26318      
26319     
26320     /**
26321      * All child nodes of this node. @type Array
26322      */
26323     this.childNodes = [];
26324     if(!this.childNodes.indexOf){ // indexOf is a must
26325         this.childNodes.indexOf = function(o){
26326             for(var i = 0, len = this.length; i < len; i++){
26327                 if(this[i] == o) {
26328                     return i;
26329                 }
26330             }
26331             return -1;
26332         };
26333     }
26334     /**
26335      * The parent node for this node. @type Node
26336      */
26337     this.parentNode = null;
26338     /**
26339      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26340      */
26341     this.firstChild = null;
26342     /**
26343      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26344      */
26345     this.lastChild = null;
26346     /**
26347      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26348      */
26349     this.previousSibling = null;
26350     /**
26351      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26352      */
26353     this.nextSibling = null;
26354
26355     this.addEvents({
26356        /**
26357         * @event append
26358         * Fires when a new child node is appended
26359         * @param {Tree} tree The owner tree
26360         * @param {Node} this This node
26361         * @param {Node} node The newly appended node
26362         * @param {Number} index The index of the newly appended node
26363         */
26364        "append" : true,
26365        /**
26366         * @event remove
26367         * Fires when a child node is removed
26368         * @param {Tree} tree The owner tree
26369         * @param {Node} this This node
26370         * @param {Node} node The removed node
26371         */
26372        "remove" : true,
26373        /**
26374         * @event move
26375         * Fires when this node is moved to a new location in the tree
26376         * @param {Tree} tree The owner tree
26377         * @param {Node} this This node
26378         * @param {Node} oldParent The old parent of this node
26379         * @param {Node} newParent The new parent of this node
26380         * @param {Number} index The index it was moved to
26381         */
26382        "move" : true,
26383        /**
26384         * @event insert
26385         * Fires when a new child node is inserted.
26386         * @param {Tree} tree The owner tree
26387         * @param {Node} this This node
26388         * @param {Node} node The child node inserted
26389         * @param {Node} refNode The child node the node was inserted before
26390         */
26391        "insert" : true,
26392        /**
26393         * @event beforeappend
26394         * Fires before a new child is appended, return false to cancel the append.
26395         * @param {Tree} tree The owner tree
26396         * @param {Node} this This node
26397         * @param {Node} node The child node to be appended
26398         */
26399        "beforeappend" : true,
26400        /**
26401         * @event beforeremove
26402         * Fires before a child is removed, return false to cancel the remove.
26403         * @param {Tree} tree The owner tree
26404         * @param {Node} this This node
26405         * @param {Node} node The child node to be removed
26406         */
26407        "beforeremove" : true,
26408        /**
26409         * @event beforemove
26410         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26411         * @param {Tree} tree The owner tree
26412         * @param {Node} this This node
26413         * @param {Node} oldParent The parent of this node
26414         * @param {Node} newParent The new parent this node is moving to
26415         * @param {Number} index The index it is being moved to
26416         */
26417        "beforemove" : true,
26418        /**
26419         * @event beforeinsert
26420         * Fires before a new child is inserted, return false to cancel the insert.
26421         * @param {Tree} tree The owner tree
26422         * @param {Node} this This node
26423         * @param {Node} node The child node to be inserted
26424         * @param {Node} refNode The child node the node is being inserted before
26425         */
26426        "beforeinsert" : true
26427    });
26428     this.listeners = this.attributes.listeners;
26429     Roo.data.Node.superclass.constructor.call(this);
26430 };
26431
26432 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26433     fireEvent : function(evtName){
26434         // first do standard event for this node
26435         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26436             return false;
26437         }
26438         // then bubble it up to the tree if the event wasn't cancelled
26439         var ot = this.getOwnerTree();
26440         if(ot){
26441             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26442                 return false;
26443             }
26444         }
26445         return true;
26446     },
26447
26448     /**
26449      * Returns true if this node is a leaf
26450      * @return {Boolean}
26451      */
26452     isLeaf : function(){
26453         return this.leaf === true;
26454     },
26455
26456     // private
26457     setFirstChild : function(node){
26458         this.firstChild = node;
26459     },
26460
26461     //private
26462     setLastChild : function(node){
26463         this.lastChild = node;
26464     },
26465
26466
26467     /**
26468      * Returns true if this node is the last child of its parent
26469      * @return {Boolean}
26470      */
26471     isLast : function(){
26472        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26473     },
26474
26475     /**
26476      * Returns true if this node is the first child of its parent
26477      * @return {Boolean}
26478      */
26479     isFirst : function(){
26480        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26481     },
26482
26483     hasChildNodes : function(){
26484         return !this.isLeaf() && this.childNodes.length > 0;
26485     },
26486
26487     /**
26488      * Insert node(s) as the last child node of this node.
26489      * @param {Node/Array} node The node or Array of nodes to append
26490      * @return {Node} The appended node if single append, or null if an array was passed
26491      */
26492     appendChild : function(node){
26493         var multi = false;
26494         if(node instanceof Array){
26495             multi = node;
26496         }else if(arguments.length > 1){
26497             multi = arguments;
26498         }
26499         
26500         // if passed an array or multiple args do them one by one
26501         if(multi){
26502             for(var i = 0, len = multi.length; i < len; i++) {
26503                 this.appendChild(multi[i]);
26504             }
26505         }else{
26506             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26507                 return false;
26508             }
26509             var index = this.childNodes.length;
26510             var oldParent = node.parentNode;
26511             // it's a move, make sure we move it cleanly
26512             if(oldParent){
26513                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26514                     return false;
26515                 }
26516                 oldParent.removeChild(node);
26517             }
26518             
26519             index = this.childNodes.length;
26520             if(index == 0){
26521                 this.setFirstChild(node);
26522             }
26523             this.childNodes.push(node);
26524             node.parentNode = this;
26525             var ps = this.childNodes[index-1];
26526             if(ps){
26527                 node.previousSibling = ps;
26528                 ps.nextSibling = node;
26529             }else{
26530                 node.previousSibling = null;
26531             }
26532             node.nextSibling = null;
26533             this.setLastChild(node);
26534             node.setOwnerTree(this.getOwnerTree());
26535             this.fireEvent("append", this.ownerTree, this, node, index);
26536             if(this.ownerTree) {
26537                 this.ownerTree.fireEvent("appendnode", this, node, index);
26538             }
26539             if(oldParent){
26540                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26541             }
26542             return node;
26543         }
26544     },
26545
26546     /**
26547      * Removes a child node from this node.
26548      * @param {Node} node The node to remove
26549      * @return {Node} The removed node
26550      */
26551     removeChild : function(node){
26552         var index = this.childNodes.indexOf(node);
26553         if(index == -1){
26554             return false;
26555         }
26556         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26557             return false;
26558         }
26559
26560         // remove it from childNodes collection
26561         this.childNodes.splice(index, 1);
26562
26563         // update siblings
26564         if(node.previousSibling){
26565             node.previousSibling.nextSibling = node.nextSibling;
26566         }
26567         if(node.nextSibling){
26568             node.nextSibling.previousSibling = node.previousSibling;
26569         }
26570
26571         // update child refs
26572         if(this.firstChild == node){
26573             this.setFirstChild(node.nextSibling);
26574         }
26575         if(this.lastChild == node){
26576             this.setLastChild(node.previousSibling);
26577         }
26578
26579         node.setOwnerTree(null);
26580         // clear any references from the node
26581         node.parentNode = null;
26582         node.previousSibling = null;
26583         node.nextSibling = null;
26584         this.fireEvent("remove", this.ownerTree, this, node);
26585         return node;
26586     },
26587
26588     /**
26589      * Inserts the first node before the second node in this nodes childNodes collection.
26590      * @param {Node} node The node to insert
26591      * @param {Node} refNode The node to insert before (if null the node is appended)
26592      * @return {Node} The inserted node
26593      */
26594     insertBefore : function(node, refNode){
26595         if(!refNode){ // like standard Dom, refNode can be null for append
26596             return this.appendChild(node);
26597         }
26598         // nothing to do
26599         if(node == refNode){
26600             return false;
26601         }
26602
26603         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26604             return false;
26605         }
26606         var index = this.childNodes.indexOf(refNode);
26607         var oldParent = node.parentNode;
26608         var refIndex = index;
26609
26610         // when moving internally, indexes will change after remove
26611         if(oldParent == this && this.childNodes.indexOf(node) < index){
26612             refIndex--;
26613         }
26614
26615         // it's a move, make sure we move it cleanly
26616         if(oldParent){
26617             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26618                 return false;
26619             }
26620             oldParent.removeChild(node);
26621         }
26622         if(refIndex == 0){
26623             this.setFirstChild(node);
26624         }
26625         this.childNodes.splice(refIndex, 0, node);
26626         node.parentNode = this;
26627         var ps = this.childNodes[refIndex-1];
26628         if(ps){
26629             node.previousSibling = ps;
26630             ps.nextSibling = node;
26631         }else{
26632             node.previousSibling = null;
26633         }
26634         node.nextSibling = refNode;
26635         refNode.previousSibling = node;
26636         node.setOwnerTree(this.getOwnerTree());
26637         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26638         if(oldParent){
26639             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26640         }
26641         return node;
26642     },
26643
26644     /**
26645      * Returns the child node at the specified index.
26646      * @param {Number} index
26647      * @return {Node}
26648      */
26649     item : function(index){
26650         return this.childNodes[index];
26651     },
26652
26653     /**
26654      * Replaces one child node in this node with another.
26655      * @param {Node} newChild The replacement node
26656      * @param {Node} oldChild The node to replace
26657      * @return {Node} The replaced node
26658      */
26659     replaceChild : function(newChild, oldChild){
26660         this.insertBefore(newChild, oldChild);
26661         this.removeChild(oldChild);
26662         return oldChild;
26663     },
26664
26665     /**
26666      * Returns the index of a child node
26667      * @param {Node} node
26668      * @return {Number} The index of the node or -1 if it was not found
26669      */
26670     indexOf : function(child){
26671         return this.childNodes.indexOf(child);
26672     },
26673
26674     /**
26675      * Returns the tree this node is in.
26676      * @return {Tree}
26677      */
26678     getOwnerTree : function(){
26679         // if it doesn't have one, look for one
26680         if(!this.ownerTree){
26681             var p = this;
26682             while(p){
26683                 if(p.ownerTree){
26684                     this.ownerTree = p.ownerTree;
26685                     break;
26686                 }
26687                 p = p.parentNode;
26688             }
26689         }
26690         return this.ownerTree;
26691     },
26692
26693     /**
26694      * Returns depth of this node (the root node has a depth of 0)
26695      * @return {Number}
26696      */
26697     getDepth : function(){
26698         var depth = 0;
26699         var p = this;
26700         while(p.parentNode){
26701             ++depth;
26702             p = p.parentNode;
26703         }
26704         return depth;
26705     },
26706
26707     // private
26708     setOwnerTree : function(tree){
26709         // if it's move, we need to update everyone
26710         if(tree != this.ownerTree){
26711             if(this.ownerTree){
26712                 this.ownerTree.unregisterNode(this);
26713             }
26714             this.ownerTree = tree;
26715             var cs = this.childNodes;
26716             for(var i = 0, len = cs.length; i < len; i++) {
26717                 cs[i].setOwnerTree(tree);
26718             }
26719             if(tree){
26720                 tree.registerNode(this);
26721             }
26722         }
26723     },
26724
26725     /**
26726      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26727      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26728      * @return {String} The path
26729      */
26730     getPath : function(attr){
26731         attr = attr || "id";
26732         var p = this.parentNode;
26733         var b = [this.attributes[attr]];
26734         while(p){
26735             b.unshift(p.attributes[attr]);
26736             p = p.parentNode;
26737         }
26738         var sep = this.getOwnerTree().pathSeparator;
26739         return sep + b.join(sep);
26740     },
26741
26742     /**
26743      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26744      * function call will be the scope provided or the current node. The arguments to the function
26745      * will be the args provided or the current node. If the function returns false at any point,
26746      * the bubble is stopped.
26747      * @param {Function} fn The function to call
26748      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26749      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26750      */
26751     bubble : function(fn, scope, args){
26752         var p = this;
26753         while(p){
26754             if(fn.call(scope || p, args || p) === false){
26755                 break;
26756             }
26757             p = p.parentNode;
26758         }
26759     },
26760
26761     /**
26762      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26763      * function call will be the scope provided or the current node. The arguments to the function
26764      * will be the args provided or the current node. If the function returns false at any point,
26765      * the cascade is stopped on that branch.
26766      * @param {Function} fn The function to call
26767      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26768      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26769      */
26770     cascade : function(fn, scope, args){
26771         if(fn.call(scope || this, args || this) !== false){
26772             var cs = this.childNodes;
26773             for(var i = 0, len = cs.length; i < len; i++) {
26774                 cs[i].cascade(fn, scope, args);
26775             }
26776         }
26777     },
26778
26779     /**
26780      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26781      * function call will be the scope provided or the current node. The arguments to the function
26782      * will be the args provided or the current node. If the function returns false at any point,
26783      * the iteration stops.
26784      * @param {Function} fn The function to call
26785      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26786      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26787      */
26788     eachChild : function(fn, scope, args){
26789         var cs = this.childNodes;
26790         for(var i = 0, len = cs.length; i < len; i++) {
26791                 if(fn.call(scope || this, args || cs[i]) === false){
26792                     break;
26793                 }
26794         }
26795     },
26796
26797     /**
26798      * Finds the first child that has the attribute with the specified value.
26799      * @param {String} attribute The attribute name
26800      * @param {Mixed} value The value to search for
26801      * @return {Node} The found child or null if none was found
26802      */
26803     findChild : function(attribute, value){
26804         var cs = this.childNodes;
26805         for(var i = 0, len = cs.length; i < len; i++) {
26806                 if(cs[i].attributes[attribute] == value){
26807                     return cs[i];
26808                 }
26809         }
26810         return null;
26811     },
26812
26813     /**
26814      * Finds the first child by a custom function. The child matches if the function passed
26815      * returns true.
26816      * @param {Function} fn
26817      * @param {Object} scope (optional)
26818      * @return {Node} The found child or null if none was found
26819      */
26820     findChildBy : function(fn, scope){
26821         var cs = this.childNodes;
26822         for(var i = 0, len = cs.length; i < len; i++) {
26823                 if(fn.call(scope||cs[i], cs[i]) === true){
26824                     return cs[i];
26825                 }
26826         }
26827         return null;
26828     },
26829
26830     /**
26831      * Sorts this nodes children using the supplied sort function
26832      * @param {Function} fn
26833      * @param {Object} scope (optional)
26834      */
26835     sort : function(fn, scope){
26836         var cs = this.childNodes;
26837         var len = cs.length;
26838         if(len > 0){
26839             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26840             cs.sort(sortFn);
26841             for(var i = 0; i < len; i++){
26842                 var n = cs[i];
26843                 n.previousSibling = cs[i-1];
26844                 n.nextSibling = cs[i+1];
26845                 if(i == 0){
26846                     this.setFirstChild(n);
26847                 }
26848                 if(i == len-1){
26849                     this.setLastChild(n);
26850                 }
26851             }
26852         }
26853     },
26854
26855     /**
26856      * Returns true if this node is an ancestor (at any point) of the passed node.
26857      * @param {Node} node
26858      * @return {Boolean}
26859      */
26860     contains : function(node){
26861         return node.isAncestor(this);
26862     },
26863
26864     /**
26865      * Returns true if the passed node is an ancestor (at any point) of this node.
26866      * @param {Node} node
26867      * @return {Boolean}
26868      */
26869     isAncestor : function(node){
26870         var p = this.parentNode;
26871         while(p){
26872             if(p == node){
26873                 return true;
26874             }
26875             p = p.parentNode;
26876         }
26877         return false;
26878     },
26879
26880     toString : function(){
26881         return "[Node"+(this.id?" "+this.id:"")+"]";
26882     }
26883 });/*
26884  * Based on:
26885  * Ext JS Library 1.1.1
26886  * Copyright(c) 2006-2007, Ext JS, LLC.
26887  *
26888  * Originally Released Under LGPL - original licence link has changed is not relivant.
26889  *
26890  * Fork - LGPL
26891  * <script type="text/javascript">
26892  */
26893
26894
26895 /**
26896  * @class Roo.Shadow
26897  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26898  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26899  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26900  * @constructor
26901  * Create a new Shadow
26902  * @param {Object} config The config object
26903  */
26904 Roo.Shadow = function(config){
26905     Roo.apply(this, config);
26906     if(typeof this.mode != "string"){
26907         this.mode = this.defaultMode;
26908     }
26909     var o = this.offset, a = {h: 0};
26910     var rad = Math.floor(this.offset/2);
26911     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26912         case "drop":
26913             a.w = 0;
26914             a.l = a.t = o;
26915             a.t -= 1;
26916             if(Roo.isIE){
26917                 a.l -= this.offset + rad;
26918                 a.t -= this.offset + rad;
26919                 a.w -= rad;
26920                 a.h -= rad;
26921                 a.t += 1;
26922             }
26923         break;
26924         case "sides":
26925             a.w = (o*2);
26926             a.l = -o;
26927             a.t = o-1;
26928             if(Roo.isIE){
26929                 a.l -= (this.offset - rad);
26930                 a.t -= this.offset + rad;
26931                 a.l += 1;
26932                 a.w -= (this.offset - rad)*2;
26933                 a.w -= rad + 1;
26934                 a.h -= 1;
26935             }
26936         break;
26937         case "frame":
26938             a.w = a.h = (o*2);
26939             a.l = a.t = -o;
26940             a.t += 1;
26941             a.h -= 2;
26942             if(Roo.isIE){
26943                 a.l -= (this.offset - rad);
26944                 a.t -= (this.offset - rad);
26945                 a.l += 1;
26946                 a.w -= (this.offset + rad + 1);
26947                 a.h -= (this.offset + rad);
26948                 a.h += 1;
26949             }
26950         break;
26951     };
26952
26953     this.adjusts = a;
26954 };
26955
26956 Roo.Shadow.prototype = {
26957     /**
26958      * @cfg {String} mode
26959      * The shadow display mode.  Supports the following options:<br />
26960      * sides: Shadow displays on both sides and bottom only<br />
26961      * frame: Shadow displays equally on all four sides<br />
26962      * drop: Traditional bottom-right drop shadow (default)
26963      */
26964     mode: false,
26965     /**
26966      * @cfg {String} offset
26967      * The number of pixels to offset the shadow from the element (defaults to 4)
26968      */
26969     offset: 4,
26970
26971     // private
26972     defaultMode: "drop",
26973
26974     /**
26975      * Displays the shadow under the target element
26976      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
26977      */
26978     show : function(target){
26979         target = Roo.get(target);
26980         if(!this.el){
26981             this.el = Roo.Shadow.Pool.pull();
26982             if(this.el.dom.nextSibling != target.dom){
26983                 this.el.insertBefore(target);
26984             }
26985         }
26986         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
26987         if(Roo.isIE){
26988             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
26989         }
26990         this.realign(
26991             target.getLeft(true),
26992             target.getTop(true),
26993             target.getWidth(),
26994             target.getHeight()
26995         );
26996         this.el.dom.style.display = "block";
26997     },
26998
26999     /**
27000      * Returns true if the shadow is visible, else false
27001      */
27002     isVisible : function(){
27003         return this.el ? true : false;  
27004     },
27005
27006     /**
27007      * Direct alignment when values are already available. Show must be called at least once before
27008      * calling this method to ensure it is initialized.
27009      * @param {Number} left The target element left position
27010      * @param {Number} top The target element top position
27011      * @param {Number} width The target element width
27012      * @param {Number} height The target element height
27013      */
27014     realign : function(l, t, w, h){
27015         if(!this.el){
27016             return;
27017         }
27018         var a = this.adjusts, d = this.el.dom, s = d.style;
27019         var iea = 0;
27020         s.left = (l+a.l)+"px";
27021         s.top = (t+a.t)+"px";
27022         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27023  
27024         if(s.width != sws || s.height != shs){
27025             s.width = sws;
27026             s.height = shs;
27027             if(!Roo.isIE){
27028                 var cn = d.childNodes;
27029                 var sww = Math.max(0, (sw-12))+"px";
27030                 cn[0].childNodes[1].style.width = sww;
27031                 cn[1].childNodes[1].style.width = sww;
27032                 cn[2].childNodes[1].style.width = sww;
27033                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27034             }
27035         }
27036     },
27037
27038     /**
27039      * Hides this shadow
27040      */
27041     hide : function(){
27042         if(this.el){
27043             this.el.dom.style.display = "none";
27044             Roo.Shadow.Pool.push(this.el);
27045             delete this.el;
27046         }
27047     },
27048
27049     /**
27050      * Adjust the z-index of this shadow
27051      * @param {Number} zindex The new z-index
27052      */
27053     setZIndex : function(z){
27054         this.zIndex = z;
27055         if(this.el){
27056             this.el.setStyle("z-index", z);
27057         }
27058     }
27059 };
27060
27061 // Private utility class that manages the internal Shadow cache
27062 Roo.Shadow.Pool = function(){
27063     var p = [];
27064     var markup = Roo.isIE ?
27065                  '<div class="x-ie-shadow"></div>' :
27066                  '<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>';
27067     return {
27068         pull : function(){
27069             var sh = p.shift();
27070             if(!sh){
27071                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27072                 sh.autoBoxAdjust = false;
27073             }
27074             return sh;
27075         },
27076
27077         push : function(sh){
27078             p.push(sh);
27079         }
27080     };
27081 }();/*
27082  * Based on:
27083  * Ext JS Library 1.1.1
27084  * Copyright(c) 2006-2007, Ext JS, LLC.
27085  *
27086  * Originally Released Under LGPL - original licence link has changed is not relivant.
27087  *
27088  * Fork - LGPL
27089  * <script type="text/javascript">
27090  */
27091
27092
27093 /**
27094  * @class Roo.SplitBar
27095  * @extends Roo.util.Observable
27096  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27097  * <br><br>
27098  * Usage:
27099  * <pre><code>
27100 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27101                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27102 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27103 split.minSize = 100;
27104 split.maxSize = 600;
27105 split.animate = true;
27106 split.on('moved', splitterMoved);
27107 </code></pre>
27108  * @constructor
27109  * Create a new SplitBar
27110  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27111  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27112  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27113  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27114                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27115                         position of the SplitBar).
27116  */
27117 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27118     
27119     /** @private */
27120     this.el = Roo.get(dragElement, true);
27121     this.el.dom.unselectable = "on";
27122     /** @private */
27123     this.resizingEl = Roo.get(resizingElement, true);
27124
27125     /**
27126      * @private
27127      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27128      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27129      * @type Number
27130      */
27131     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27132     
27133     /**
27134      * The minimum size of the resizing element. (Defaults to 0)
27135      * @type Number
27136      */
27137     this.minSize = 0;
27138     
27139     /**
27140      * The maximum size of the resizing element. (Defaults to 2000)
27141      * @type Number
27142      */
27143     this.maxSize = 2000;
27144     
27145     /**
27146      * Whether to animate the transition to the new size
27147      * @type Boolean
27148      */
27149     this.animate = false;
27150     
27151     /**
27152      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27153      * @type Boolean
27154      */
27155     this.useShim = false;
27156     
27157     /** @private */
27158     this.shim = null;
27159     
27160     if(!existingProxy){
27161         /** @private */
27162         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27163     }else{
27164         this.proxy = Roo.get(existingProxy).dom;
27165     }
27166     /** @private */
27167     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27168     
27169     /** @private */
27170     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27171     
27172     /** @private */
27173     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27174     
27175     /** @private */
27176     this.dragSpecs = {};
27177     
27178     /**
27179      * @private The adapter to use to positon and resize elements
27180      */
27181     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27182     this.adapter.init(this);
27183     
27184     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27185         /** @private */
27186         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27187         this.el.addClass("x-splitbar-h");
27188     }else{
27189         /** @private */
27190         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27191         this.el.addClass("x-splitbar-v");
27192     }
27193     
27194     this.addEvents({
27195         /**
27196          * @event resize
27197          * Fires when the splitter is moved (alias for {@link #event-moved})
27198          * @param {Roo.SplitBar} this
27199          * @param {Number} newSize the new width or height
27200          */
27201         "resize" : true,
27202         /**
27203          * @event moved
27204          * Fires when the splitter is moved
27205          * @param {Roo.SplitBar} this
27206          * @param {Number} newSize the new width or height
27207          */
27208         "moved" : true,
27209         /**
27210          * @event beforeresize
27211          * Fires before the splitter is dragged
27212          * @param {Roo.SplitBar} this
27213          */
27214         "beforeresize" : true,
27215
27216         "beforeapply" : true
27217     });
27218
27219     Roo.util.Observable.call(this);
27220 };
27221
27222 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27223     onStartProxyDrag : function(x, y){
27224         this.fireEvent("beforeresize", this);
27225         if(!this.overlay){
27226             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27227             o.unselectable();
27228             o.enableDisplayMode("block");
27229             // all splitbars share the same overlay
27230             Roo.SplitBar.prototype.overlay = o;
27231         }
27232         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27233         this.overlay.show();
27234         Roo.get(this.proxy).setDisplayed("block");
27235         var size = this.adapter.getElementSize(this);
27236         this.activeMinSize = this.getMinimumSize();;
27237         this.activeMaxSize = this.getMaximumSize();;
27238         var c1 = size - this.activeMinSize;
27239         var c2 = Math.max(this.activeMaxSize - size, 0);
27240         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27241             this.dd.resetConstraints();
27242             this.dd.setXConstraint(
27243                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27244                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27245             );
27246             this.dd.setYConstraint(0, 0);
27247         }else{
27248             this.dd.resetConstraints();
27249             this.dd.setXConstraint(0, 0);
27250             this.dd.setYConstraint(
27251                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27252                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27253             );
27254          }
27255         this.dragSpecs.startSize = size;
27256         this.dragSpecs.startPoint = [x, y];
27257         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27258     },
27259     
27260     /** 
27261      * @private Called after the drag operation by the DDProxy
27262      */
27263     onEndProxyDrag : function(e){
27264         Roo.get(this.proxy).setDisplayed(false);
27265         var endPoint = Roo.lib.Event.getXY(e);
27266         if(this.overlay){
27267             this.overlay.hide();
27268         }
27269         var newSize;
27270         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27271             newSize = this.dragSpecs.startSize + 
27272                 (this.placement == Roo.SplitBar.LEFT ?
27273                     endPoint[0] - this.dragSpecs.startPoint[0] :
27274                     this.dragSpecs.startPoint[0] - endPoint[0]
27275                 );
27276         }else{
27277             newSize = this.dragSpecs.startSize + 
27278                 (this.placement == Roo.SplitBar.TOP ?
27279                     endPoint[1] - this.dragSpecs.startPoint[1] :
27280                     this.dragSpecs.startPoint[1] - endPoint[1]
27281                 );
27282         }
27283         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27284         if(newSize != this.dragSpecs.startSize){
27285             if(this.fireEvent('beforeapply', this, newSize) !== false){
27286                 this.adapter.setElementSize(this, newSize);
27287                 this.fireEvent("moved", this, newSize);
27288                 this.fireEvent("resize", this, newSize);
27289             }
27290         }
27291     },
27292     
27293     /**
27294      * Get the adapter this SplitBar uses
27295      * @return The adapter object
27296      */
27297     getAdapter : function(){
27298         return this.adapter;
27299     },
27300     
27301     /**
27302      * Set the adapter this SplitBar uses
27303      * @param {Object} adapter A SplitBar adapter object
27304      */
27305     setAdapter : function(adapter){
27306         this.adapter = adapter;
27307         this.adapter.init(this);
27308     },
27309     
27310     /**
27311      * Gets the minimum size for the resizing element
27312      * @return {Number} The minimum size
27313      */
27314     getMinimumSize : function(){
27315         return this.minSize;
27316     },
27317     
27318     /**
27319      * Sets the minimum size for the resizing element
27320      * @param {Number} minSize The minimum size
27321      */
27322     setMinimumSize : function(minSize){
27323         this.minSize = minSize;
27324     },
27325     
27326     /**
27327      * Gets the maximum size for the resizing element
27328      * @return {Number} The maximum size
27329      */
27330     getMaximumSize : function(){
27331         return this.maxSize;
27332     },
27333     
27334     /**
27335      * Sets the maximum size for the resizing element
27336      * @param {Number} maxSize The maximum size
27337      */
27338     setMaximumSize : function(maxSize){
27339         this.maxSize = maxSize;
27340     },
27341     
27342     /**
27343      * Sets the initialize size for the resizing element
27344      * @param {Number} size The initial size
27345      */
27346     setCurrentSize : function(size){
27347         var oldAnimate = this.animate;
27348         this.animate = false;
27349         this.adapter.setElementSize(this, size);
27350         this.animate = oldAnimate;
27351     },
27352     
27353     /**
27354      * Destroy this splitbar. 
27355      * @param {Boolean} removeEl True to remove the element
27356      */
27357     destroy : function(removeEl){
27358         if(this.shim){
27359             this.shim.remove();
27360         }
27361         this.dd.unreg();
27362         this.proxy.parentNode.removeChild(this.proxy);
27363         if(removeEl){
27364             this.el.remove();
27365         }
27366     }
27367 });
27368
27369 /**
27370  * @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.
27371  */
27372 Roo.SplitBar.createProxy = function(dir){
27373     var proxy = new Roo.Element(document.createElement("div"));
27374     proxy.unselectable();
27375     var cls = 'x-splitbar-proxy';
27376     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27377     document.body.appendChild(proxy.dom);
27378     return proxy.dom;
27379 };
27380
27381 /** 
27382  * @class Roo.SplitBar.BasicLayoutAdapter
27383  * Default Adapter. It assumes the splitter and resizing element are not positioned
27384  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27385  */
27386 Roo.SplitBar.BasicLayoutAdapter = function(){
27387 };
27388
27389 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27390     // do nothing for now
27391     init : function(s){
27392     
27393     },
27394     /**
27395      * Called before drag operations to get the current size of the resizing element. 
27396      * @param {Roo.SplitBar} s The SplitBar using this adapter
27397      */
27398      getElementSize : function(s){
27399         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27400             return s.resizingEl.getWidth();
27401         }else{
27402             return s.resizingEl.getHeight();
27403         }
27404     },
27405     
27406     /**
27407      * Called after drag operations to set the size of the resizing element.
27408      * @param {Roo.SplitBar} s The SplitBar using this adapter
27409      * @param {Number} newSize The new size to set
27410      * @param {Function} onComplete A function to be invoked when resizing is complete
27411      */
27412     setElementSize : function(s, newSize, onComplete){
27413         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27414             if(!s.animate){
27415                 s.resizingEl.setWidth(newSize);
27416                 if(onComplete){
27417                     onComplete(s, newSize);
27418                 }
27419             }else{
27420                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27421             }
27422         }else{
27423             
27424             if(!s.animate){
27425                 s.resizingEl.setHeight(newSize);
27426                 if(onComplete){
27427                     onComplete(s, newSize);
27428                 }
27429             }else{
27430                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27431             }
27432         }
27433     }
27434 };
27435
27436 /** 
27437  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27438  * @extends Roo.SplitBar.BasicLayoutAdapter
27439  * Adapter that  moves the splitter element to align with the resized sizing element. 
27440  * Used with an absolute positioned SplitBar.
27441  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27442  * document.body, make sure you assign an id to the body element.
27443  */
27444 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27445     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27446     this.container = Roo.get(container);
27447 };
27448
27449 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27450     init : function(s){
27451         this.basic.init(s);
27452     },
27453     
27454     getElementSize : function(s){
27455         return this.basic.getElementSize(s);
27456     },
27457     
27458     setElementSize : function(s, newSize, onComplete){
27459         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27460     },
27461     
27462     moveSplitter : function(s){
27463         var yes = Roo.SplitBar;
27464         switch(s.placement){
27465             case yes.LEFT:
27466                 s.el.setX(s.resizingEl.getRight());
27467                 break;
27468             case yes.RIGHT:
27469                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27470                 break;
27471             case yes.TOP:
27472                 s.el.setY(s.resizingEl.getBottom());
27473                 break;
27474             case yes.BOTTOM:
27475                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27476                 break;
27477         }
27478     }
27479 };
27480
27481 /**
27482  * Orientation constant - Create a vertical SplitBar
27483  * @static
27484  * @type Number
27485  */
27486 Roo.SplitBar.VERTICAL = 1;
27487
27488 /**
27489  * Orientation constant - Create a horizontal SplitBar
27490  * @static
27491  * @type Number
27492  */
27493 Roo.SplitBar.HORIZONTAL = 2;
27494
27495 /**
27496  * Placement constant - The resizing element is to the left of the splitter element
27497  * @static
27498  * @type Number
27499  */
27500 Roo.SplitBar.LEFT = 1;
27501
27502 /**
27503  * Placement constant - The resizing element is to the right of the splitter element
27504  * @static
27505  * @type Number
27506  */
27507 Roo.SplitBar.RIGHT = 2;
27508
27509 /**
27510  * Placement constant - The resizing element is positioned above the splitter element
27511  * @static
27512  * @type Number
27513  */
27514 Roo.SplitBar.TOP = 3;
27515
27516 /**
27517  * Placement constant - The resizing element is positioned under splitter element
27518  * @static
27519  * @type Number
27520  */
27521 Roo.SplitBar.BOTTOM = 4;
27522 /*
27523  * Based on:
27524  * Ext JS Library 1.1.1
27525  * Copyright(c) 2006-2007, Ext JS, LLC.
27526  *
27527  * Originally Released Under LGPL - original licence link has changed is not relivant.
27528  *
27529  * Fork - LGPL
27530  * <script type="text/javascript">
27531  */
27532
27533 /**
27534  * @class Roo.View
27535  * @extends Roo.util.Observable
27536  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27537  * This class also supports single and multi selection modes. <br>
27538  * Create a data model bound view:
27539  <pre><code>
27540  var store = new Roo.data.Store(...);
27541
27542  var view = new Roo.View({
27543     el : "my-element",
27544     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27545  
27546     singleSelect: true,
27547     selectedClass: "ydataview-selected",
27548     store: store
27549  });
27550
27551  // listen for node click?
27552  view.on("click", function(vw, index, node, e){
27553  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27554  });
27555
27556  // load XML data
27557  dataModel.load("foobar.xml");
27558  </code></pre>
27559  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27560  * <br><br>
27561  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27562  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27563  * 
27564  * Note: old style constructor is still suported (container, template, config)
27565  * 
27566  * @constructor
27567  * Create a new View
27568  * @param {Object} config The config object
27569  * 
27570  */
27571 Roo.View = function(config, depreciated_tpl, depreciated_config){
27572     
27573     this.parent = false;
27574     
27575     if (typeof(depreciated_tpl) == 'undefined') {
27576         // new way.. - universal constructor.
27577         Roo.apply(this, config);
27578         this.el  = Roo.get(this.el);
27579     } else {
27580         // old format..
27581         this.el  = Roo.get(config);
27582         this.tpl = depreciated_tpl;
27583         Roo.apply(this, depreciated_config);
27584     }
27585     this.wrapEl  = this.el.wrap().wrap();
27586     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27587     
27588     
27589     if(typeof(this.tpl) == "string"){
27590         this.tpl = new Roo.Template(this.tpl);
27591     } else {
27592         // support xtype ctors..
27593         this.tpl = new Roo.factory(this.tpl, Roo);
27594     }
27595     
27596     
27597     this.tpl.compile();
27598     
27599     /** @private */
27600     this.addEvents({
27601         /**
27602          * @event beforeclick
27603          * Fires before a click is processed. Returns false to cancel the default action.
27604          * @param {Roo.View} this
27605          * @param {Number} index The index of the target node
27606          * @param {HTMLElement} node The target node
27607          * @param {Roo.EventObject} e The raw event object
27608          */
27609             "beforeclick" : true,
27610         /**
27611          * @event click
27612          * Fires when a template node is clicked.
27613          * @param {Roo.View} this
27614          * @param {Number} index The index of the target node
27615          * @param {HTMLElement} node The target node
27616          * @param {Roo.EventObject} e The raw event object
27617          */
27618             "click" : true,
27619         /**
27620          * @event dblclick
27621          * Fires when a template node is double clicked.
27622          * @param {Roo.View} this
27623          * @param {Number} index The index of the target node
27624          * @param {HTMLElement} node The target node
27625          * @param {Roo.EventObject} e The raw event object
27626          */
27627             "dblclick" : true,
27628         /**
27629          * @event contextmenu
27630          * Fires when a template node is right clicked.
27631          * @param {Roo.View} this
27632          * @param {Number} index The index of the target node
27633          * @param {HTMLElement} node The target node
27634          * @param {Roo.EventObject} e The raw event object
27635          */
27636             "contextmenu" : true,
27637         /**
27638          * @event selectionchange
27639          * Fires when the selected nodes change.
27640          * @param {Roo.View} this
27641          * @param {Array} selections Array of the selected nodes
27642          */
27643             "selectionchange" : true,
27644     
27645         /**
27646          * @event beforeselect
27647          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27648          * @param {Roo.View} this
27649          * @param {HTMLElement} node The node to be selected
27650          * @param {Array} selections Array of currently selected nodes
27651          */
27652             "beforeselect" : true,
27653         /**
27654          * @event preparedata
27655          * Fires on every row to render, to allow you to change the data.
27656          * @param {Roo.View} this
27657          * @param {Object} data to be rendered (change this)
27658          */
27659           "preparedata" : true
27660           
27661           
27662         });
27663
27664
27665
27666     this.el.on({
27667         "click": this.onClick,
27668         "dblclick": this.onDblClick,
27669         "contextmenu": this.onContextMenu,
27670         scope:this
27671     });
27672
27673     this.selections = [];
27674     this.nodes = [];
27675     this.cmp = new Roo.CompositeElementLite([]);
27676     if(this.store){
27677         this.store = Roo.factory(this.store, Roo.data);
27678         this.setStore(this.store, true);
27679     }
27680     
27681     if ( this.footer && this.footer.xtype) {
27682            
27683          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27684         
27685         this.footer.dataSource = this.store;
27686         this.footer.container = fctr;
27687         this.footer = Roo.factory(this.footer, Roo);
27688         fctr.insertFirst(this.el);
27689         
27690         // this is a bit insane - as the paging toolbar seems to detach the el..
27691 //        dom.parentNode.parentNode.parentNode
27692          // they get detached?
27693     }
27694     
27695     
27696     Roo.View.superclass.constructor.call(this);
27697     
27698     
27699 };
27700
27701 Roo.extend(Roo.View, Roo.util.Observable, {
27702     
27703      /**
27704      * @cfg {Roo.data.Store} store Data store to load data from.
27705      */
27706     store : false,
27707     
27708     /**
27709      * @cfg {String|Roo.Element} el The container element.
27710      */
27711     el : '',
27712     
27713     /**
27714      * @cfg {String|Roo.Template} tpl The template used by this View 
27715      */
27716     tpl : false,
27717     /**
27718      * @cfg {String} dataName the named area of the template to use as the data area
27719      *                          Works with domtemplates roo-name="name"
27720      */
27721     dataName: false,
27722     /**
27723      * @cfg {String} selectedClass The css class to add to selected nodes
27724      */
27725     selectedClass : "x-view-selected",
27726      /**
27727      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27728      */
27729     emptyText : "",
27730     
27731     /**
27732      * @cfg {String} text to display on mask (default Loading)
27733      */
27734     mask : false,
27735     /**
27736      * @cfg {Boolean} multiSelect Allow multiple selection
27737      */
27738     multiSelect : false,
27739     /**
27740      * @cfg {Boolean} singleSelect Allow single selection
27741      */
27742     singleSelect:  false,
27743     
27744     /**
27745      * @cfg {Boolean} toggleSelect - selecting 
27746      */
27747     toggleSelect : false,
27748     
27749     /**
27750      * @cfg {Boolean} tickable - selecting 
27751      */
27752     tickable : false,
27753     
27754     /**
27755      * Returns the element this view is bound to.
27756      * @return {Roo.Element}
27757      */
27758     getEl : function(){
27759         return this.wrapEl;
27760     },
27761     
27762     
27763
27764     /**
27765      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27766      */
27767     refresh : function(){
27768         //Roo.log('refresh');
27769         var t = this.tpl;
27770         
27771         // if we are using something like 'domtemplate', then
27772         // the what gets used is:
27773         // t.applySubtemplate(NAME, data, wrapping data..)
27774         // the outer template then get' applied with
27775         //     the store 'extra data'
27776         // and the body get's added to the
27777         //      roo-name="data" node?
27778         //      <span class='roo-tpl-{name}'></span> ?????
27779         
27780         
27781         
27782         this.clearSelections();
27783         this.el.update("");
27784         var html = [];
27785         var records = this.store.getRange();
27786         if(records.length < 1) {
27787             
27788             // is this valid??  = should it render a template??
27789             
27790             this.el.update(this.emptyText);
27791             return;
27792         }
27793         var el = this.el;
27794         if (this.dataName) {
27795             this.el.update(t.apply(this.store.meta)); //????
27796             el = this.el.child('.roo-tpl-' + this.dataName);
27797         }
27798         
27799         for(var i = 0, len = records.length; i < len; i++){
27800             var data = this.prepareData(records[i].data, i, records[i]);
27801             this.fireEvent("preparedata", this, data, i, records[i]);
27802             
27803             var d = Roo.apply({}, data);
27804             
27805             if(this.tickable){
27806                 Roo.apply(d, {'roo-id' : Roo.id()});
27807                 
27808                 var _this = this;
27809             
27810                 Roo.each(this.parent.item, function(item){
27811                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27812                         return;
27813                     }
27814                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27815                 });
27816             }
27817             
27818             html[html.length] = Roo.util.Format.trim(
27819                 this.dataName ?
27820                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27821                     t.apply(d)
27822             );
27823         }
27824         
27825         
27826         
27827         el.update(html.join(""));
27828         this.nodes = el.dom.childNodes;
27829         this.updateIndexes(0);
27830     },
27831     
27832
27833     /**
27834      * Function to override to reformat the data that is sent to
27835      * the template for each node.
27836      * DEPRICATED - use the preparedata event handler.
27837      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27838      * a JSON object for an UpdateManager bound view).
27839      */
27840     prepareData : function(data, index, record)
27841     {
27842         this.fireEvent("preparedata", this, data, index, record);
27843         return data;
27844     },
27845
27846     onUpdate : function(ds, record){
27847         // Roo.log('on update');   
27848         this.clearSelections();
27849         var index = this.store.indexOf(record);
27850         var n = this.nodes[index];
27851         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27852         n.parentNode.removeChild(n);
27853         this.updateIndexes(index, index);
27854     },
27855
27856     
27857     
27858 // --------- FIXME     
27859     onAdd : function(ds, records, index)
27860     {
27861         //Roo.log(['on Add', ds, records, index] );        
27862         this.clearSelections();
27863         if(this.nodes.length == 0){
27864             this.refresh();
27865             return;
27866         }
27867         var n = this.nodes[index];
27868         for(var i = 0, len = records.length; i < len; i++){
27869             var d = this.prepareData(records[i].data, i, records[i]);
27870             if(n){
27871                 this.tpl.insertBefore(n, d);
27872             }else{
27873                 
27874                 this.tpl.append(this.el, d);
27875             }
27876         }
27877         this.updateIndexes(index);
27878     },
27879
27880     onRemove : function(ds, record, index){
27881        // Roo.log('onRemove');
27882         this.clearSelections();
27883         var el = this.dataName  ?
27884             this.el.child('.roo-tpl-' + this.dataName) :
27885             this.el; 
27886         
27887         el.dom.removeChild(this.nodes[index]);
27888         this.updateIndexes(index);
27889     },
27890
27891     /**
27892      * Refresh an individual node.
27893      * @param {Number} index
27894      */
27895     refreshNode : function(index){
27896         this.onUpdate(this.store, this.store.getAt(index));
27897     },
27898
27899     updateIndexes : function(startIndex, endIndex){
27900         var ns = this.nodes;
27901         startIndex = startIndex || 0;
27902         endIndex = endIndex || ns.length - 1;
27903         for(var i = startIndex; i <= endIndex; i++){
27904             ns[i].nodeIndex = i;
27905         }
27906     },
27907
27908     /**
27909      * Changes the data store this view uses and refresh the view.
27910      * @param {Store} store
27911      */
27912     setStore : function(store, initial){
27913         if(!initial && this.store){
27914             this.store.un("datachanged", this.refresh);
27915             this.store.un("add", this.onAdd);
27916             this.store.un("remove", this.onRemove);
27917             this.store.un("update", this.onUpdate);
27918             this.store.un("clear", this.refresh);
27919             this.store.un("beforeload", this.onBeforeLoad);
27920             this.store.un("load", this.onLoad);
27921             this.store.un("loadexception", this.onLoad);
27922         }
27923         if(store){
27924           
27925             store.on("datachanged", this.refresh, this);
27926             store.on("add", this.onAdd, this);
27927             store.on("remove", this.onRemove, this);
27928             store.on("update", this.onUpdate, this);
27929             store.on("clear", this.refresh, this);
27930             store.on("beforeload", this.onBeforeLoad, this);
27931             store.on("load", this.onLoad, this);
27932             store.on("loadexception", this.onLoad, this);
27933         }
27934         
27935         if(store){
27936             this.refresh();
27937         }
27938     },
27939     /**
27940      * onbeforeLoad - masks the loading area.
27941      *
27942      */
27943     onBeforeLoad : function(store,opts)
27944     {
27945          //Roo.log('onBeforeLoad');   
27946         if (!opts.add) {
27947             this.el.update("");
27948         }
27949         this.el.mask(this.mask ? this.mask : "Loading" ); 
27950     },
27951     onLoad : function ()
27952     {
27953         this.el.unmask();
27954     },
27955     
27956
27957     /**
27958      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
27959      * @param {HTMLElement} node
27960      * @return {HTMLElement} The template node
27961      */
27962     findItemFromChild : function(node){
27963         var el = this.dataName  ?
27964             this.el.child('.roo-tpl-' + this.dataName,true) :
27965             this.el.dom; 
27966         
27967         if(!node || node.parentNode == el){
27968                     return node;
27969             }
27970             var p = node.parentNode;
27971             while(p && p != el){
27972             if(p.parentNode == el){
27973                 return p;
27974             }
27975             p = p.parentNode;
27976         }
27977             return null;
27978     },
27979
27980     /** @ignore */
27981     onClick : function(e){
27982         var item = this.findItemFromChild(e.getTarget());
27983         if(item){
27984             var index = this.indexOf(item);
27985             if(this.onItemClick(item, index, e) !== false){
27986                 this.fireEvent("click", this, index, item, e);
27987             }
27988         }else{
27989             this.clearSelections();
27990         }
27991     },
27992
27993     /** @ignore */
27994     onContextMenu : function(e){
27995         var item = this.findItemFromChild(e.getTarget());
27996         if(item){
27997             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
27998         }
27999     },
28000
28001     /** @ignore */
28002     onDblClick : function(e){
28003         var item = this.findItemFromChild(e.getTarget());
28004         if(item){
28005             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28006         }
28007     },
28008
28009     onItemClick : function(item, index, e)
28010     {
28011         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28012             return false;
28013         }
28014         if (this.toggleSelect) {
28015             var m = this.isSelected(item) ? 'unselect' : 'select';
28016             //Roo.log(m);
28017             var _t = this;
28018             _t[m](item, true, false);
28019             return true;
28020         }
28021         if(this.multiSelect || this.singleSelect){
28022             if(this.multiSelect && e.shiftKey && this.lastSelection){
28023                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28024             }else{
28025                 this.select(item, this.multiSelect && e.ctrlKey);
28026                 this.lastSelection = item;
28027             }
28028             
28029             if(!this.tickable){
28030                 e.preventDefault();
28031             }
28032             
28033         }
28034         return true;
28035     },
28036
28037     /**
28038      * Get the number of selected nodes.
28039      * @return {Number}
28040      */
28041     getSelectionCount : function(){
28042         return this.selections.length;
28043     },
28044
28045     /**
28046      * Get the currently selected nodes.
28047      * @return {Array} An array of HTMLElements
28048      */
28049     getSelectedNodes : function(){
28050         return this.selections;
28051     },
28052
28053     /**
28054      * Get the indexes of the selected nodes.
28055      * @return {Array}
28056      */
28057     getSelectedIndexes : function(){
28058         var indexes = [], s = this.selections;
28059         for(var i = 0, len = s.length; i < len; i++){
28060             indexes.push(s[i].nodeIndex);
28061         }
28062         return indexes;
28063     },
28064
28065     /**
28066      * Clear all selections
28067      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28068      */
28069     clearSelections : function(suppressEvent){
28070         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28071             this.cmp.elements = this.selections;
28072             this.cmp.removeClass(this.selectedClass);
28073             this.selections = [];
28074             if(!suppressEvent){
28075                 this.fireEvent("selectionchange", this, this.selections);
28076             }
28077         }
28078     },
28079
28080     /**
28081      * Returns true if the passed node is selected
28082      * @param {HTMLElement/Number} node The node or node index
28083      * @return {Boolean}
28084      */
28085     isSelected : function(node){
28086         var s = this.selections;
28087         if(s.length < 1){
28088             return false;
28089         }
28090         node = this.getNode(node);
28091         return s.indexOf(node) !== -1;
28092     },
28093
28094     /**
28095      * Selects nodes.
28096      * @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
28097      * @param {Boolean} keepExisting (optional) true to keep existing selections
28098      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28099      */
28100     select : function(nodeInfo, keepExisting, suppressEvent){
28101         if(nodeInfo instanceof Array){
28102             if(!keepExisting){
28103                 this.clearSelections(true);
28104             }
28105             for(var i = 0, len = nodeInfo.length; i < len; i++){
28106                 this.select(nodeInfo[i], true, true);
28107             }
28108             return;
28109         } 
28110         var node = this.getNode(nodeInfo);
28111         if(!node || this.isSelected(node)){
28112             return; // already selected.
28113         }
28114         if(!keepExisting){
28115             this.clearSelections(true);
28116         }
28117         
28118         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28119             Roo.fly(node).addClass(this.selectedClass);
28120             this.selections.push(node);
28121             if(!suppressEvent){
28122                 this.fireEvent("selectionchange", this, this.selections);
28123             }
28124         }
28125         
28126         
28127     },
28128       /**
28129      * Unselects nodes.
28130      * @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
28131      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28132      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28133      */
28134     unselect : function(nodeInfo, keepExisting, suppressEvent)
28135     {
28136         if(nodeInfo instanceof Array){
28137             Roo.each(this.selections, function(s) {
28138                 this.unselect(s, nodeInfo);
28139             }, this);
28140             return;
28141         }
28142         var node = this.getNode(nodeInfo);
28143         if(!node || !this.isSelected(node)){
28144             //Roo.log("not selected");
28145             return; // not selected.
28146         }
28147         // fireevent???
28148         var ns = [];
28149         Roo.each(this.selections, function(s) {
28150             if (s == node ) {
28151                 Roo.fly(node).removeClass(this.selectedClass);
28152
28153                 return;
28154             }
28155             ns.push(s);
28156         },this);
28157         
28158         this.selections= ns;
28159         this.fireEvent("selectionchange", this, this.selections);
28160     },
28161
28162     /**
28163      * Gets a template node.
28164      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28165      * @return {HTMLElement} The node or null if it wasn't found
28166      */
28167     getNode : function(nodeInfo){
28168         if(typeof nodeInfo == "string"){
28169             return document.getElementById(nodeInfo);
28170         }else if(typeof nodeInfo == "number"){
28171             return this.nodes[nodeInfo];
28172         }
28173         return nodeInfo;
28174     },
28175
28176     /**
28177      * Gets a range template nodes.
28178      * @param {Number} startIndex
28179      * @param {Number} endIndex
28180      * @return {Array} An array of nodes
28181      */
28182     getNodes : function(start, end){
28183         var ns = this.nodes;
28184         start = start || 0;
28185         end = typeof end == "undefined" ? ns.length - 1 : end;
28186         var nodes = [];
28187         if(start <= end){
28188             for(var i = start; i <= end; i++){
28189                 nodes.push(ns[i]);
28190             }
28191         } else{
28192             for(var i = start; i >= end; i--){
28193                 nodes.push(ns[i]);
28194             }
28195         }
28196         return nodes;
28197     },
28198
28199     /**
28200      * Finds the index of the passed node
28201      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28202      * @return {Number} The index of the node or -1
28203      */
28204     indexOf : function(node){
28205         node = this.getNode(node);
28206         if(typeof node.nodeIndex == "number"){
28207             return node.nodeIndex;
28208         }
28209         var ns = this.nodes;
28210         for(var i = 0, len = ns.length; i < len; i++){
28211             if(ns[i] == node){
28212                 return i;
28213             }
28214         }
28215         return -1;
28216     }
28217 });
28218 /*
28219  * Based on:
28220  * Ext JS Library 1.1.1
28221  * Copyright(c) 2006-2007, Ext JS, LLC.
28222  *
28223  * Originally Released Under LGPL - original licence link has changed is not relivant.
28224  *
28225  * Fork - LGPL
28226  * <script type="text/javascript">
28227  */
28228
28229 /**
28230  * @class Roo.JsonView
28231  * @extends Roo.View
28232  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28233 <pre><code>
28234 var view = new Roo.JsonView({
28235     container: "my-element",
28236     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28237     multiSelect: true, 
28238     jsonRoot: "data" 
28239 });
28240
28241 // listen for node click?
28242 view.on("click", function(vw, index, node, e){
28243     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28244 });
28245
28246 // direct load of JSON data
28247 view.load("foobar.php");
28248
28249 // Example from my blog list
28250 var tpl = new Roo.Template(
28251     '&lt;div class="entry"&gt;' +
28252     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28253     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28254     "&lt;/div&gt;&lt;hr /&gt;"
28255 );
28256
28257 var moreView = new Roo.JsonView({
28258     container :  "entry-list", 
28259     template : tpl,
28260     jsonRoot: "posts"
28261 });
28262 moreView.on("beforerender", this.sortEntries, this);
28263 moreView.load({
28264     url: "/blog/get-posts.php",
28265     params: "allposts=true",
28266     text: "Loading Blog Entries..."
28267 });
28268 </code></pre>
28269
28270 * Note: old code is supported with arguments : (container, template, config)
28271
28272
28273  * @constructor
28274  * Create a new JsonView
28275  * 
28276  * @param {Object} config The config object
28277  * 
28278  */
28279 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28280     
28281     
28282     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28283
28284     var um = this.el.getUpdateManager();
28285     um.setRenderer(this);
28286     um.on("update", this.onLoad, this);
28287     um.on("failure", this.onLoadException, this);
28288
28289     /**
28290      * @event beforerender
28291      * Fires before rendering of the downloaded JSON data.
28292      * @param {Roo.JsonView} this
28293      * @param {Object} data The JSON data loaded
28294      */
28295     /**
28296      * @event load
28297      * Fires when data is loaded.
28298      * @param {Roo.JsonView} this
28299      * @param {Object} data The JSON data loaded
28300      * @param {Object} response The raw Connect response object
28301      */
28302     /**
28303      * @event loadexception
28304      * Fires when loading fails.
28305      * @param {Roo.JsonView} this
28306      * @param {Object} response The raw Connect response object
28307      */
28308     this.addEvents({
28309         'beforerender' : true,
28310         'load' : true,
28311         'loadexception' : true
28312     });
28313 };
28314 Roo.extend(Roo.JsonView, Roo.View, {
28315     /**
28316      * @type {String} The root property in the loaded JSON object that contains the data
28317      */
28318     jsonRoot : "",
28319
28320     /**
28321      * Refreshes the view.
28322      */
28323     refresh : function(){
28324         this.clearSelections();
28325         this.el.update("");
28326         var html = [];
28327         var o = this.jsonData;
28328         if(o && o.length > 0){
28329             for(var i = 0, len = o.length; i < len; i++){
28330                 var data = this.prepareData(o[i], i, o);
28331                 html[html.length] = this.tpl.apply(data);
28332             }
28333         }else{
28334             html.push(this.emptyText);
28335         }
28336         this.el.update(html.join(""));
28337         this.nodes = this.el.dom.childNodes;
28338         this.updateIndexes(0);
28339     },
28340
28341     /**
28342      * 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.
28343      * @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:
28344      <pre><code>
28345      view.load({
28346          url: "your-url.php",
28347          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28348          callback: yourFunction,
28349          scope: yourObject, //(optional scope)
28350          discardUrl: false,
28351          nocache: false,
28352          text: "Loading...",
28353          timeout: 30,
28354          scripts: false
28355      });
28356      </code></pre>
28357      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28358      * 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.
28359      * @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}
28360      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28361      * @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.
28362      */
28363     load : function(){
28364         var um = this.el.getUpdateManager();
28365         um.update.apply(um, arguments);
28366     },
28367
28368     // note - render is a standard framework call...
28369     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28370     render : function(el, response){
28371         
28372         this.clearSelections();
28373         this.el.update("");
28374         var o;
28375         try{
28376             if (response != '') {
28377                 o = Roo.util.JSON.decode(response.responseText);
28378                 if(this.jsonRoot){
28379                     
28380                     o = o[this.jsonRoot];
28381                 }
28382             }
28383         } catch(e){
28384         }
28385         /**
28386          * The current JSON data or null
28387          */
28388         this.jsonData = o;
28389         this.beforeRender();
28390         this.refresh();
28391     },
28392
28393 /**
28394  * Get the number of records in the current JSON dataset
28395  * @return {Number}
28396  */
28397     getCount : function(){
28398         return this.jsonData ? this.jsonData.length : 0;
28399     },
28400
28401 /**
28402  * Returns the JSON object for the specified node(s)
28403  * @param {HTMLElement/Array} node The node or an array of nodes
28404  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28405  * you get the JSON object for the node
28406  */
28407     getNodeData : function(node){
28408         if(node instanceof Array){
28409             var data = [];
28410             for(var i = 0, len = node.length; i < len; i++){
28411                 data.push(this.getNodeData(node[i]));
28412             }
28413             return data;
28414         }
28415         return this.jsonData[this.indexOf(node)] || null;
28416     },
28417
28418     beforeRender : function(){
28419         this.snapshot = this.jsonData;
28420         if(this.sortInfo){
28421             this.sort.apply(this, this.sortInfo);
28422         }
28423         this.fireEvent("beforerender", this, this.jsonData);
28424     },
28425
28426     onLoad : function(el, o){
28427         this.fireEvent("load", this, this.jsonData, o);
28428     },
28429
28430     onLoadException : function(el, o){
28431         this.fireEvent("loadexception", this, o);
28432     },
28433
28434 /**
28435  * Filter the data by a specific property.
28436  * @param {String} property A property on your JSON objects
28437  * @param {String/RegExp} value Either string that the property values
28438  * should start with, or a RegExp to test against the property
28439  */
28440     filter : function(property, value){
28441         if(this.jsonData){
28442             var data = [];
28443             var ss = this.snapshot;
28444             if(typeof value == "string"){
28445                 var vlen = value.length;
28446                 if(vlen == 0){
28447                     this.clearFilter();
28448                     return;
28449                 }
28450                 value = value.toLowerCase();
28451                 for(var i = 0, len = ss.length; i < len; i++){
28452                     var o = ss[i];
28453                     if(o[property].substr(0, vlen).toLowerCase() == value){
28454                         data.push(o);
28455                     }
28456                 }
28457             } else if(value.exec){ // regex?
28458                 for(var i = 0, len = ss.length; i < len; i++){
28459                     var o = ss[i];
28460                     if(value.test(o[property])){
28461                         data.push(o);
28462                     }
28463                 }
28464             } else{
28465                 return;
28466             }
28467             this.jsonData = data;
28468             this.refresh();
28469         }
28470     },
28471
28472 /**
28473  * Filter by a function. The passed function will be called with each
28474  * object in the current dataset. If the function returns true the value is kept,
28475  * otherwise it is filtered.
28476  * @param {Function} fn
28477  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28478  */
28479     filterBy : function(fn, scope){
28480         if(this.jsonData){
28481             var data = [];
28482             var ss = this.snapshot;
28483             for(var i = 0, len = ss.length; i < len; i++){
28484                 var o = ss[i];
28485                 if(fn.call(scope || this, o)){
28486                     data.push(o);
28487                 }
28488             }
28489             this.jsonData = data;
28490             this.refresh();
28491         }
28492     },
28493
28494 /**
28495  * Clears the current filter.
28496  */
28497     clearFilter : function(){
28498         if(this.snapshot && this.jsonData != this.snapshot){
28499             this.jsonData = this.snapshot;
28500             this.refresh();
28501         }
28502     },
28503
28504
28505 /**
28506  * Sorts the data for this view and refreshes it.
28507  * @param {String} property A property on your JSON objects to sort on
28508  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28509  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28510  */
28511     sort : function(property, dir, sortType){
28512         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28513         if(this.jsonData){
28514             var p = property;
28515             var dsc = dir && dir.toLowerCase() == "desc";
28516             var f = function(o1, o2){
28517                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28518                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28519                 ;
28520                 if(v1 < v2){
28521                     return dsc ? +1 : -1;
28522                 } else if(v1 > v2){
28523                     return dsc ? -1 : +1;
28524                 } else{
28525                     return 0;
28526                 }
28527             };
28528             this.jsonData.sort(f);
28529             this.refresh();
28530             if(this.jsonData != this.snapshot){
28531                 this.snapshot.sort(f);
28532             }
28533         }
28534     }
28535 });/*
28536  * Based on:
28537  * Ext JS Library 1.1.1
28538  * Copyright(c) 2006-2007, Ext JS, LLC.
28539  *
28540  * Originally Released Under LGPL - original licence link has changed is not relivant.
28541  *
28542  * Fork - LGPL
28543  * <script type="text/javascript">
28544  */
28545  
28546
28547 /**
28548  * @class Roo.ColorPalette
28549  * @extends Roo.Component
28550  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28551  * Here's an example of typical usage:
28552  * <pre><code>
28553 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28554 cp.render('my-div');
28555
28556 cp.on('select', function(palette, selColor){
28557     // do something with selColor
28558 });
28559 </code></pre>
28560  * @constructor
28561  * Create a new ColorPalette
28562  * @param {Object} config The config object
28563  */
28564 Roo.ColorPalette = function(config){
28565     Roo.ColorPalette.superclass.constructor.call(this, config);
28566     this.addEvents({
28567         /**
28568              * @event select
28569              * Fires when a color is selected
28570              * @param {ColorPalette} this
28571              * @param {String} color The 6-digit color hex code (without the # symbol)
28572              */
28573         select: true
28574     });
28575
28576     if(this.handler){
28577         this.on("select", this.handler, this.scope, true);
28578     }
28579 };
28580 Roo.extend(Roo.ColorPalette, Roo.Component, {
28581     /**
28582      * @cfg {String} itemCls
28583      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28584      */
28585     itemCls : "x-color-palette",
28586     /**
28587      * @cfg {String} value
28588      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28589      * the hex codes are case-sensitive.
28590      */
28591     value : null,
28592     clickEvent:'click',
28593     // private
28594     ctype: "Roo.ColorPalette",
28595
28596     /**
28597      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28598      */
28599     allowReselect : false,
28600
28601     /**
28602      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28603      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28604      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28605      * of colors with the width setting until the box is symmetrical.</p>
28606      * <p>You can override individual colors if needed:</p>
28607      * <pre><code>
28608 var cp = new Roo.ColorPalette();
28609 cp.colors[0] = "FF0000";  // change the first box to red
28610 </code></pre>
28611
28612 Or you can provide a custom array of your own for complete control:
28613 <pre><code>
28614 var cp = new Roo.ColorPalette();
28615 cp.colors = ["000000", "993300", "333300"];
28616 </code></pre>
28617      * @type Array
28618      */
28619     colors : [
28620         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28621         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28622         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28623         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28624         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28625     ],
28626
28627     // private
28628     onRender : function(container, position){
28629         var t = new Roo.MasterTemplate(
28630             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28631         );
28632         var c = this.colors;
28633         for(var i = 0, len = c.length; i < len; i++){
28634             t.add([c[i]]);
28635         }
28636         var el = document.createElement("div");
28637         el.className = this.itemCls;
28638         t.overwrite(el);
28639         container.dom.insertBefore(el, position);
28640         this.el = Roo.get(el);
28641         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28642         if(this.clickEvent != 'click'){
28643             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28644         }
28645     },
28646
28647     // private
28648     afterRender : function(){
28649         Roo.ColorPalette.superclass.afterRender.call(this);
28650         if(this.value){
28651             var s = this.value;
28652             this.value = null;
28653             this.select(s);
28654         }
28655     },
28656
28657     // private
28658     handleClick : function(e, t){
28659         e.preventDefault();
28660         if(!this.disabled){
28661             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28662             this.select(c.toUpperCase());
28663         }
28664     },
28665
28666     /**
28667      * Selects the specified color in the palette (fires the select event)
28668      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28669      */
28670     select : function(color){
28671         color = color.replace("#", "");
28672         if(color != this.value || this.allowReselect){
28673             var el = this.el;
28674             if(this.value){
28675                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28676             }
28677             el.child("a.color-"+color).addClass("x-color-palette-sel");
28678             this.value = color;
28679             this.fireEvent("select", this, color);
28680         }
28681     }
28682 });/*
28683  * Based on:
28684  * Ext JS Library 1.1.1
28685  * Copyright(c) 2006-2007, Ext JS, LLC.
28686  *
28687  * Originally Released Under LGPL - original licence link has changed is not relivant.
28688  *
28689  * Fork - LGPL
28690  * <script type="text/javascript">
28691  */
28692  
28693 /**
28694  * @class Roo.DatePicker
28695  * @extends Roo.Component
28696  * Simple date picker class.
28697  * @constructor
28698  * Create a new DatePicker
28699  * @param {Object} config The config object
28700  */
28701 Roo.DatePicker = function(config){
28702     Roo.DatePicker.superclass.constructor.call(this, config);
28703
28704     this.value = config && config.value ?
28705                  config.value.clearTime() : new Date().clearTime();
28706
28707     this.addEvents({
28708         /**
28709              * @event select
28710              * Fires when a date is selected
28711              * @param {DatePicker} this
28712              * @param {Date} date The selected date
28713              */
28714         'select': true,
28715         /**
28716              * @event monthchange
28717              * Fires when the displayed month changes 
28718              * @param {DatePicker} this
28719              * @param {Date} date The selected month
28720              */
28721         'monthchange': true
28722     });
28723
28724     if(this.handler){
28725         this.on("select", this.handler,  this.scope || this);
28726     }
28727     // build the disabledDatesRE
28728     if(!this.disabledDatesRE && this.disabledDates){
28729         var dd = this.disabledDates;
28730         var re = "(?:";
28731         for(var i = 0; i < dd.length; i++){
28732             re += dd[i];
28733             if(i != dd.length-1) {
28734                 re += "|";
28735             }
28736         }
28737         this.disabledDatesRE = new RegExp(re + ")");
28738     }
28739 };
28740
28741 Roo.extend(Roo.DatePicker, Roo.Component, {
28742     /**
28743      * @cfg {String} todayText
28744      * The text to display on the button that selects the current date (defaults to "Today")
28745      */
28746     todayText : "Today",
28747     /**
28748      * @cfg {String} okText
28749      * The text to display on the ok button
28750      */
28751     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28752     /**
28753      * @cfg {String} cancelText
28754      * The text to display on the cancel button
28755      */
28756     cancelText : "Cancel",
28757     /**
28758      * @cfg {String} todayTip
28759      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28760      */
28761     todayTip : "{0} (Spacebar)",
28762     /**
28763      * @cfg {Date} minDate
28764      * Minimum allowable date (JavaScript date object, defaults to null)
28765      */
28766     minDate : null,
28767     /**
28768      * @cfg {Date} maxDate
28769      * Maximum allowable date (JavaScript date object, defaults to null)
28770      */
28771     maxDate : null,
28772     /**
28773      * @cfg {String} minText
28774      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28775      */
28776     minText : "This date is before the minimum date",
28777     /**
28778      * @cfg {String} maxText
28779      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28780      */
28781     maxText : "This date is after the maximum date",
28782     /**
28783      * @cfg {String} format
28784      * The default date format string which can be overriden for localization support.  The format must be
28785      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28786      */
28787     format : "m/d/y",
28788     /**
28789      * @cfg {Array} disabledDays
28790      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28791      */
28792     disabledDays : null,
28793     /**
28794      * @cfg {String} disabledDaysText
28795      * The tooltip to display when the date falls on a disabled day (defaults to "")
28796      */
28797     disabledDaysText : "",
28798     /**
28799      * @cfg {RegExp} disabledDatesRE
28800      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28801      */
28802     disabledDatesRE : null,
28803     /**
28804      * @cfg {String} disabledDatesText
28805      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28806      */
28807     disabledDatesText : "",
28808     /**
28809      * @cfg {Boolean} constrainToViewport
28810      * True to constrain the date picker to the viewport (defaults to true)
28811      */
28812     constrainToViewport : true,
28813     /**
28814      * @cfg {Array} monthNames
28815      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28816      */
28817     monthNames : Date.monthNames,
28818     /**
28819      * @cfg {Array} dayNames
28820      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28821      */
28822     dayNames : Date.dayNames,
28823     /**
28824      * @cfg {String} nextText
28825      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28826      */
28827     nextText: 'Next Month (Control+Right)',
28828     /**
28829      * @cfg {String} prevText
28830      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28831      */
28832     prevText: 'Previous Month (Control+Left)',
28833     /**
28834      * @cfg {String} monthYearText
28835      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28836      */
28837     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28838     /**
28839      * @cfg {Number} startDay
28840      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28841      */
28842     startDay : 0,
28843     /**
28844      * @cfg {Bool} showClear
28845      * Show a clear button (usefull for date form elements that can be blank.)
28846      */
28847     
28848     showClear: false,
28849     
28850     /**
28851      * Sets the value of the date field
28852      * @param {Date} value The date to set
28853      */
28854     setValue : function(value){
28855         var old = this.value;
28856         
28857         if (typeof(value) == 'string') {
28858          
28859             value = Date.parseDate(value, this.format);
28860         }
28861         if (!value) {
28862             value = new Date();
28863         }
28864         
28865         this.value = value.clearTime(true);
28866         if(this.el){
28867             this.update(this.value);
28868         }
28869     },
28870
28871     /**
28872      * Gets the current selected value of the date field
28873      * @return {Date} The selected date
28874      */
28875     getValue : function(){
28876         return this.value;
28877     },
28878
28879     // private
28880     focus : function(){
28881         if(this.el){
28882             this.update(this.activeDate);
28883         }
28884     },
28885
28886     // privateval
28887     onRender : function(container, position){
28888         
28889         var m = [
28890              '<table cellspacing="0">',
28891                 '<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>',
28892                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28893         var dn = this.dayNames;
28894         for(var i = 0; i < 7; i++){
28895             var d = this.startDay+i;
28896             if(d > 6){
28897                 d = d-7;
28898             }
28899             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28900         }
28901         m[m.length] = "</tr></thead><tbody><tr>";
28902         for(var i = 0; i < 42; i++) {
28903             if(i % 7 == 0 && i != 0){
28904                 m[m.length] = "</tr><tr>";
28905             }
28906             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28907         }
28908         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28909             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28910
28911         var el = document.createElement("div");
28912         el.className = "x-date-picker";
28913         el.innerHTML = m.join("");
28914
28915         container.dom.insertBefore(el, position);
28916
28917         this.el = Roo.get(el);
28918         this.eventEl = Roo.get(el.firstChild);
28919
28920         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28921             handler: this.showPrevMonth,
28922             scope: this,
28923             preventDefault:true,
28924             stopDefault:true
28925         });
28926
28927         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
28928             handler: this.showNextMonth,
28929             scope: this,
28930             preventDefault:true,
28931             stopDefault:true
28932         });
28933
28934         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
28935
28936         this.monthPicker = this.el.down('div.x-date-mp');
28937         this.monthPicker.enableDisplayMode('block');
28938         
28939         var kn = new Roo.KeyNav(this.eventEl, {
28940             "left" : function(e){
28941                 e.ctrlKey ?
28942                     this.showPrevMonth() :
28943                     this.update(this.activeDate.add("d", -1));
28944             },
28945
28946             "right" : function(e){
28947                 e.ctrlKey ?
28948                     this.showNextMonth() :
28949                     this.update(this.activeDate.add("d", 1));
28950             },
28951
28952             "up" : function(e){
28953                 e.ctrlKey ?
28954                     this.showNextYear() :
28955                     this.update(this.activeDate.add("d", -7));
28956             },
28957
28958             "down" : function(e){
28959                 e.ctrlKey ?
28960                     this.showPrevYear() :
28961                     this.update(this.activeDate.add("d", 7));
28962             },
28963
28964             "pageUp" : function(e){
28965                 this.showNextMonth();
28966             },
28967
28968             "pageDown" : function(e){
28969                 this.showPrevMonth();
28970             },
28971
28972             "enter" : function(e){
28973                 e.stopPropagation();
28974                 return true;
28975             },
28976
28977             scope : this
28978         });
28979
28980         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
28981
28982         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
28983
28984         this.el.unselectable();
28985         
28986         this.cells = this.el.select("table.x-date-inner tbody td");
28987         this.textNodes = this.el.query("table.x-date-inner tbody span");
28988
28989         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
28990             text: "&#160;",
28991             tooltip: this.monthYearText
28992         });
28993
28994         this.mbtn.on('click', this.showMonthPicker, this);
28995         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
28996
28997
28998         var today = (new Date()).dateFormat(this.format);
28999         
29000         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29001         if (this.showClear) {
29002             baseTb.add( new Roo.Toolbar.Fill());
29003         }
29004         baseTb.add({
29005             text: String.format(this.todayText, today),
29006             tooltip: String.format(this.todayTip, today),
29007             handler: this.selectToday,
29008             scope: this
29009         });
29010         
29011         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29012             
29013         //});
29014         if (this.showClear) {
29015             
29016             baseTb.add( new Roo.Toolbar.Fill());
29017             baseTb.add({
29018                 text: '&#160;',
29019                 cls: 'x-btn-icon x-btn-clear',
29020                 handler: function() {
29021                     //this.value = '';
29022                     this.fireEvent("select", this, '');
29023                 },
29024                 scope: this
29025             });
29026         }
29027         
29028         
29029         if(Roo.isIE){
29030             this.el.repaint();
29031         }
29032         this.update(this.value);
29033     },
29034
29035     createMonthPicker : function(){
29036         if(!this.monthPicker.dom.firstChild){
29037             var buf = ['<table border="0" cellspacing="0">'];
29038             for(var i = 0; i < 6; i++){
29039                 buf.push(
29040                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29041                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29042                     i == 0 ?
29043                     '<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>' :
29044                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29045                 );
29046             }
29047             buf.push(
29048                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29049                     this.okText,
29050                     '</button><button type="button" class="x-date-mp-cancel">',
29051                     this.cancelText,
29052                     '</button></td></tr>',
29053                 '</table>'
29054             );
29055             this.monthPicker.update(buf.join(''));
29056             this.monthPicker.on('click', this.onMonthClick, this);
29057             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29058
29059             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29060             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29061
29062             this.mpMonths.each(function(m, a, i){
29063                 i += 1;
29064                 if((i%2) == 0){
29065                     m.dom.xmonth = 5 + Math.round(i * .5);
29066                 }else{
29067                     m.dom.xmonth = Math.round((i-1) * .5);
29068                 }
29069             });
29070         }
29071     },
29072
29073     showMonthPicker : function(){
29074         this.createMonthPicker();
29075         var size = this.el.getSize();
29076         this.monthPicker.setSize(size);
29077         this.monthPicker.child('table').setSize(size);
29078
29079         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29080         this.updateMPMonth(this.mpSelMonth);
29081         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29082         this.updateMPYear(this.mpSelYear);
29083
29084         this.monthPicker.slideIn('t', {duration:.2});
29085     },
29086
29087     updateMPYear : function(y){
29088         this.mpyear = y;
29089         var ys = this.mpYears.elements;
29090         for(var i = 1; i <= 10; i++){
29091             var td = ys[i-1], y2;
29092             if((i%2) == 0){
29093                 y2 = y + Math.round(i * .5);
29094                 td.firstChild.innerHTML = y2;
29095                 td.xyear = y2;
29096             }else{
29097                 y2 = y - (5-Math.round(i * .5));
29098                 td.firstChild.innerHTML = y2;
29099                 td.xyear = y2;
29100             }
29101             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29102         }
29103     },
29104
29105     updateMPMonth : function(sm){
29106         this.mpMonths.each(function(m, a, i){
29107             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29108         });
29109     },
29110
29111     selectMPMonth: function(m){
29112         
29113     },
29114
29115     onMonthClick : function(e, t){
29116         e.stopEvent();
29117         var el = new Roo.Element(t), pn;
29118         if(el.is('button.x-date-mp-cancel')){
29119             this.hideMonthPicker();
29120         }
29121         else if(el.is('button.x-date-mp-ok')){
29122             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29123             this.hideMonthPicker();
29124         }
29125         else if(pn = el.up('td.x-date-mp-month', 2)){
29126             this.mpMonths.removeClass('x-date-mp-sel');
29127             pn.addClass('x-date-mp-sel');
29128             this.mpSelMonth = pn.dom.xmonth;
29129         }
29130         else if(pn = el.up('td.x-date-mp-year', 2)){
29131             this.mpYears.removeClass('x-date-mp-sel');
29132             pn.addClass('x-date-mp-sel');
29133             this.mpSelYear = pn.dom.xyear;
29134         }
29135         else if(el.is('a.x-date-mp-prev')){
29136             this.updateMPYear(this.mpyear-10);
29137         }
29138         else if(el.is('a.x-date-mp-next')){
29139             this.updateMPYear(this.mpyear+10);
29140         }
29141     },
29142
29143     onMonthDblClick : function(e, t){
29144         e.stopEvent();
29145         var el = new Roo.Element(t), pn;
29146         if(pn = el.up('td.x-date-mp-month', 2)){
29147             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29148             this.hideMonthPicker();
29149         }
29150         else if(pn = el.up('td.x-date-mp-year', 2)){
29151             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29152             this.hideMonthPicker();
29153         }
29154     },
29155
29156     hideMonthPicker : function(disableAnim){
29157         if(this.monthPicker){
29158             if(disableAnim === true){
29159                 this.monthPicker.hide();
29160             }else{
29161                 this.monthPicker.slideOut('t', {duration:.2});
29162             }
29163         }
29164     },
29165
29166     // private
29167     showPrevMonth : function(e){
29168         this.update(this.activeDate.add("mo", -1));
29169     },
29170
29171     // private
29172     showNextMonth : function(e){
29173         this.update(this.activeDate.add("mo", 1));
29174     },
29175
29176     // private
29177     showPrevYear : function(){
29178         this.update(this.activeDate.add("y", -1));
29179     },
29180
29181     // private
29182     showNextYear : function(){
29183         this.update(this.activeDate.add("y", 1));
29184     },
29185
29186     // private
29187     handleMouseWheel : function(e){
29188         var delta = e.getWheelDelta();
29189         if(delta > 0){
29190             this.showPrevMonth();
29191             e.stopEvent();
29192         } else if(delta < 0){
29193             this.showNextMonth();
29194             e.stopEvent();
29195         }
29196     },
29197
29198     // private
29199     handleDateClick : function(e, t){
29200         e.stopEvent();
29201         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29202             this.setValue(new Date(t.dateValue));
29203             this.fireEvent("select", this, this.value);
29204         }
29205     },
29206
29207     // private
29208     selectToday : function(){
29209         this.setValue(new Date().clearTime());
29210         this.fireEvent("select", this, this.value);
29211     },
29212
29213     // private
29214     update : function(date)
29215     {
29216         var vd = this.activeDate;
29217         this.activeDate = date;
29218         if(vd && this.el){
29219             var t = date.getTime();
29220             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29221                 this.cells.removeClass("x-date-selected");
29222                 this.cells.each(function(c){
29223                    if(c.dom.firstChild.dateValue == t){
29224                        c.addClass("x-date-selected");
29225                        setTimeout(function(){
29226                             try{c.dom.firstChild.focus();}catch(e){}
29227                        }, 50);
29228                        return false;
29229                    }
29230                 });
29231                 return;
29232             }
29233         }
29234         
29235         var days = date.getDaysInMonth();
29236         var firstOfMonth = date.getFirstDateOfMonth();
29237         var startingPos = firstOfMonth.getDay()-this.startDay;
29238
29239         if(startingPos <= this.startDay){
29240             startingPos += 7;
29241         }
29242
29243         var pm = date.add("mo", -1);
29244         var prevStart = pm.getDaysInMonth()-startingPos;
29245
29246         var cells = this.cells.elements;
29247         var textEls = this.textNodes;
29248         days += startingPos;
29249
29250         // convert everything to numbers so it's fast
29251         var day = 86400000;
29252         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29253         var today = new Date().clearTime().getTime();
29254         var sel = date.clearTime().getTime();
29255         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29256         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29257         var ddMatch = this.disabledDatesRE;
29258         var ddText = this.disabledDatesText;
29259         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29260         var ddaysText = this.disabledDaysText;
29261         var format = this.format;
29262
29263         var setCellClass = function(cal, cell){
29264             cell.title = "";
29265             var t = d.getTime();
29266             cell.firstChild.dateValue = t;
29267             if(t == today){
29268                 cell.className += " x-date-today";
29269                 cell.title = cal.todayText;
29270             }
29271             if(t == sel){
29272                 cell.className += " x-date-selected";
29273                 setTimeout(function(){
29274                     try{cell.firstChild.focus();}catch(e){}
29275                 }, 50);
29276             }
29277             // disabling
29278             if(t < min) {
29279                 cell.className = " x-date-disabled";
29280                 cell.title = cal.minText;
29281                 return;
29282             }
29283             if(t > max) {
29284                 cell.className = " x-date-disabled";
29285                 cell.title = cal.maxText;
29286                 return;
29287             }
29288             if(ddays){
29289                 if(ddays.indexOf(d.getDay()) != -1){
29290                     cell.title = ddaysText;
29291                     cell.className = " x-date-disabled";
29292                 }
29293             }
29294             if(ddMatch && format){
29295                 var fvalue = d.dateFormat(format);
29296                 if(ddMatch.test(fvalue)){
29297                     cell.title = ddText.replace("%0", fvalue);
29298                     cell.className = " x-date-disabled";
29299                 }
29300             }
29301         };
29302
29303         var i = 0;
29304         for(; i < startingPos; i++) {
29305             textEls[i].innerHTML = (++prevStart);
29306             d.setDate(d.getDate()+1);
29307             cells[i].className = "x-date-prevday";
29308             setCellClass(this, cells[i]);
29309         }
29310         for(; i < days; i++){
29311             intDay = i - startingPos + 1;
29312             textEls[i].innerHTML = (intDay);
29313             d.setDate(d.getDate()+1);
29314             cells[i].className = "x-date-active";
29315             setCellClass(this, cells[i]);
29316         }
29317         var extraDays = 0;
29318         for(; i < 42; i++) {
29319              textEls[i].innerHTML = (++extraDays);
29320              d.setDate(d.getDate()+1);
29321              cells[i].className = "x-date-nextday";
29322              setCellClass(this, cells[i]);
29323         }
29324
29325         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29326         this.fireEvent('monthchange', this, date);
29327         
29328         if(!this.internalRender){
29329             var main = this.el.dom.firstChild;
29330             var w = main.offsetWidth;
29331             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29332             Roo.fly(main).setWidth(w);
29333             this.internalRender = true;
29334             // opera does not respect the auto grow header center column
29335             // then, after it gets a width opera refuses to recalculate
29336             // without a second pass
29337             if(Roo.isOpera && !this.secondPass){
29338                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29339                 this.secondPass = true;
29340                 this.update.defer(10, this, [date]);
29341             }
29342         }
29343         
29344         
29345     }
29346 });        /*
29347  * Based on:
29348  * Ext JS Library 1.1.1
29349  * Copyright(c) 2006-2007, Ext JS, LLC.
29350  *
29351  * Originally Released Under LGPL - original licence link has changed is not relivant.
29352  *
29353  * Fork - LGPL
29354  * <script type="text/javascript">
29355  */
29356 /**
29357  * @class Roo.TabPanel
29358  * @extends Roo.util.Observable
29359  * A lightweight tab container.
29360  * <br><br>
29361  * Usage:
29362  * <pre><code>
29363 // basic tabs 1, built from existing content
29364 var tabs = new Roo.TabPanel("tabs1");
29365 tabs.addTab("script", "View Script");
29366 tabs.addTab("markup", "View Markup");
29367 tabs.activate("script");
29368
29369 // more advanced tabs, built from javascript
29370 var jtabs = new Roo.TabPanel("jtabs");
29371 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29372
29373 // set up the UpdateManager
29374 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29375 var updater = tab2.getUpdateManager();
29376 updater.setDefaultUrl("ajax1.htm");
29377 tab2.on('activate', updater.refresh, updater, true);
29378
29379 // Use setUrl for Ajax loading
29380 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29381 tab3.setUrl("ajax2.htm", null, true);
29382
29383 // Disabled tab
29384 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29385 tab4.disable();
29386
29387 jtabs.activate("jtabs-1");
29388  * </code></pre>
29389  * @constructor
29390  * Create a new TabPanel.
29391  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29392  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29393  */
29394 Roo.TabPanel = function(container, config){
29395     /**
29396     * The container element for this TabPanel.
29397     * @type Roo.Element
29398     */
29399     this.el = Roo.get(container, true);
29400     if(config){
29401         if(typeof config == "boolean"){
29402             this.tabPosition = config ? "bottom" : "top";
29403         }else{
29404             Roo.apply(this, config);
29405         }
29406     }
29407     if(this.tabPosition == "bottom"){
29408         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29409         this.el.addClass("x-tabs-bottom");
29410     }
29411     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29412     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29413     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29414     if(Roo.isIE){
29415         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29416     }
29417     if(this.tabPosition != "bottom"){
29418         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29419          * @type Roo.Element
29420          */
29421         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29422         this.el.addClass("x-tabs-top");
29423     }
29424     this.items = [];
29425
29426     this.bodyEl.setStyle("position", "relative");
29427
29428     this.active = null;
29429     this.activateDelegate = this.activate.createDelegate(this);
29430
29431     this.addEvents({
29432         /**
29433          * @event tabchange
29434          * Fires when the active tab changes
29435          * @param {Roo.TabPanel} this
29436          * @param {Roo.TabPanelItem} activePanel The new active tab
29437          */
29438         "tabchange": true,
29439         /**
29440          * @event beforetabchange
29441          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29442          * @param {Roo.TabPanel} this
29443          * @param {Object} e Set cancel to true on this object to cancel the tab change
29444          * @param {Roo.TabPanelItem} tab The tab being changed to
29445          */
29446         "beforetabchange" : true
29447     });
29448
29449     Roo.EventManager.onWindowResize(this.onResize, this);
29450     this.cpad = this.el.getPadding("lr");
29451     this.hiddenCount = 0;
29452
29453
29454     // toolbar on the tabbar support...
29455     if (this.toolbar) {
29456         var tcfg = this.toolbar;
29457         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29458         this.toolbar = new Roo.Toolbar(tcfg);
29459         if (Roo.isSafari) {
29460             var tbl = tcfg.container.child('table', true);
29461             tbl.setAttribute('width', '100%');
29462         }
29463         
29464     }
29465    
29466
29467
29468     Roo.TabPanel.superclass.constructor.call(this);
29469 };
29470
29471 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29472     /*
29473      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29474      */
29475     tabPosition : "top",
29476     /*
29477      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29478      */
29479     currentTabWidth : 0,
29480     /*
29481      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29482      */
29483     minTabWidth : 40,
29484     /*
29485      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29486      */
29487     maxTabWidth : 250,
29488     /*
29489      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29490      */
29491     preferredTabWidth : 175,
29492     /*
29493      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29494      */
29495     resizeTabs : false,
29496     /*
29497      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29498      */
29499     monitorResize : true,
29500     /*
29501      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29502      */
29503     toolbar : false,
29504
29505     /**
29506      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29507      * @param {String} id The id of the div to use <b>or create</b>
29508      * @param {String} text The text for the tab
29509      * @param {String} content (optional) Content to put in the TabPanelItem body
29510      * @param {Boolean} closable (optional) True to create a close icon on the tab
29511      * @return {Roo.TabPanelItem} The created TabPanelItem
29512      */
29513     addTab : function(id, text, content, closable){
29514         var item = new Roo.TabPanelItem(this, id, text, closable);
29515         this.addTabItem(item);
29516         if(content){
29517             item.setContent(content);
29518         }
29519         return item;
29520     },
29521
29522     /**
29523      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29524      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29525      * @return {Roo.TabPanelItem}
29526      */
29527     getTab : function(id){
29528         return this.items[id];
29529     },
29530
29531     /**
29532      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29533      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29534      */
29535     hideTab : function(id){
29536         var t = this.items[id];
29537         if(!t.isHidden()){
29538            t.setHidden(true);
29539            this.hiddenCount++;
29540            this.autoSizeTabs();
29541         }
29542     },
29543
29544     /**
29545      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29546      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29547      */
29548     unhideTab : function(id){
29549         var t = this.items[id];
29550         if(t.isHidden()){
29551            t.setHidden(false);
29552            this.hiddenCount--;
29553            this.autoSizeTabs();
29554         }
29555     },
29556
29557     /**
29558      * Adds an existing {@link Roo.TabPanelItem}.
29559      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29560      */
29561     addTabItem : function(item){
29562         this.items[item.id] = item;
29563         this.items.push(item);
29564         if(this.resizeTabs){
29565            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29566            this.autoSizeTabs();
29567         }else{
29568             item.autoSize();
29569         }
29570     },
29571
29572     /**
29573      * Removes a {@link Roo.TabPanelItem}.
29574      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29575      */
29576     removeTab : function(id){
29577         var items = this.items;
29578         var tab = items[id];
29579         if(!tab) { return; }
29580         var index = items.indexOf(tab);
29581         if(this.active == tab && items.length > 1){
29582             var newTab = this.getNextAvailable(index);
29583             if(newTab) {
29584                 newTab.activate();
29585             }
29586         }
29587         this.stripEl.dom.removeChild(tab.pnode.dom);
29588         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29589             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29590         }
29591         items.splice(index, 1);
29592         delete this.items[tab.id];
29593         tab.fireEvent("close", tab);
29594         tab.purgeListeners();
29595         this.autoSizeTabs();
29596     },
29597
29598     getNextAvailable : function(start){
29599         var items = this.items;
29600         var index = start;
29601         // look for a next tab that will slide over to
29602         // replace the one being removed
29603         while(index < items.length){
29604             var item = items[++index];
29605             if(item && !item.isHidden()){
29606                 return item;
29607             }
29608         }
29609         // if one isn't found select the previous tab (on the left)
29610         index = start;
29611         while(index >= 0){
29612             var item = items[--index];
29613             if(item && !item.isHidden()){
29614                 return item;
29615             }
29616         }
29617         return null;
29618     },
29619
29620     /**
29621      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29622      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29623      */
29624     disableTab : function(id){
29625         var tab = this.items[id];
29626         if(tab && this.active != tab){
29627             tab.disable();
29628         }
29629     },
29630
29631     /**
29632      * Enables a {@link Roo.TabPanelItem} that is disabled.
29633      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29634      */
29635     enableTab : function(id){
29636         var tab = this.items[id];
29637         tab.enable();
29638     },
29639
29640     /**
29641      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29642      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29643      * @return {Roo.TabPanelItem} The TabPanelItem.
29644      */
29645     activate : function(id){
29646         var tab = this.items[id];
29647         if(!tab){
29648             return null;
29649         }
29650         if(tab == this.active || tab.disabled){
29651             return tab;
29652         }
29653         var e = {};
29654         this.fireEvent("beforetabchange", this, e, tab);
29655         if(e.cancel !== true && !tab.disabled){
29656             if(this.active){
29657                 this.active.hide();
29658             }
29659             this.active = this.items[id];
29660             this.active.show();
29661             this.fireEvent("tabchange", this, this.active);
29662         }
29663         return tab;
29664     },
29665
29666     /**
29667      * Gets the active {@link Roo.TabPanelItem}.
29668      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29669      */
29670     getActiveTab : function(){
29671         return this.active;
29672     },
29673
29674     /**
29675      * Updates the tab body element to fit the height of the container element
29676      * for overflow scrolling
29677      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29678      */
29679     syncHeight : function(targetHeight){
29680         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29681         var bm = this.bodyEl.getMargins();
29682         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29683         this.bodyEl.setHeight(newHeight);
29684         return newHeight;
29685     },
29686
29687     onResize : function(){
29688         if(this.monitorResize){
29689             this.autoSizeTabs();
29690         }
29691     },
29692
29693     /**
29694      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29695      */
29696     beginUpdate : function(){
29697         this.updating = true;
29698     },
29699
29700     /**
29701      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29702      */
29703     endUpdate : function(){
29704         this.updating = false;
29705         this.autoSizeTabs();
29706     },
29707
29708     /**
29709      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29710      */
29711     autoSizeTabs : function(){
29712         var count = this.items.length;
29713         var vcount = count - this.hiddenCount;
29714         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29715             return;
29716         }
29717         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29718         var availWidth = Math.floor(w / vcount);
29719         var b = this.stripBody;
29720         if(b.getWidth() > w){
29721             var tabs = this.items;
29722             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29723             if(availWidth < this.minTabWidth){
29724                 /*if(!this.sleft){    // incomplete scrolling code
29725                     this.createScrollButtons();
29726                 }
29727                 this.showScroll();
29728                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29729             }
29730         }else{
29731             if(this.currentTabWidth < this.preferredTabWidth){
29732                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29733             }
29734         }
29735     },
29736
29737     /**
29738      * Returns the number of tabs in this TabPanel.
29739      * @return {Number}
29740      */
29741      getCount : function(){
29742          return this.items.length;
29743      },
29744
29745     /**
29746      * Resizes all the tabs to the passed width
29747      * @param {Number} The new width
29748      */
29749     setTabWidth : function(width){
29750         this.currentTabWidth = width;
29751         for(var i = 0, len = this.items.length; i < len; i++) {
29752                 if(!this.items[i].isHidden()) {
29753                 this.items[i].setWidth(width);
29754             }
29755         }
29756     },
29757
29758     /**
29759      * Destroys this TabPanel
29760      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29761      */
29762     destroy : function(removeEl){
29763         Roo.EventManager.removeResizeListener(this.onResize, this);
29764         for(var i = 0, len = this.items.length; i < len; i++){
29765             this.items[i].purgeListeners();
29766         }
29767         if(removeEl === true){
29768             this.el.update("");
29769             this.el.remove();
29770         }
29771     }
29772 });
29773
29774 /**
29775  * @class Roo.TabPanelItem
29776  * @extends Roo.util.Observable
29777  * Represents an individual item (tab plus body) in a TabPanel.
29778  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29779  * @param {String} id The id of this TabPanelItem
29780  * @param {String} text The text for the tab of this TabPanelItem
29781  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29782  */
29783 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29784     /**
29785      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29786      * @type Roo.TabPanel
29787      */
29788     this.tabPanel = tabPanel;
29789     /**
29790      * The id for this TabPanelItem
29791      * @type String
29792      */
29793     this.id = id;
29794     /** @private */
29795     this.disabled = false;
29796     /** @private */
29797     this.text = text;
29798     /** @private */
29799     this.loaded = false;
29800     this.closable = closable;
29801
29802     /**
29803      * The body element for this TabPanelItem.
29804      * @type Roo.Element
29805      */
29806     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29807     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29808     this.bodyEl.setStyle("display", "block");
29809     this.bodyEl.setStyle("zoom", "1");
29810     this.hideAction();
29811
29812     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29813     /** @private */
29814     this.el = Roo.get(els.el, true);
29815     this.inner = Roo.get(els.inner, true);
29816     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29817     this.pnode = Roo.get(els.el.parentNode, true);
29818     this.el.on("mousedown", this.onTabMouseDown, this);
29819     this.el.on("click", this.onTabClick, this);
29820     /** @private */
29821     if(closable){
29822         var c = Roo.get(els.close, true);
29823         c.dom.title = this.closeText;
29824         c.addClassOnOver("close-over");
29825         c.on("click", this.closeClick, this);
29826      }
29827
29828     this.addEvents({
29829          /**
29830          * @event activate
29831          * Fires when this tab becomes the active tab.
29832          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29833          * @param {Roo.TabPanelItem} this
29834          */
29835         "activate": true,
29836         /**
29837          * @event beforeclose
29838          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29839          * @param {Roo.TabPanelItem} this
29840          * @param {Object} e Set cancel to true on this object to cancel the close.
29841          */
29842         "beforeclose": true,
29843         /**
29844          * @event close
29845          * Fires when this tab is closed.
29846          * @param {Roo.TabPanelItem} this
29847          */
29848          "close": true,
29849         /**
29850          * @event deactivate
29851          * Fires when this tab is no longer the active tab.
29852          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29853          * @param {Roo.TabPanelItem} this
29854          */
29855          "deactivate" : true
29856     });
29857     this.hidden = false;
29858
29859     Roo.TabPanelItem.superclass.constructor.call(this);
29860 };
29861
29862 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29863     purgeListeners : function(){
29864        Roo.util.Observable.prototype.purgeListeners.call(this);
29865        this.el.removeAllListeners();
29866     },
29867     /**
29868      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29869      */
29870     show : function(){
29871         this.pnode.addClass("on");
29872         this.showAction();
29873         if(Roo.isOpera){
29874             this.tabPanel.stripWrap.repaint();
29875         }
29876         this.fireEvent("activate", this.tabPanel, this);
29877     },
29878
29879     /**
29880      * Returns true if this tab is the active tab.
29881      * @return {Boolean}
29882      */
29883     isActive : function(){
29884         return this.tabPanel.getActiveTab() == this;
29885     },
29886
29887     /**
29888      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29889      */
29890     hide : function(){
29891         this.pnode.removeClass("on");
29892         this.hideAction();
29893         this.fireEvent("deactivate", this.tabPanel, this);
29894     },
29895
29896     hideAction : function(){
29897         this.bodyEl.hide();
29898         this.bodyEl.setStyle("position", "absolute");
29899         this.bodyEl.setLeft("-20000px");
29900         this.bodyEl.setTop("-20000px");
29901     },
29902
29903     showAction : function(){
29904         this.bodyEl.setStyle("position", "relative");
29905         this.bodyEl.setTop("");
29906         this.bodyEl.setLeft("");
29907         this.bodyEl.show();
29908     },
29909
29910     /**
29911      * Set the tooltip for the tab.
29912      * @param {String} tooltip The tab's tooltip
29913      */
29914     setTooltip : function(text){
29915         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29916             this.textEl.dom.qtip = text;
29917             this.textEl.dom.removeAttribute('title');
29918         }else{
29919             this.textEl.dom.title = text;
29920         }
29921     },
29922
29923     onTabClick : function(e){
29924         e.preventDefault();
29925         this.tabPanel.activate(this.id);
29926     },
29927
29928     onTabMouseDown : function(e){
29929         e.preventDefault();
29930         this.tabPanel.activate(this.id);
29931     },
29932
29933     getWidth : function(){
29934         return this.inner.getWidth();
29935     },
29936
29937     setWidth : function(width){
29938         var iwidth = width - this.pnode.getPadding("lr");
29939         this.inner.setWidth(iwidth);
29940         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
29941         this.pnode.setWidth(width);
29942     },
29943
29944     /**
29945      * Show or hide the tab
29946      * @param {Boolean} hidden True to hide or false to show.
29947      */
29948     setHidden : function(hidden){
29949         this.hidden = hidden;
29950         this.pnode.setStyle("display", hidden ? "none" : "");
29951     },
29952
29953     /**
29954      * Returns true if this tab is "hidden"
29955      * @return {Boolean}
29956      */
29957     isHidden : function(){
29958         return this.hidden;
29959     },
29960
29961     /**
29962      * Returns the text for this tab
29963      * @return {String}
29964      */
29965     getText : function(){
29966         return this.text;
29967     },
29968
29969     autoSize : function(){
29970         //this.el.beginMeasure();
29971         this.textEl.setWidth(1);
29972         /*
29973          *  #2804 [new] Tabs in Roojs
29974          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
29975          */
29976         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
29977         //this.el.endMeasure();
29978     },
29979
29980     /**
29981      * Sets the text for the tab (Note: this also sets the tooltip text)
29982      * @param {String} text The tab's text and tooltip
29983      */
29984     setText : function(text){
29985         this.text = text;
29986         this.textEl.update(text);
29987         this.setTooltip(text);
29988         if(!this.tabPanel.resizeTabs){
29989             this.autoSize();
29990         }
29991     },
29992     /**
29993      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
29994      */
29995     activate : function(){
29996         this.tabPanel.activate(this.id);
29997     },
29998
29999     /**
30000      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30001      */
30002     disable : function(){
30003         if(this.tabPanel.active != this){
30004             this.disabled = true;
30005             this.pnode.addClass("disabled");
30006         }
30007     },
30008
30009     /**
30010      * Enables this TabPanelItem if it was previously disabled.
30011      */
30012     enable : function(){
30013         this.disabled = false;
30014         this.pnode.removeClass("disabled");
30015     },
30016
30017     /**
30018      * Sets the content for this TabPanelItem.
30019      * @param {String} content The content
30020      * @param {Boolean} loadScripts true to look for and load scripts
30021      */
30022     setContent : function(content, loadScripts){
30023         this.bodyEl.update(content, loadScripts);
30024     },
30025
30026     /**
30027      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30028      * @return {Roo.UpdateManager} The UpdateManager
30029      */
30030     getUpdateManager : function(){
30031         return this.bodyEl.getUpdateManager();
30032     },
30033
30034     /**
30035      * Set a URL to be used to load the content for this TabPanelItem.
30036      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30037      * @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)
30038      * @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)
30039      * @return {Roo.UpdateManager} The UpdateManager
30040      */
30041     setUrl : function(url, params, loadOnce){
30042         if(this.refreshDelegate){
30043             this.un('activate', this.refreshDelegate);
30044         }
30045         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30046         this.on("activate", this.refreshDelegate);
30047         return this.bodyEl.getUpdateManager();
30048     },
30049
30050     /** @private */
30051     _handleRefresh : function(url, params, loadOnce){
30052         if(!loadOnce || !this.loaded){
30053             var updater = this.bodyEl.getUpdateManager();
30054             updater.update(url, params, this._setLoaded.createDelegate(this));
30055         }
30056     },
30057
30058     /**
30059      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30060      *   Will fail silently if the setUrl method has not been called.
30061      *   This does not activate the panel, just updates its content.
30062      */
30063     refresh : function(){
30064         if(this.refreshDelegate){
30065            this.loaded = false;
30066            this.refreshDelegate();
30067         }
30068     },
30069
30070     /** @private */
30071     _setLoaded : function(){
30072         this.loaded = true;
30073     },
30074
30075     /** @private */
30076     closeClick : function(e){
30077         var o = {};
30078         e.stopEvent();
30079         this.fireEvent("beforeclose", this, o);
30080         if(o.cancel !== true){
30081             this.tabPanel.removeTab(this.id);
30082         }
30083     },
30084     /**
30085      * The text displayed in the tooltip for the close icon.
30086      * @type String
30087      */
30088     closeText : "Close this tab"
30089 });
30090
30091 /** @private */
30092 Roo.TabPanel.prototype.createStrip = function(container){
30093     var strip = document.createElement("div");
30094     strip.className = "x-tabs-wrap";
30095     container.appendChild(strip);
30096     return strip;
30097 };
30098 /** @private */
30099 Roo.TabPanel.prototype.createStripList = function(strip){
30100     // div wrapper for retard IE
30101     // returns the "tr" element.
30102     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30103         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30104         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30105     return strip.firstChild.firstChild.firstChild.firstChild;
30106 };
30107 /** @private */
30108 Roo.TabPanel.prototype.createBody = function(container){
30109     var body = document.createElement("div");
30110     Roo.id(body, "tab-body");
30111     Roo.fly(body).addClass("x-tabs-body");
30112     container.appendChild(body);
30113     return body;
30114 };
30115 /** @private */
30116 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30117     var body = Roo.getDom(id);
30118     if(!body){
30119         body = document.createElement("div");
30120         body.id = id;
30121     }
30122     Roo.fly(body).addClass("x-tabs-item-body");
30123     bodyEl.insertBefore(body, bodyEl.firstChild);
30124     return body;
30125 };
30126 /** @private */
30127 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30128     var td = document.createElement("td");
30129     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30130     //stripEl.appendChild(td);
30131     if(closable){
30132         td.className = "x-tabs-closable";
30133         if(!this.closeTpl){
30134             this.closeTpl = new Roo.Template(
30135                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30136                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30137                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30138             );
30139         }
30140         var el = this.closeTpl.overwrite(td, {"text": text});
30141         var close = el.getElementsByTagName("div")[0];
30142         var inner = el.getElementsByTagName("em")[0];
30143         return {"el": el, "close": close, "inner": inner};
30144     } else {
30145         if(!this.tabTpl){
30146             this.tabTpl = new Roo.Template(
30147                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30148                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30149             );
30150         }
30151         var el = this.tabTpl.overwrite(td, {"text": text});
30152         var inner = el.getElementsByTagName("em")[0];
30153         return {"el": el, "inner": inner};
30154     }
30155 };/*
30156  * Based on:
30157  * Ext JS Library 1.1.1
30158  * Copyright(c) 2006-2007, Ext JS, LLC.
30159  *
30160  * Originally Released Under LGPL - original licence link has changed is not relivant.
30161  *
30162  * Fork - LGPL
30163  * <script type="text/javascript">
30164  */
30165
30166 /**
30167  * @class Roo.Button
30168  * @extends Roo.util.Observable
30169  * Simple Button class
30170  * @cfg {String} text The button text
30171  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30172  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30173  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30174  * @cfg {Object} scope The scope of the handler
30175  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30176  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30177  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30178  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30179  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30180  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30181    applies if enableToggle = true)
30182  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30183  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30184   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30185  * @constructor
30186  * Create a new button
30187  * @param {Object} config The config object
30188  */
30189 Roo.Button = function(renderTo, config)
30190 {
30191     if (!config) {
30192         config = renderTo;
30193         renderTo = config.renderTo || false;
30194     }
30195     
30196     Roo.apply(this, config);
30197     this.addEvents({
30198         /**
30199              * @event click
30200              * Fires when this button is clicked
30201              * @param {Button} this
30202              * @param {EventObject} e The click event
30203              */
30204             "click" : true,
30205         /**
30206              * @event toggle
30207              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30208              * @param {Button} this
30209              * @param {Boolean} pressed
30210              */
30211             "toggle" : true,
30212         /**
30213              * @event mouseover
30214              * Fires when the mouse hovers over the button
30215              * @param {Button} this
30216              * @param {Event} e The event object
30217              */
30218         'mouseover' : true,
30219         /**
30220              * @event mouseout
30221              * Fires when the mouse exits the button
30222              * @param {Button} this
30223              * @param {Event} e The event object
30224              */
30225         'mouseout': true,
30226          /**
30227              * @event render
30228              * Fires when the button is rendered
30229              * @param {Button} this
30230              */
30231         'render': true
30232     });
30233     if(this.menu){
30234         this.menu = Roo.menu.MenuMgr.get(this.menu);
30235     }
30236     // register listeners first!!  - so render can be captured..
30237     Roo.util.Observable.call(this);
30238     if(renderTo){
30239         this.render(renderTo);
30240     }
30241     
30242   
30243 };
30244
30245 Roo.extend(Roo.Button, Roo.util.Observable, {
30246     /**
30247      * 
30248      */
30249     
30250     /**
30251      * Read-only. True if this button is hidden
30252      * @type Boolean
30253      */
30254     hidden : false,
30255     /**
30256      * Read-only. True if this button is disabled
30257      * @type Boolean
30258      */
30259     disabled : false,
30260     /**
30261      * Read-only. True if this button is pressed (only if enableToggle = true)
30262      * @type Boolean
30263      */
30264     pressed : false,
30265
30266     /**
30267      * @cfg {Number} tabIndex 
30268      * The DOM tabIndex for this button (defaults to undefined)
30269      */
30270     tabIndex : undefined,
30271
30272     /**
30273      * @cfg {Boolean} enableToggle
30274      * True to enable pressed/not pressed toggling (defaults to false)
30275      */
30276     enableToggle: false,
30277     /**
30278      * @cfg {Roo.menu.Menu} menu
30279      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30280      */
30281     menu : undefined,
30282     /**
30283      * @cfg {String} menuAlign
30284      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30285      */
30286     menuAlign : "tl-bl?",
30287
30288     /**
30289      * @cfg {String} iconCls
30290      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30291      */
30292     iconCls : undefined,
30293     /**
30294      * @cfg {String} type
30295      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30296      */
30297     type : 'button',
30298
30299     // private
30300     menuClassTarget: 'tr',
30301
30302     /**
30303      * @cfg {String} clickEvent
30304      * The type of event to map to the button's event handler (defaults to 'click')
30305      */
30306     clickEvent : 'click',
30307
30308     /**
30309      * @cfg {Boolean} handleMouseEvents
30310      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30311      */
30312     handleMouseEvents : true,
30313
30314     /**
30315      * @cfg {String} tooltipType
30316      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30317      */
30318     tooltipType : 'qtip',
30319
30320     /**
30321      * @cfg {String} cls
30322      * A CSS class to apply to the button's main element.
30323      */
30324     
30325     /**
30326      * @cfg {Roo.Template} template (Optional)
30327      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30328      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30329      * require code modifications if required elements (e.g. a button) aren't present.
30330      */
30331
30332     // private
30333     render : function(renderTo){
30334         var btn;
30335         if(this.hideParent){
30336             this.parentEl = Roo.get(renderTo);
30337         }
30338         if(!this.dhconfig){
30339             if(!this.template){
30340                 if(!Roo.Button.buttonTemplate){
30341                     // hideous table template
30342                     Roo.Button.buttonTemplate = new Roo.Template(
30343                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30344                         '<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>',
30345                         "</tr></tbody></table>");
30346                 }
30347                 this.template = Roo.Button.buttonTemplate;
30348             }
30349             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30350             var btnEl = btn.child("button:first");
30351             btnEl.on('focus', this.onFocus, this);
30352             btnEl.on('blur', this.onBlur, this);
30353             if(this.cls){
30354                 btn.addClass(this.cls);
30355             }
30356             if(this.icon){
30357                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30358             }
30359             if(this.iconCls){
30360                 btnEl.addClass(this.iconCls);
30361                 if(!this.cls){
30362                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30363                 }
30364             }
30365             if(this.tabIndex !== undefined){
30366                 btnEl.dom.tabIndex = this.tabIndex;
30367             }
30368             if(this.tooltip){
30369                 if(typeof this.tooltip == 'object'){
30370                     Roo.QuickTips.tips(Roo.apply({
30371                           target: btnEl.id
30372                     }, this.tooltip));
30373                 } else {
30374                     btnEl.dom[this.tooltipType] = this.tooltip;
30375                 }
30376             }
30377         }else{
30378             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30379         }
30380         this.el = btn;
30381         if(this.id){
30382             this.el.dom.id = this.el.id = this.id;
30383         }
30384         if(this.menu){
30385             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30386             this.menu.on("show", this.onMenuShow, this);
30387             this.menu.on("hide", this.onMenuHide, this);
30388         }
30389         btn.addClass("x-btn");
30390         if(Roo.isIE && !Roo.isIE7){
30391             this.autoWidth.defer(1, this);
30392         }else{
30393             this.autoWidth();
30394         }
30395         if(this.handleMouseEvents){
30396             btn.on("mouseover", this.onMouseOver, this);
30397             btn.on("mouseout", this.onMouseOut, this);
30398             btn.on("mousedown", this.onMouseDown, this);
30399         }
30400         btn.on(this.clickEvent, this.onClick, this);
30401         //btn.on("mouseup", this.onMouseUp, this);
30402         if(this.hidden){
30403             this.hide();
30404         }
30405         if(this.disabled){
30406             this.disable();
30407         }
30408         Roo.ButtonToggleMgr.register(this);
30409         if(this.pressed){
30410             this.el.addClass("x-btn-pressed");
30411         }
30412         if(this.repeat){
30413             var repeater = new Roo.util.ClickRepeater(btn,
30414                 typeof this.repeat == "object" ? this.repeat : {}
30415             );
30416             repeater.on("click", this.onClick,  this);
30417         }
30418         
30419         this.fireEvent('render', this);
30420         
30421     },
30422     /**
30423      * Returns the button's underlying element
30424      * @return {Roo.Element} The element
30425      */
30426     getEl : function(){
30427         return this.el;  
30428     },
30429     
30430     /**
30431      * Destroys this Button and removes any listeners.
30432      */
30433     destroy : function(){
30434         Roo.ButtonToggleMgr.unregister(this);
30435         this.el.removeAllListeners();
30436         this.purgeListeners();
30437         this.el.remove();
30438     },
30439
30440     // private
30441     autoWidth : function(){
30442         if(this.el){
30443             this.el.setWidth("auto");
30444             if(Roo.isIE7 && Roo.isStrict){
30445                 var ib = this.el.child('button');
30446                 if(ib && ib.getWidth() > 20){
30447                     ib.clip();
30448                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30449                 }
30450             }
30451             if(this.minWidth){
30452                 if(this.hidden){
30453                     this.el.beginMeasure();
30454                 }
30455                 if(this.el.getWidth() < this.minWidth){
30456                     this.el.setWidth(this.minWidth);
30457                 }
30458                 if(this.hidden){
30459                     this.el.endMeasure();
30460                 }
30461             }
30462         }
30463     },
30464
30465     /**
30466      * Assigns this button's click handler
30467      * @param {Function} handler The function to call when the button is clicked
30468      * @param {Object} scope (optional) Scope for the function passed in
30469      */
30470     setHandler : function(handler, scope){
30471         this.handler = handler;
30472         this.scope = scope;  
30473     },
30474     
30475     /**
30476      * Sets this button's text
30477      * @param {String} text The button text
30478      */
30479     setText : function(text){
30480         this.text = text;
30481         if(this.el){
30482             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30483         }
30484         this.autoWidth();
30485     },
30486     
30487     /**
30488      * Gets the text for this button
30489      * @return {String} The button text
30490      */
30491     getText : function(){
30492         return this.text;  
30493     },
30494     
30495     /**
30496      * Show this button
30497      */
30498     show: function(){
30499         this.hidden = false;
30500         if(this.el){
30501             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30502         }
30503     },
30504     
30505     /**
30506      * Hide this button
30507      */
30508     hide: function(){
30509         this.hidden = true;
30510         if(this.el){
30511             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30512         }
30513     },
30514     
30515     /**
30516      * Convenience function for boolean show/hide
30517      * @param {Boolean} visible True to show, false to hide
30518      */
30519     setVisible: function(visible){
30520         if(visible) {
30521             this.show();
30522         }else{
30523             this.hide();
30524         }
30525     },
30526     
30527     /**
30528      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30529      * @param {Boolean} state (optional) Force a particular state
30530      */
30531     toggle : function(state){
30532         state = state === undefined ? !this.pressed : state;
30533         if(state != this.pressed){
30534             if(state){
30535                 this.el.addClass("x-btn-pressed");
30536                 this.pressed = true;
30537                 this.fireEvent("toggle", this, true);
30538             }else{
30539                 this.el.removeClass("x-btn-pressed");
30540                 this.pressed = false;
30541                 this.fireEvent("toggle", this, false);
30542             }
30543             if(this.toggleHandler){
30544                 this.toggleHandler.call(this.scope || this, this, state);
30545             }
30546         }
30547     },
30548     
30549     /**
30550      * Focus the button
30551      */
30552     focus : function(){
30553         this.el.child('button:first').focus();
30554     },
30555     
30556     /**
30557      * Disable this button
30558      */
30559     disable : function(){
30560         if(this.el){
30561             this.el.addClass("x-btn-disabled");
30562         }
30563         this.disabled = true;
30564     },
30565     
30566     /**
30567      * Enable this button
30568      */
30569     enable : function(){
30570         if(this.el){
30571             this.el.removeClass("x-btn-disabled");
30572         }
30573         this.disabled = false;
30574     },
30575
30576     /**
30577      * Convenience function for boolean enable/disable
30578      * @param {Boolean} enabled True to enable, false to disable
30579      */
30580     setDisabled : function(v){
30581         this[v !== true ? "enable" : "disable"]();
30582     },
30583
30584     // private
30585     onClick : function(e)
30586     {
30587         if(e){
30588             e.preventDefault();
30589         }
30590         if(e.button != 0){
30591             return;
30592         }
30593         if(!this.disabled){
30594             if(this.enableToggle){
30595                 this.toggle();
30596             }
30597             if(this.menu && !this.menu.isVisible()){
30598                 this.menu.show(this.el, this.menuAlign);
30599             }
30600             this.fireEvent("click", this, e);
30601             if(this.handler){
30602                 this.el.removeClass("x-btn-over");
30603                 this.handler.call(this.scope || this, this, e);
30604             }
30605         }
30606     },
30607     // private
30608     onMouseOver : function(e){
30609         if(!this.disabled){
30610             this.el.addClass("x-btn-over");
30611             this.fireEvent('mouseover', this, e);
30612         }
30613     },
30614     // private
30615     onMouseOut : function(e){
30616         if(!e.within(this.el,  true)){
30617             this.el.removeClass("x-btn-over");
30618             this.fireEvent('mouseout', this, e);
30619         }
30620     },
30621     // private
30622     onFocus : function(e){
30623         if(!this.disabled){
30624             this.el.addClass("x-btn-focus");
30625         }
30626     },
30627     // private
30628     onBlur : function(e){
30629         this.el.removeClass("x-btn-focus");
30630     },
30631     // private
30632     onMouseDown : function(e){
30633         if(!this.disabled && e.button == 0){
30634             this.el.addClass("x-btn-click");
30635             Roo.get(document).on('mouseup', this.onMouseUp, this);
30636         }
30637     },
30638     // private
30639     onMouseUp : function(e){
30640         if(e.button == 0){
30641             this.el.removeClass("x-btn-click");
30642             Roo.get(document).un('mouseup', this.onMouseUp, this);
30643         }
30644     },
30645     // private
30646     onMenuShow : function(e){
30647         this.el.addClass("x-btn-menu-active");
30648     },
30649     // private
30650     onMenuHide : function(e){
30651         this.el.removeClass("x-btn-menu-active");
30652     }   
30653 });
30654
30655 // Private utility class used by Button
30656 Roo.ButtonToggleMgr = function(){
30657    var groups = {};
30658    
30659    function toggleGroup(btn, state){
30660        if(state){
30661            var g = groups[btn.toggleGroup];
30662            for(var i = 0, l = g.length; i < l; i++){
30663                if(g[i] != btn){
30664                    g[i].toggle(false);
30665                }
30666            }
30667        }
30668    }
30669    
30670    return {
30671        register : function(btn){
30672            if(!btn.toggleGroup){
30673                return;
30674            }
30675            var g = groups[btn.toggleGroup];
30676            if(!g){
30677                g = groups[btn.toggleGroup] = [];
30678            }
30679            g.push(btn);
30680            btn.on("toggle", toggleGroup);
30681        },
30682        
30683        unregister : function(btn){
30684            if(!btn.toggleGroup){
30685                return;
30686            }
30687            var g = groups[btn.toggleGroup];
30688            if(g){
30689                g.remove(btn);
30690                btn.un("toggle", toggleGroup);
30691            }
30692        }
30693    };
30694 }();/*
30695  * Based on:
30696  * Ext JS Library 1.1.1
30697  * Copyright(c) 2006-2007, Ext JS, LLC.
30698  *
30699  * Originally Released Under LGPL - original licence link has changed is not relivant.
30700  *
30701  * Fork - LGPL
30702  * <script type="text/javascript">
30703  */
30704  
30705 /**
30706  * @class Roo.SplitButton
30707  * @extends Roo.Button
30708  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30709  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30710  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30711  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30712  * @cfg {String} arrowTooltip The title attribute of the arrow
30713  * @constructor
30714  * Create a new menu button
30715  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30716  * @param {Object} config The config object
30717  */
30718 Roo.SplitButton = function(renderTo, config){
30719     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30720     /**
30721      * @event arrowclick
30722      * Fires when this button's arrow is clicked
30723      * @param {SplitButton} this
30724      * @param {EventObject} e The click event
30725      */
30726     this.addEvents({"arrowclick":true});
30727 };
30728
30729 Roo.extend(Roo.SplitButton, Roo.Button, {
30730     render : function(renderTo){
30731         // this is one sweet looking template!
30732         var tpl = new Roo.Template(
30733             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30734             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30735             '<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>',
30736             "</tbody></table></td><td>",
30737             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30738             '<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>',
30739             "</tbody></table></td></tr></table>"
30740         );
30741         var btn = tpl.append(renderTo, [this.text, this.type], true);
30742         var btnEl = btn.child("button");
30743         if(this.cls){
30744             btn.addClass(this.cls);
30745         }
30746         if(this.icon){
30747             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30748         }
30749         if(this.iconCls){
30750             btnEl.addClass(this.iconCls);
30751             if(!this.cls){
30752                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30753             }
30754         }
30755         this.el = btn;
30756         if(this.handleMouseEvents){
30757             btn.on("mouseover", this.onMouseOver, this);
30758             btn.on("mouseout", this.onMouseOut, this);
30759             btn.on("mousedown", this.onMouseDown, this);
30760             btn.on("mouseup", this.onMouseUp, this);
30761         }
30762         btn.on(this.clickEvent, this.onClick, this);
30763         if(this.tooltip){
30764             if(typeof this.tooltip == 'object'){
30765                 Roo.QuickTips.tips(Roo.apply({
30766                       target: btnEl.id
30767                 }, this.tooltip));
30768             } else {
30769                 btnEl.dom[this.tooltipType] = this.tooltip;
30770             }
30771         }
30772         if(this.arrowTooltip){
30773             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30774         }
30775         if(this.hidden){
30776             this.hide();
30777         }
30778         if(this.disabled){
30779             this.disable();
30780         }
30781         if(this.pressed){
30782             this.el.addClass("x-btn-pressed");
30783         }
30784         if(Roo.isIE && !Roo.isIE7){
30785             this.autoWidth.defer(1, this);
30786         }else{
30787             this.autoWidth();
30788         }
30789         if(this.menu){
30790             this.menu.on("show", this.onMenuShow, this);
30791             this.menu.on("hide", this.onMenuHide, this);
30792         }
30793         this.fireEvent('render', this);
30794     },
30795
30796     // private
30797     autoWidth : function(){
30798         if(this.el){
30799             var tbl = this.el.child("table:first");
30800             var tbl2 = this.el.child("table:last");
30801             this.el.setWidth("auto");
30802             tbl.setWidth("auto");
30803             if(Roo.isIE7 && Roo.isStrict){
30804                 var ib = this.el.child('button:first');
30805                 if(ib && ib.getWidth() > 20){
30806                     ib.clip();
30807                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30808                 }
30809             }
30810             if(this.minWidth){
30811                 if(this.hidden){
30812                     this.el.beginMeasure();
30813                 }
30814                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30815                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30816                 }
30817                 if(this.hidden){
30818                     this.el.endMeasure();
30819                 }
30820             }
30821             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30822         } 
30823     },
30824     /**
30825      * Sets this button's click handler
30826      * @param {Function} handler The function to call when the button is clicked
30827      * @param {Object} scope (optional) Scope for the function passed above
30828      */
30829     setHandler : function(handler, scope){
30830         this.handler = handler;
30831         this.scope = scope;  
30832     },
30833     
30834     /**
30835      * Sets this button's arrow click handler
30836      * @param {Function} handler The function to call when the arrow is clicked
30837      * @param {Object} scope (optional) Scope for the function passed above
30838      */
30839     setArrowHandler : function(handler, scope){
30840         this.arrowHandler = handler;
30841         this.scope = scope;  
30842     },
30843     
30844     /**
30845      * Focus the button
30846      */
30847     focus : function(){
30848         if(this.el){
30849             this.el.child("button:first").focus();
30850         }
30851     },
30852
30853     // private
30854     onClick : function(e){
30855         e.preventDefault();
30856         if(!this.disabled){
30857             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30858                 if(this.menu && !this.menu.isVisible()){
30859                     this.menu.show(this.el, this.menuAlign);
30860                 }
30861                 this.fireEvent("arrowclick", this, e);
30862                 if(this.arrowHandler){
30863                     this.arrowHandler.call(this.scope || this, this, e);
30864                 }
30865             }else{
30866                 this.fireEvent("click", this, e);
30867                 if(this.handler){
30868                     this.handler.call(this.scope || this, this, e);
30869                 }
30870             }
30871         }
30872     },
30873     // private
30874     onMouseDown : function(e){
30875         if(!this.disabled){
30876             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30877         }
30878     },
30879     // private
30880     onMouseUp : function(e){
30881         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30882     }   
30883 });
30884
30885
30886 // backwards compat
30887 Roo.MenuButton = Roo.SplitButton;/*
30888  * Based on:
30889  * Ext JS Library 1.1.1
30890  * Copyright(c) 2006-2007, Ext JS, LLC.
30891  *
30892  * Originally Released Under LGPL - original licence link has changed is not relivant.
30893  *
30894  * Fork - LGPL
30895  * <script type="text/javascript">
30896  */
30897
30898 /**
30899  * @class Roo.Toolbar
30900  * @children   Roo.Toolbar.Item Roo.form.Field
30901  * Basic Toolbar class.
30902  * @constructor
30903  * Creates a new Toolbar
30904  * @param {Object} container The config object
30905  */ 
30906 Roo.Toolbar = function(container, buttons, config)
30907 {
30908     /// old consturctor format still supported..
30909     if(container instanceof Array){ // omit the container for later rendering
30910         buttons = container;
30911         config = buttons;
30912         container = null;
30913     }
30914     if (typeof(container) == 'object' && container.xtype) {
30915         config = container;
30916         container = config.container;
30917         buttons = config.buttons || []; // not really - use items!!
30918     }
30919     var xitems = [];
30920     if (config && config.items) {
30921         xitems = config.items;
30922         delete config.items;
30923     }
30924     Roo.apply(this, config);
30925     this.buttons = buttons;
30926     
30927     if(container){
30928         this.render(container);
30929     }
30930     this.xitems = xitems;
30931     Roo.each(xitems, function(b) {
30932         this.add(b);
30933     }, this);
30934     
30935 };
30936
30937 Roo.Toolbar.prototype = {
30938     /**
30939      * @cfg {Array} items
30940      * array of button configs or elements to add (will be converted to a MixedCollection)
30941      */
30942     items: false,
30943     /**
30944      * @cfg {String/HTMLElement/Element} container
30945      * The id or element that will contain the toolbar
30946      */
30947     // private
30948     render : function(ct){
30949         this.el = Roo.get(ct);
30950         if(this.cls){
30951             this.el.addClass(this.cls);
30952         }
30953         // using a table allows for vertical alignment
30954         // 100% width is needed by Safari...
30955         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
30956         this.tr = this.el.child("tr", true);
30957         var autoId = 0;
30958         this.items = new Roo.util.MixedCollection(false, function(o){
30959             return o.id || ("item" + (++autoId));
30960         });
30961         if(this.buttons){
30962             this.add.apply(this, this.buttons);
30963             delete this.buttons;
30964         }
30965     },
30966
30967     /**
30968      * Adds element(s) to the toolbar -- this function takes a variable number of 
30969      * arguments of mixed type and adds them to the toolbar.
30970      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
30971      * <ul>
30972      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
30973      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
30974      * <li>Field: Any form field (equivalent to {@link #addField})</li>
30975      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
30976      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
30977      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
30978      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
30979      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
30980      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
30981      * </ul>
30982      * @param {Mixed} arg2
30983      * @param {Mixed} etc.
30984      */
30985     add : function(){
30986         var a = arguments, l = a.length;
30987         for(var i = 0; i < l; i++){
30988             this._add(a[i]);
30989         }
30990     },
30991     // private..
30992     _add : function(el) {
30993         
30994         if (el.xtype) {
30995             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
30996         }
30997         
30998         if (el.applyTo){ // some kind of form field
30999             return this.addField(el);
31000         } 
31001         if (el.render){ // some kind of Toolbar.Item
31002             return this.addItem(el);
31003         }
31004         if (typeof el == "string"){ // string
31005             if(el == "separator" || el == "-"){
31006                 return this.addSeparator();
31007             }
31008             if (el == " "){
31009                 return this.addSpacer();
31010             }
31011             if(el == "->"){
31012                 return this.addFill();
31013             }
31014             return this.addText(el);
31015             
31016         }
31017         if(el.tagName){ // element
31018             return this.addElement(el);
31019         }
31020         if(typeof el == "object"){ // must be button config?
31021             return this.addButton(el);
31022         }
31023         // and now what?!?!
31024         return false;
31025         
31026     },
31027     
31028     /**
31029      * Add an Xtype element
31030      * @param {Object} xtype Xtype Object
31031      * @return {Object} created Object
31032      */
31033     addxtype : function(e){
31034         return this.add(e);  
31035     },
31036     
31037     /**
31038      * Returns the Element for this toolbar.
31039      * @return {Roo.Element}
31040      */
31041     getEl : function(){
31042         return this.el;  
31043     },
31044     
31045     /**
31046      * Adds a separator
31047      * @return {Roo.Toolbar.Item} The separator item
31048      */
31049     addSeparator : function(){
31050         return this.addItem(new Roo.Toolbar.Separator());
31051     },
31052
31053     /**
31054      * Adds a spacer element
31055      * @return {Roo.Toolbar.Spacer} The spacer item
31056      */
31057     addSpacer : function(){
31058         return this.addItem(new Roo.Toolbar.Spacer());
31059     },
31060
31061     /**
31062      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31063      * @return {Roo.Toolbar.Fill} The fill item
31064      */
31065     addFill : function(){
31066         return this.addItem(new Roo.Toolbar.Fill());
31067     },
31068
31069     /**
31070      * Adds any standard HTML element to the toolbar
31071      * @param {String/HTMLElement/Element} el The element or id of the element to add
31072      * @return {Roo.Toolbar.Item} The element's item
31073      */
31074     addElement : function(el){
31075         return this.addItem(new Roo.Toolbar.Item(el));
31076     },
31077     /**
31078      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31079      * @type Roo.util.MixedCollection  
31080      */
31081     items : false,
31082      
31083     /**
31084      * Adds any Toolbar.Item or subclass
31085      * @param {Roo.Toolbar.Item} item
31086      * @return {Roo.Toolbar.Item} The item
31087      */
31088     addItem : function(item){
31089         var td = this.nextBlock();
31090         item.render(td);
31091         this.items.add(item);
31092         return item;
31093     },
31094     
31095     /**
31096      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31097      * @param {Object/Array} config A button config or array of configs
31098      * @return {Roo.Toolbar.Button/Array}
31099      */
31100     addButton : function(config){
31101         if(config instanceof Array){
31102             var buttons = [];
31103             for(var i = 0, len = config.length; i < len; i++) {
31104                 buttons.push(this.addButton(config[i]));
31105             }
31106             return buttons;
31107         }
31108         var b = config;
31109         if(!(config instanceof Roo.Toolbar.Button)){
31110             b = config.split ?
31111                 new Roo.Toolbar.SplitButton(config) :
31112                 new Roo.Toolbar.Button(config);
31113         }
31114         var td = this.nextBlock();
31115         b.render(td);
31116         this.items.add(b);
31117         return b;
31118     },
31119     
31120     /**
31121      * Adds text to the toolbar
31122      * @param {String} text The text to add
31123      * @return {Roo.Toolbar.Item} The element's item
31124      */
31125     addText : function(text){
31126         return this.addItem(new Roo.Toolbar.TextItem(text));
31127     },
31128     
31129     /**
31130      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31131      * @param {Number} index The index where the item is to be inserted
31132      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31133      * @return {Roo.Toolbar.Button/Item}
31134      */
31135     insertButton : function(index, item){
31136         if(item instanceof Array){
31137             var buttons = [];
31138             for(var i = 0, len = item.length; i < len; i++) {
31139                buttons.push(this.insertButton(index + i, item[i]));
31140             }
31141             return buttons;
31142         }
31143         if (!(item instanceof Roo.Toolbar.Button)){
31144            item = new Roo.Toolbar.Button(item);
31145         }
31146         var td = document.createElement("td");
31147         this.tr.insertBefore(td, this.tr.childNodes[index]);
31148         item.render(td);
31149         this.items.insert(index, item);
31150         return item;
31151     },
31152     
31153     /**
31154      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31155      * @param {Object} config
31156      * @return {Roo.Toolbar.Item} The element's item
31157      */
31158     addDom : function(config, returnEl){
31159         var td = this.nextBlock();
31160         Roo.DomHelper.overwrite(td, config);
31161         var ti = new Roo.Toolbar.Item(td.firstChild);
31162         ti.render(td);
31163         this.items.add(ti);
31164         return ti;
31165     },
31166
31167     /**
31168      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31169      * @type Roo.util.MixedCollection  
31170      */
31171     fields : false,
31172     
31173     /**
31174      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31175      * Note: the field should not have been rendered yet. For a field that has already been
31176      * rendered, use {@link #addElement}.
31177      * @param {Roo.form.Field} field
31178      * @return {Roo.ToolbarItem}
31179      */
31180      
31181       
31182     addField : function(field) {
31183         if (!this.fields) {
31184             var autoId = 0;
31185             this.fields = new Roo.util.MixedCollection(false, function(o){
31186                 return o.id || ("item" + (++autoId));
31187             });
31188
31189         }
31190         
31191         var td = this.nextBlock();
31192         field.render(td);
31193         var ti = new Roo.Toolbar.Item(td.firstChild);
31194         ti.render(td);
31195         this.items.add(ti);
31196         this.fields.add(field);
31197         return ti;
31198     },
31199     /**
31200      * Hide the toolbar
31201      * @method hide
31202      */
31203      
31204       
31205     hide : function()
31206     {
31207         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31208         this.el.child('div').hide();
31209     },
31210     /**
31211      * Show the toolbar
31212      * @method show
31213      */
31214     show : function()
31215     {
31216         this.el.child('div').show();
31217     },
31218       
31219     // private
31220     nextBlock : function(){
31221         var td = document.createElement("td");
31222         this.tr.appendChild(td);
31223         return td;
31224     },
31225
31226     // private
31227     destroy : function(){
31228         if(this.items){ // rendered?
31229             Roo.destroy.apply(Roo, this.items.items);
31230         }
31231         if(this.fields){ // rendered?
31232             Roo.destroy.apply(Roo, this.fields.items);
31233         }
31234         Roo.Element.uncache(this.el, this.tr);
31235     }
31236 };
31237
31238 /**
31239  * @class Roo.Toolbar.Item
31240  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31241  * @constructor
31242  * Creates a new Item
31243  * @param {HTMLElement} el 
31244  */
31245 Roo.Toolbar.Item = function(el){
31246     var cfg = {};
31247     if (typeof (el.xtype) != 'undefined') {
31248         cfg = el;
31249         el = cfg.el;
31250     }
31251     
31252     this.el = Roo.getDom(el);
31253     this.id = Roo.id(this.el);
31254     this.hidden = false;
31255     
31256     this.addEvents({
31257          /**
31258              * @event render
31259              * Fires when the button is rendered
31260              * @param {Button} this
31261              */
31262         'render': true
31263     });
31264     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31265 };
31266 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31267 //Roo.Toolbar.Item.prototype = {
31268     
31269     /**
31270      * Get this item's HTML Element
31271      * @return {HTMLElement}
31272      */
31273     getEl : function(){
31274        return this.el;  
31275     },
31276
31277     // private
31278     render : function(td){
31279         
31280          this.td = td;
31281         td.appendChild(this.el);
31282         
31283         this.fireEvent('render', this);
31284     },
31285     
31286     /**
31287      * Removes and destroys this item.
31288      */
31289     destroy : function(){
31290         this.td.parentNode.removeChild(this.td);
31291     },
31292     
31293     /**
31294      * Shows this item.
31295      */
31296     show: function(){
31297         this.hidden = false;
31298         this.td.style.display = "";
31299     },
31300     
31301     /**
31302      * Hides this item.
31303      */
31304     hide: function(){
31305         this.hidden = true;
31306         this.td.style.display = "none";
31307     },
31308     
31309     /**
31310      * Convenience function for boolean show/hide.
31311      * @param {Boolean} visible true to show/false to hide
31312      */
31313     setVisible: function(visible){
31314         if(visible) {
31315             this.show();
31316         }else{
31317             this.hide();
31318         }
31319     },
31320     
31321     /**
31322      * Try to focus this item.
31323      */
31324     focus : function(){
31325         Roo.fly(this.el).focus();
31326     },
31327     
31328     /**
31329      * Disables this item.
31330      */
31331     disable : function(){
31332         Roo.fly(this.td).addClass("x-item-disabled");
31333         this.disabled = true;
31334         this.el.disabled = true;
31335     },
31336     
31337     /**
31338      * Enables this item.
31339      */
31340     enable : function(){
31341         Roo.fly(this.td).removeClass("x-item-disabled");
31342         this.disabled = false;
31343         this.el.disabled = false;
31344     }
31345 });
31346
31347
31348 /**
31349  * @class Roo.Toolbar.Separator
31350  * @extends Roo.Toolbar.Item
31351  * A simple toolbar separator class
31352  * @constructor
31353  * Creates a new Separator
31354  */
31355 Roo.Toolbar.Separator = function(cfg){
31356     
31357     var s = document.createElement("span");
31358     s.className = "ytb-sep";
31359     if (cfg) {
31360         cfg.el = s;
31361     }
31362     
31363     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31364 };
31365 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31366     enable:Roo.emptyFn,
31367     disable:Roo.emptyFn,
31368     focus:Roo.emptyFn
31369 });
31370
31371 /**
31372  * @class Roo.Toolbar.Spacer
31373  * @extends Roo.Toolbar.Item
31374  * A simple element that adds extra horizontal space to a toolbar.
31375  * @constructor
31376  * Creates a new Spacer
31377  */
31378 Roo.Toolbar.Spacer = function(cfg){
31379     var s = document.createElement("div");
31380     s.className = "ytb-spacer";
31381     if (cfg) {
31382         cfg.el = s;
31383     }
31384     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31385 };
31386 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31387     enable:Roo.emptyFn,
31388     disable:Roo.emptyFn,
31389     focus:Roo.emptyFn
31390 });
31391
31392 /**
31393  * @class Roo.Toolbar.Fill
31394  * @extends Roo.Toolbar.Spacer
31395  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31396  * @constructor
31397  * Creates a new Spacer
31398  */
31399 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31400     // private
31401     render : function(td){
31402         td.style.width = '100%';
31403         Roo.Toolbar.Fill.superclass.render.call(this, td);
31404     }
31405 });
31406
31407 /**
31408  * @class Roo.Toolbar.TextItem
31409  * @extends Roo.Toolbar.Item
31410  * A simple class that renders text directly into a toolbar.
31411  * @constructor
31412  * Creates a new TextItem
31413  * @cfg {string} text 
31414  */
31415 Roo.Toolbar.TextItem = function(cfg){
31416     var  text = cfg || "";
31417     if (typeof(cfg) == 'object') {
31418         text = cfg.text || "";
31419     }  else {
31420         cfg = null;
31421     }
31422     var s = document.createElement("span");
31423     s.className = "ytb-text";
31424     s.innerHTML = text;
31425     if (cfg) {
31426         cfg.el  = s;
31427     }
31428     
31429     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31430 };
31431 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31432     
31433      
31434     enable:Roo.emptyFn,
31435     disable:Roo.emptyFn,
31436     focus:Roo.emptyFn
31437 });
31438
31439 /**
31440  * @class Roo.Toolbar.Button
31441  * @extends Roo.Button
31442  * A button that renders into a toolbar.
31443  * @constructor
31444  * Creates a new Button
31445  * @param {Object} config A standard {@link Roo.Button} config object
31446  */
31447 Roo.Toolbar.Button = function(config){
31448     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31449 };
31450 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31451 {
31452     
31453     
31454     render : function(td){
31455         this.td = td;
31456         Roo.Toolbar.Button.superclass.render.call(this, td);
31457     },
31458     
31459     /**
31460      * Removes and destroys this button
31461      */
31462     destroy : function(){
31463         Roo.Toolbar.Button.superclass.destroy.call(this);
31464         this.td.parentNode.removeChild(this.td);
31465     },
31466     
31467     /**
31468      * Shows this button
31469      */
31470     show: function(){
31471         this.hidden = false;
31472         this.td.style.display = "";
31473     },
31474     
31475     /**
31476      * Hides this button
31477      */
31478     hide: function(){
31479         this.hidden = true;
31480         this.td.style.display = "none";
31481     },
31482
31483     /**
31484      * Disables this item
31485      */
31486     disable : function(){
31487         Roo.fly(this.td).addClass("x-item-disabled");
31488         this.disabled = true;
31489     },
31490
31491     /**
31492      * Enables this item
31493      */
31494     enable : function(){
31495         Roo.fly(this.td).removeClass("x-item-disabled");
31496         this.disabled = false;
31497     }
31498 });
31499 // backwards compat
31500 Roo.ToolbarButton = Roo.Toolbar.Button;
31501
31502 /**
31503  * @class Roo.Toolbar.SplitButton
31504  * @extends Roo.SplitButton
31505  * A menu button that renders into a toolbar.
31506  * @constructor
31507  * Creates a new SplitButton
31508  * @param {Object} config A standard {@link Roo.SplitButton} config object
31509  */
31510 Roo.Toolbar.SplitButton = function(config){
31511     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31512 };
31513 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31514     render : function(td){
31515         this.td = td;
31516         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31517     },
31518     
31519     /**
31520      * Removes and destroys this button
31521      */
31522     destroy : function(){
31523         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31524         this.td.parentNode.removeChild(this.td);
31525     },
31526     
31527     /**
31528      * Shows this button
31529      */
31530     show: function(){
31531         this.hidden = false;
31532         this.td.style.display = "";
31533     },
31534     
31535     /**
31536      * Hides this button
31537      */
31538     hide: function(){
31539         this.hidden = true;
31540         this.td.style.display = "none";
31541     }
31542 });
31543
31544 // backwards compat
31545 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31546  * Based on:
31547  * Ext JS Library 1.1.1
31548  * Copyright(c) 2006-2007, Ext JS, LLC.
31549  *
31550  * Originally Released Under LGPL - original licence link has changed is not relivant.
31551  *
31552  * Fork - LGPL
31553  * <script type="text/javascript">
31554  */
31555  
31556 /**
31557  * @class Roo.PagingToolbar
31558  * @extends Roo.Toolbar
31559  * @children   Roo.Toolbar.Item Roo.form.Field
31560  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31561  * @constructor
31562  * Create a new PagingToolbar
31563  * @param {Object} config The config object
31564  */
31565 Roo.PagingToolbar = function(el, ds, config)
31566 {
31567     // old args format still supported... - xtype is prefered..
31568     if (typeof(el) == 'object' && el.xtype) {
31569         // created from xtype...
31570         config = el;
31571         ds = el.dataSource;
31572         el = config.container;
31573     }
31574     var items = [];
31575     if (config.items) {
31576         items = config.items;
31577         config.items = [];
31578     }
31579     
31580     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31581     this.ds = ds;
31582     this.cursor = 0;
31583     this.renderButtons(this.el);
31584     this.bind(ds);
31585     
31586     // supprot items array.
31587    
31588     Roo.each(items, function(e) {
31589         this.add(Roo.factory(e));
31590     },this);
31591     
31592 };
31593
31594 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31595    
31596     /**
31597      * @cfg {String/HTMLElement/Element} container
31598      * container The id or element that will contain the toolbar
31599      */
31600     /**
31601      * @cfg {Boolean} displayInfo
31602      * True to display the displayMsg (defaults to false)
31603      */
31604     
31605     
31606     /**
31607      * @cfg {Number} pageSize
31608      * The number of records to display per page (defaults to 20)
31609      */
31610     pageSize: 20,
31611     /**
31612      * @cfg {String} displayMsg
31613      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31614      */
31615     displayMsg : 'Displaying {0} - {1} of {2}',
31616     /**
31617      * @cfg {String} emptyMsg
31618      * The message to display when no records are found (defaults to "No data to display")
31619      */
31620     emptyMsg : 'No data to display',
31621     /**
31622      * Customizable piece of the default paging text (defaults to "Page")
31623      * @type String
31624      */
31625     beforePageText : "Page",
31626     /**
31627      * Customizable piece of the default paging text (defaults to "of %0")
31628      * @type String
31629      */
31630     afterPageText : "of {0}",
31631     /**
31632      * Customizable piece of the default paging text (defaults to "First Page")
31633      * @type String
31634      */
31635     firstText : "First Page",
31636     /**
31637      * Customizable piece of the default paging text (defaults to "Previous Page")
31638      * @type String
31639      */
31640     prevText : "Previous Page",
31641     /**
31642      * Customizable piece of the default paging text (defaults to "Next Page")
31643      * @type String
31644      */
31645     nextText : "Next Page",
31646     /**
31647      * Customizable piece of the default paging text (defaults to "Last Page")
31648      * @type String
31649      */
31650     lastText : "Last Page",
31651     /**
31652      * Customizable piece of the default paging text (defaults to "Refresh")
31653      * @type String
31654      */
31655     refreshText : "Refresh",
31656
31657     // private
31658     renderButtons : function(el){
31659         Roo.PagingToolbar.superclass.render.call(this, el);
31660         this.first = this.addButton({
31661             tooltip: this.firstText,
31662             cls: "x-btn-icon x-grid-page-first",
31663             disabled: true,
31664             handler: this.onClick.createDelegate(this, ["first"])
31665         });
31666         this.prev = this.addButton({
31667             tooltip: this.prevText,
31668             cls: "x-btn-icon x-grid-page-prev",
31669             disabled: true,
31670             handler: this.onClick.createDelegate(this, ["prev"])
31671         });
31672         //this.addSeparator();
31673         this.add(this.beforePageText);
31674         this.field = Roo.get(this.addDom({
31675            tag: "input",
31676            type: "text",
31677            size: "3",
31678            value: "1",
31679            cls: "x-grid-page-number"
31680         }).el);
31681         this.field.on("keydown", this.onPagingKeydown, this);
31682         this.field.on("focus", function(){this.dom.select();});
31683         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31684         this.field.setHeight(18);
31685         //this.addSeparator();
31686         this.next = this.addButton({
31687             tooltip: this.nextText,
31688             cls: "x-btn-icon x-grid-page-next",
31689             disabled: true,
31690             handler: this.onClick.createDelegate(this, ["next"])
31691         });
31692         this.last = this.addButton({
31693             tooltip: this.lastText,
31694             cls: "x-btn-icon x-grid-page-last",
31695             disabled: true,
31696             handler: this.onClick.createDelegate(this, ["last"])
31697         });
31698         //this.addSeparator();
31699         this.loading = this.addButton({
31700             tooltip: this.refreshText,
31701             cls: "x-btn-icon x-grid-loading",
31702             handler: this.onClick.createDelegate(this, ["refresh"])
31703         });
31704
31705         if(this.displayInfo){
31706             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31707         }
31708     },
31709
31710     // private
31711     updateInfo : function(){
31712         if(this.displayEl){
31713             var count = this.ds.getCount();
31714             var msg = count == 0 ?
31715                 this.emptyMsg :
31716                 String.format(
31717                     this.displayMsg,
31718                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31719                 );
31720             this.displayEl.update(msg);
31721         }
31722     },
31723
31724     // private
31725     onLoad : function(ds, r, o){
31726        this.cursor = o.params ? o.params.start : 0;
31727        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31728
31729        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31730        this.field.dom.value = ap;
31731        this.first.setDisabled(ap == 1);
31732        this.prev.setDisabled(ap == 1);
31733        this.next.setDisabled(ap == ps);
31734        this.last.setDisabled(ap == ps);
31735        this.loading.enable();
31736        this.updateInfo();
31737     },
31738
31739     // private
31740     getPageData : function(){
31741         var total = this.ds.getTotalCount();
31742         return {
31743             total : total,
31744             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31745             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31746         };
31747     },
31748
31749     // private
31750     onLoadError : function(){
31751         this.loading.enable();
31752     },
31753
31754     // private
31755     onPagingKeydown : function(e){
31756         var k = e.getKey();
31757         var d = this.getPageData();
31758         if(k == e.RETURN){
31759             var v = this.field.dom.value, pageNum;
31760             if(!v || isNaN(pageNum = parseInt(v, 10))){
31761                 this.field.dom.value = d.activePage;
31762                 return;
31763             }
31764             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31765             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31766             e.stopEvent();
31767         }
31768         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))
31769         {
31770           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31771           this.field.dom.value = pageNum;
31772           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31773           e.stopEvent();
31774         }
31775         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31776         {
31777           var v = this.field.dom.value, pageNum; 
31778           var increment = (e.shiftKey) ? 10 : 1;
31779           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31780             increment *= -1;
31781           }
31782           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31783             this.field.dom.value = d.activePage;
31784             return;
31785           }
31786           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31787           {
31788             this.field.dom.value = parseInt(v, 10) + increment;
31789             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31790             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31791           }
31792           e.stopEvent();
31793         }
31794     },
31795
31796     // private
31797     beforeLoad : function(){
31798         if(this.loading){
31799             this.loading.disable();
31800         }
31801     },
31802
31803     // private
31804     onClick : function(which){
31805         var ds = this.ds;
31806         switch(which){
31807             case "first":
31808                 ds.load({params:{start: 0, limit: this.pageSize}});
31809             break;
31810             case "prev":
31811                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31812             break;
31813             case "next":
31814                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31815             break;
31816             case "last":
31817                 var total = ds.getTotalCount();
31818                 var extra = total % this.pageSize;
31819                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31820                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31821             break;
31822             case "refresh":
31823                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31824             break;
31825         }
31826     },
31827
31828     /**
31829      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31830      * @param {Roo.data.Store} store The data store to unbind
31831      */
31832     unbind : function(ds){
31833         ds.un("beforeload", this.beforeLoad, this);
31834         ds.un("load", this.onLoad, this);
31835         ds.un("loadexception", this.onLoadError, this);
31836         ds.un("remove", this.updateInfo, this);
31837         ds.un("add", this.updateInfo, this);
31838         this.ds = undefined;
31839     },
31840
31841     /**
31842      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31843      * @param {Roo.data.Store} store The data store to bind
31844      */
31845     bind : function(ds){
31846         ds.on("beforeload", this.beforeLoad, this);
31847         ds.on("load", this.onLoad, this);
31848         ds.on("loadexception", this.onLoadError, this);
31849         ds.on("remove", this.updateInfo, this);
31850         ds.on("add", this.updateInfo, this);
31851         this.ds = ds;
31852     }
31853 });/*
31854  * Based on:
31855  * Ext JS Library 1.1.1
31856  * Copyright(c) 2006-2007, Ext JS, LLC.
31857  *
31858  * Originally Released Under LGPL - original licence link has changed is not relivant.
31859  *
31860  * Fork - LGPL
31861  * <script type="text/javascript">
31862  */
31863
31864 /**
31865  * @class Roo.Resizable
31866  * @extends Roo.util.Observable
31867  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31868  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31869  * 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
31870  * the element will be wrapped for you automatically.</p>
31871  * <p>Here is the list of valid resize handles:</p>
31872  * <pre>
31873 Value   Description
31874 ------  -------------------
31875  'n'     north
31876  's'     south
31877  'e'     east
31878  'w'     west
31879  'nw'    northwest
31880  'sw'    southwest
31881  'se'    southeast
31882  'ne'    northeast
31883  'hd'    horizontal drag
31884  'all'   all
31885 </pre>
31886  * <p>Here's an example showing the creation of a typical Resizable:</p>
31887  * <pre><code>
31888 var resizer = new Roo.Resizable("element-id", {
31889     handles: 'all',
31890     minWidth: 200,
31891     minHeight: 100,
31892     maxWidth: 500,
31893     maxHeight: 400,
31894     pinned: true
31895 });
31896 resizer.on("resize", myHandler);
31897 </code></pre>
31898  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31899  * resizer.east.setDisplayed(false);</p>
31900  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31901  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31902  * resize operation's new size (defaults to [0, 0])
31903  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31904  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31905  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31906  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31907  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31908  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31909  * @cfg {Number} width The width of the element in pixels (defaults to null)
31910  * @cfg {Number} height The height of the element in pixels (defaults to null)
31911  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31912  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31913  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31914  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31915  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31916  * in favor of the handles config option (defaults to false)
31917  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31918  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31919  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31920  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31921  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31922  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
31923  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
31924  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
31925  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
31926  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
31927  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
31928  * @constructor
31929  * Create a new resizable component
31930  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
31931  * @param {Object} config configuration options
31932   */
31933 Roo.Resizable = function(el, config)
31934 {
31935     this.el = Roo.get(el);
31936
31937     if(config && config.wrap){
31938         config.resizeChild = this.el;
31939         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
31940         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
31941         this.el.setStyle("overflow", "hidden");
31942         this.el.setPositioning(config.resizeChild.getPositioning());
31943         config.resizeChild.clearPositioning();
31944         if(!config.width || !config.height){
31945             var csize = config.resizeChild.getSize();
31946             this.el.setSize(csize.width, csize.height);
31947         }
31948         if(config.pinned && !config.adjustments){
31949             config.adjustments = "auto";
31950         }
31951     }
31952
31953     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
31954     this.proxy.unselectable();
31955     this.proxy.enableDisplayMode('block');
31956
31957     Roo.apply(this, config);
31958
31959     if(this.pinned){
31960         this.disableTrackOver = true;
31961         this.el.addClass("x-resizable-pinned");
31962     }
31963     // if the element isn't positioned, make it relative
31964     var position = this.el.getStyle("position");
31965     if(position != "absolute" && position != "fixed"){
31966         this.el.setStyle("position", "relative");
31967     }
31968     if(!this.handles){ // no handles passed, must be legacy style
31969         this.handles = 's,e,se';
31970         if(this.multiDirectional){
31971             this.handles += ',n,w';
31972         }
31973     }
31974     if(this.handles == "all"){
31975         this.handles = "n s e w ne nw se sw";
31976     }
31977     var hs = this.handles.split(/\s*?[,;]\s*?| /);
31978     var ps = Roo.Resizable.positions;
31979     for(var i = 0, len = hs.length; i < len; i++){
31980         if(hs[i] && ps[hs[i]]){
31981             var pos = ps[hs[i]];
31982             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
31983         }
31984     }
31985     // legacy
31986     this.corner = this.southeast;
31987     
31988     // updateBox = the box can move..
31989     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
31990         this.updateBox = true;
31991     }
31992
31993     this.activeHandle = null;
31994
31995     if(this.resizeChild){
31996         if(typeof this.resizeChild == "boolean"){
31997             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
31998         }else{
31999             this.resizeChild = Roo.get(this.resizeChild, true);
32000         }
32001     }
32002     
32003     if(this.adjustments == "auto"){
32004         var rc = this.resizeChild;
32005         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32006         if(rc && (hw || hn)){
32007             rc.position("relative");
32008             rc.setLeft(hw ? hw.el.getWidth() : 0);
32009             rc.setTop(hn ? hn.el.getHeight() : 0);
32010         }
32011         this.adjustments = [
32012             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32013             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32014         ];
32015     }
32016
32017     if(this.draggable){
32018         this.dd = this.dynamic ?
32019             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32020         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32021     }
32022
32023     // public events
32024     this.addEvents({
32025         /**
32026          * @event beforeresize
32027          * Fired before resize is allowed. Set enabled to false to cancel resize.
32028          * @param {Roo.Resizable} this
32029          * @param {Roo.EventObject} e The mousedown event
32030          */
32031         "beforeresize" : true,
32032         /**
32033          * @event resizing
32034          * Fired a resizing.
32035          * @param {Roo.Resizable} this
32036          * @param {Number} x The new x position
32037          * @param {Number} y The new y position
32038          * @param {Number} w The new w width
32039          * @param {Number} h The new h hight
32040          * @param {Roo.EventObject} e The mouseup event
32041          */
32042         "resizing" : true,
32043         /**
32044          * @event resize
32045          * Fired after a resize.
32046          * @param {Roo.Resizable} this
32047          * @param {Number} width The new width
32048          * @param {Number} height The new height
32049          * @param {Roo.EventObject} e The mouseup event
32050          */
32051         "resize" : true
32052     });
32053
32054     if(this.width !== null && this.height !== null){
32055         this.resizeTo(this.width, this.height);
32056     }else{
32057         this.updateChildSize();
32058     }
32059     if(Roo.isIE){
32060         this.el.dom.style.zoom = 1;
32061     }
32062     Roo.Resizable.superclass.constructor.call(this);
32063 };
32064
32065 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32066         resizeChild : false,
32067         adjustments : [0, 0],
32068         minWidth : 5,
32069         minHeight : 5,
32070         maxWidth : 10000,
32071         maxHeight : 10000,
32072         enabled : true,
32073         animate : false,
32074         duration : .35,
32075         dynamic : false,
32076         handles : false,
32077         multiDirectional : false,
32078         disableTrackOver : false,
32079         easing : 'easeOutStrong',
32080         widthIncrement : 0,
32081         heightIncrement : 0,
32082         pinned : false,
32083         width : null,
32084         height : null,
32085         preserveRatio : false,
32086         transparent: false,
32087         minX: 0,
32088         minY: 0,
32089         draggable: false,
32090
32091         /**
32092          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32093          */
32094         constrainTo: undefined,
32095         /**
32096          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32097          */
32098         resizeRegion: undefined,
32099
32100
32101     /**
32102      * Perform a manual resize
32103      * @param {Number} width
32104      * @param {Number} height
32105      */
32106     resizeTo : function(width, height){
32107         this.el.setSize(width, height);
32108         this.updateChildSize();
32109         this.fireEvent("resize", this, width, height, null);
32110     },
32111
32112     // private
32113     startSizing : function(e, handle){
32114         this.fireEvent("beforeresize", this, e);
32115         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32116
32117             if(!this.overlay){
32118                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32119                 this.overlay.unselectable();
32120                 this.overlay.enableDisplayMode("block");
32121                 this.overlay.on("mousemove", this.onMouseMove, this);
32122                 this.overlay.on("mouseup", this.onMouseUp, this);
32123             }
32124             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32125
32126             this.resizing = true;
32127             this.startBox = this.el.getBox();
32128             this.startPoint = e.getXY();
32129             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32130                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32131
32132             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32133             this.overlay.show();
32134
32135             if(this.constrainTo) {
32136                 var ct = Roo.get(this.constrainTo);
32137                 this.resizeRegion = ct.getRegion().adjust(
32138                     ct.getFrameWidth('t'),
32139                     ct.getFrameWidth('l'),
32140                     -ct.getFrameWidth('b'),
32141                     -ct.getFrameWidth('r')
32142                 );
32143             }
32144
32145             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32146             this.proxy.show();
32147             this.proxy.setBox(this.startBox);
32148             if(!this.dynamic){
32149                 this.proxy.setStyle('visibility', 'visible');
32150             }
32151         }
32152     },
32153
32154     // private
32155     onMouseDown : function(handle, e){
32156         if(this.enabled){
32157             e.stopEvent();
32158             this.activeHandle = handle;
32159             this.startSizing(e, handle);
32160         }
32161     },
32162
32163     // private
32164     onMouseUp : function(e){
32165         var size = this.resizeElement();
32166         this.resizing = false;
32167         this.handleOut();
32168         this.overlay.hide();
32169         this.proxy.hide();
32170         this.fireEvent("resize", this, size.width, size.height, e);
32171     },
32172
32173     // private
32174     updateChildSize : function(){
32175         
32176         if(this.resizeChild){
32177             var el = this.el;
32178             var child = this.resizeChild;
32179             var adj = this.adjustments;
32180             if(el.dom.offsetWidth){
32181                 var b = el.getSize(true);
32182                 child.setSize(b.width+adj[0], b.height+adj[1]);
32183             }
32184             // Second call here for IE
32185             // The first call enables instant resizing and
32186             // the second call corrects scroll bars if they
32187             // exist
32188             if(Roo.isIE){
32189                 setTimeout(function(){
32190                     if(el.dom.offsetWidth){
32191                         var b = el.getSize(true);
32192                         child.setSize(b.width+adj[0], b.height+adj[1]);
32193                     }
32194                 }, 10);
32195             }
32196         }
32197     },
32198
32199     // private
32200     snap : function(value, inc, min){
32201         if(!inc || !value) {
32202             return value;
32203         }
32204         var newValue = value;
32205         var m = value % inc;
32206         if(m > 0){
32207             if(m > (inc/2)){
32208                 newValue = value + (inc-m);
32209             }else{
32210                 newValue = value - m;
32211             }
32212         }
32213         return Math.max(min, newValue);
32214     },
32215
32216     // private
32217     resizeElement : function(){
32218         var box = this.proxy.getBox();
32219         if(this.updateBox){
32220             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32221         }else{
32222             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32223         }
32224         this.updateChildSize();
32225         if(!this.dynamic){
32226             this.proxy.hide();
32227         }
32228         return box;
32229     },
32230
32231     // private
32232     constrain : function(v, diff, m, mx){
32233         if(v - diff < m){
32234             diff = v - m;
32235         }else if(v - diff > mx){
32236             diff = mx - v;
32237         }
32238         return diff;
32239     },
32240
32241     // private
32242     onMouseMove : function(e){
32243         
32244         if(this.enabled){
32245             try{// try catch so if something goes wrong the user doesn't get hung
32246
32247             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32248                 return;
32249             }
32250
32251             //var curXY = this.startPoint;
32252             var curSize = this.curSize || this.startBox;
32253             var x = this.startBox.x, y = this.startBox.y;
32254             var ox = x, oy = y;
32255             var w = curSize.width, h = curSize.height;
32256             var ow = w, oh = h;
32257             var mw = this.minWidth, mh = this.minHeight;
32258             var mxw = this.maxWidth, mxh = this.maxHeight;
32259             var wi = this.widthIncrement;
32260             var hi = this.heightIncrement;
32261
32262             var eventXY = e.getXY();
32263             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32264             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32265
32266             var pos = this.activeHandle.position;
32267
32268             switch(pos){
32269                 case "east":
32270                     w += diffX;
32271                     w = Math.min(Math.max(mw, w), mxw);
32272                     break;
32273              
32274                 case "south":
32275                     h += diffY;
32276                     h = Math.min(Math.max(mh, h), mxh);
32277                     break;
32278                 case "southeast":
32279                     w += diffX;
32280                     h += diffY;
32281                     w = Math.min(Math.max(mw, w), mxw);
32282                     h = Math.min(Math.max(mh, h), mxh);
32283                     break;
32284                 case "north":
32285                     diffY = this.constrain(h, diffY, mh, mxh);
32286                     y += diffY;
32287                     h -= diffY;
32288                     break;
32289                 case "hdrag":
32290                     
32291                     if (wi) {
32292                         var adiffX = Math.abs(diffX);
32293                         var sub = (adiffX % wi); // how much 
32294                         if (sub > (wi/2)) { // far enough to snap
32295                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32296                         } else {
32297                             // remove difference.. 
32298                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32299                         }
32300                     }
32301                     x += diffX;
32302                     x = Math.max(this.minX, x);
32303                     break;
32304                 case "west":
32305                     diffX = this.constrain(w, diffX, mw, mxw);
32306                     x += diffX;
32307                     w -= diffX;
32308                     break;
32309                 case "northeast":
32310                     w += diffX;
32311                     w = Math.min(Math.max(mw, w), mxw);
32312                     diffY = this.constrain(h, diffY, mh, mxh);
32313                     y += diffY;
32314                     h -= diffY;
32315                     break;
32316                 case "northwest":
32317                     diffX = this.constrain(w, diffX, mw, mxw);
32318                     diffY = this.constrain(h, diffY, mh, mxh);
32319                     y += diffY;
32320                     h -= diffY;
32321                     x += diffX;
32322                     w -= diffX;
32323                     break;
32324                case "southwest":
32325                     diffX = this.constrain(w, diffX, mw, mxw);
32326                     h += diffY;
32327                     h = Math.min(Math.max(mh, h), mxh);
32328                     x += diffX;
32329                     w -= diffX;
32330                     break;
32331             }
32332
32333             var sw = this.snap(w, wi, mw);
32334             var sh = this.snap(h, hi, mh);
32335             if(sw != w || sh != h){
32336                 switch(pos){
32337                     case "northeast":
32338                         y -= sh - h;
32339                     break;
32340                     case "north":
32341                         y -= sh - h;
32342                         break;
32343                     case "southwest":
32344                         x -= sw - w;
32345                     break;
32346                     case "west":
32347                         x -= sw - w;
32348                         break;
32349                     case "northwest":
32350                         x -= sw - w;
32351                         y -= sh - h;
32352                     break;
32353                 }
32354                 w = sw;
32355                 h = sh;
32356             }
32357
32358             if(this.preserveRatio){
32359                 switch(pos){
32360                     case "southeast":
32361                     case "east":
32362                         h = oh * (w/ow);
32363                         h = Math.min(Math.max(mh, h), mxh);
32364                         w = ow * (h/oh);
32365                        break;
32366                     case "south":
32367                         w = ow * (h/oh);
32368                         w = Math.min(Math.max(mw, w), mxw);
32369                         h = oh * (w/ow);
32370                         break;
32371                     case "northeast":
32372                         w = ow * (h/oh);
32373                         w = Math.min(Math.max(mw, w), mxw);
32374                         h = oh * (w/ow);
32375                     break;
32376                     case "north":
32377                         var tw = w;
32378                         w = ow * (h/oh);
32379                         w = Math.min(Math.max(mw, w), mxw);
32380                         h = oh * (w/ow);
32381                         x += (tw - w) / 2;
32382                         break;
32383                     case "southwest":
32384                         h = oh * (w/ow);
32385                         h = Math.min(Math.max(mh, h), mxh);
32386                         var tw = w;
32387                         w = ow * (h/oh);
32388                         x += tw - w;
32389                         break;
32390                     case "west":
32391                         var th = h;
32392                         h = oh * (w/ow);
32393                         h = Math.min(Math.max(mh, h), mxh);
32394                         y += (th - h) / 2;
32395                         var tw = w;
32396                         w = ow * (h/oh);
32397                         x += tw - w;
32398                        break;
32399                     case "northwest":
32400                         var tw = w;
32401                         var th = h;
32402                         h = oh * (w/ow);
32403                         h = Math.min(Math.max(mh, h), mxh);
32404                         w = ow * (h/oh);
32405                         y += th - h;
32406                         x += tw - w;
32407                        break;
32408
32409                 }
32410             }
32411             if (pos == 'hdrag') {
32412                 w = ow;
32413             }
32414             this.proxy.setBounds(x, y, w, h);
32415             if(this.dynamic){
32416                 this.resizeElement();
32417             }
32418             }catch(e){}
32419         }
32420         this.fireEvent("resizing", this, x, y, w, h, e);
32421     },
32422
32423     // private
32424     handleOver : function(){
32425         if(this.enabled){
32426             this.el.addClass("x-resizable-over");
32427         }
32428     },
32429
32430     // private
32431     handleOut : function(){
32432         if(!this.resizing){
32433             this.el.removeClass("x-resizable-over");
32434         }
32435     },
32436
32437     /**
32438      * Returns the element this component is bound to.
32439      * @return {Roo.Element}
32440      */
32441     getEl : function(){
32442         return this.el;
32443     },
32444
32445     /**
32446      * Returns the resizeChild element (or null).
32447      * @return {Roo.Element}
32448      */
32449     getResizeChild : function(){
32450         return this.resizeChild;
32451     },
32452     groupHandler : function()
32453     {
32454         
32455     },
32456     /**
32457      * Destroys this resizable. If the element was wrapped and
32458      * removeEl is not true then the element remains.
32459      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32460      */
32461     destroy : function(removeEl){
32462         this.proxy.remove();
32463         if(this.overlay){
32464             this.overlay.removeAllListeners();
32465             this.overlay.remove();
32466         }
32467         var ps = Roo.Resizable.positions;
32468         for(var k in ps){
32469             if(typeof ps[k] != "function" && this[ps[k]]){
32470                 var h = this[ps[k]];
32471                 h.el.removeAllListeners();
32472                 h.el.remove();
32473             }
32474         }
32475         if(removeEl){
32476             this.el.update("");
32477             this.el.remove();
32478         }
32479     }
32480 });
32481
32482 // private
32483 // hash to map config positions to true positions
32484 Roo.Resizable.positions = {
32485     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32486     hd: "hdrag"
32487 };
32488
32489 // private
32490 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32491     if(!this.tpl){
32492         // only initialize the template if resizable is used
32493         var tpl = Roo.DomHelper.createTemplate(
32494             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32495         );
32496         tpl.compile();
32497         Roo.Resizable.Handle.prototype.tpl = tpl;
32498     }
32499     this.position = pos;
32500     this.rz = rz;
32501     // show north drag fro topdra
32502     var handlepos = pos == 'hdrag' ? 'north' : pos;
32503     
32504     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32505     if (pos == 'hdrag') {
32506         this.el.setStyle('cursor', 'pointer');
32507     }
32508     this.el.unselectable();
32509     if(transparent){
32510         this.el.setOpacity(0);
32511     }
32512     this.el.on("mousedown", this.onMouseDown, this);
32513     if(!disableTrackOver){
32514         this.el.on("mouseover", this.onMouseOver, this);
32515         this.el.on("mouseout", this.onMouseOut, this);
32516     }
32517 };
32518
32519 // private
32520 Roo.Resizable.Handle.prototype = {
32521     afterResize : function(rz){
32522         Roo.log('after?');
32523         // do nothing
32524     },
32525     // private
32526     onMouseDown : function(e){
32527         this.rz.onMouseDown(this, e);
32528     },
32529     // private
32530     onMouseOver : function(e){
32531         this.rz.handleOver(this, e);
32532     },
32533     // private
32534     onMouseOut : function(e){
32535         this.rz.handleOut(this, e);
32536     }
32537 };/*
32538  * Based on:
32539  * Ext JS Library 1.1.1
32540  * Copyright(c) 2006-2007, Ext JS, LLC.
32541  *
32542  * Originally Released Under LGPL - original licence link has changed is not relivant.
32543  *
32544  * Fork - LGPL
32545  * <script type="text/javascript">
32546  */
32547
32548 /**
32549  * @class Roo.Editor
32550  * @extends Roo.Component
32551  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32552  * @constructor
32553  * Create a new Editor
32554  * @param {Roo.form.Field} field The Field object (or descendant)
32555  * @param {Object} config The config object
32556  */
32557 Roo.Editor = function(field, config){
32558     Roo.Editor.superclass.constructor.call(this, config);
32559     this.field = field;
32560     this.addEvents({
32561         /**
32562              * @event beforestartedit
32563              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32564              * false from the handler of this event.
32565              * @param {Editor} this
32566              * @param {Roo.Element} boundEl The underlying element bound to this editor
32567              * @param {Mixed} value The field value being set
32568              */
32569         "beforestartedit" : true,
32570         /**
32571              * @event startedit
32572              * Fires when this editor is displayed
32573              * @param {Roo.Element} boundEl The underlying element bound to this editor
32574              * @param {Mixed} value The starting field value
32575              */
32576         "startedit" : true,
32577         /**
32578              * @event beforecomplete
32579              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32580              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32581              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32582              * event will not fire since no edit actually occurred.
32583              * @param {Editor} this
32584              * @param {Mixed} value The current field value
32585              * @param {Mixed} startValue The original field value
32586              */
32587         "beforecomplete" : true,
32588         /**
32589              * @event complete
32590              * Fires after editing is complete and any changed value has been written to the underlying field.
32591              * @param {Editor} this
32592              * @param {Mixed} value The current field value
32593              * @param {Mixed} startValue The original field value
32594              */
32595         "complete" : true,
32596         /**
32597          * @event specialkey
32598          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32599          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32600          * @param {Roo.form.Field} this
32601          * @param {Roo.EventObject} e The event object
32602          */
32603         "specialkey" : true
32604     });
32605 };
32606
32607 Roo.extend(Roo.Editor, Roo.Component, {
32608     /**
32609      * @cfg {Boolean/String} autosize
32610      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32611      * or "height" to adopt the height only (defaults to false)
32612      */
32613     /**
32614      * @cfg {Boolean} revertInvalid
32615      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32616      * validation fails (defaults to true)
32617      */
32618     /**
32619      * @cfg {Boolean} ignoreNoChange
32620      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32621      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32622      * will never be ignored.
32623      */
32624     /**
32625      * @cfg {Boolean} hideEl
32626      * False to keep the bound element visible while the editor is displayed (defaults to true)
32627      */
32628     /**
32629      * @cfg {Mixed} value
32630      * The data value of the underlying field (defaults to "")
32631      */
32632     value : "",
32633     /**
32634      * @cfg {String} alignment
32635      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32636      */
32637     alignment: "c-c?",
32638     /**
32639      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32640      * for bottom-right shadow (defaults to "frame")
32641      */
32642     shadow : "frame",
32643     /**
32644      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32645      */
32646     constrain : false,
32647     /**
32648      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32649      */
32650     completeOnEnter : false,
32651     /**
32652      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32653      */
32654     cancelOnEsc : false,
32655     /**
32656      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32657      */
32658     updateEl : false,
32659
32660     // private
32661     onRender : function(ct, position){
32662         this.el = new Roo.Layer({
32663             shadow: this.shadow,
32664             cls: "x-editor",
32665             parentEl : ct,
32666             shim : this.shim,
32667             shadowOffset:4,
32668             id: this.id,
32669             constrain: this.constrain
32670         });
32671         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32672         if(this.field.msgTarget != 'title'){
32673             this.field.msgTarget = 'qtip';
32674         }
32675         this.field.render(this.el);
32676         if(Roo.isGecko){
32677             this.field.el.dom.setAttribute('autocomplete', 'off');
32678         }
32679         this.field.on("specialkey", this.onSpecialKey, this);
32680         if(this.swallowKeys){
32681             this.field.el.swallowEvent(['keydown','keypress']);
32682         }
32683         this.field.show();
32684         this.field.on("blur", this.onBlur, this);
32685         if(this.field.grow){
32686             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32687         }
32688     },
32689
32690     onSpecialKey : function(field, e)
32691     {
32692         //Roo.log('editor onSpecialKey');
32693         if(this.completeOnEnter && e.getKey() == e.ENTER){
32694             e.stopEvent();
32695             this.completeEdit();
32696             return;
32697         }
32698         // do not fire special key otherwise it might hide close the editor...
32699         if(e.getKey() == e.ENTER){    
32700             return;
32701         }
32702         if(this.cancelOnEsc && e.getKey() == e.ESC){
32703             this.cancelEdit();
32704             return;
32705         } 
32706         this.fireEvent('specialkey', field, e);
32707     
32708     },
32709
32710     /**
32711      * Starts the editing process and shows the editor.
32712      * @param {String/HTMLElement/Element} el The element to edit
32713      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32714       * to the innerHTML of el.
32715      */
32716     startEdit : function(el, value){
32717         if(this.editing){
32718             this.completeEdit();
32719         }
32720         this.boundEl = Roo.get(el);
32721         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32722         if(!this.rendered){
32723             this.render(this.parentEl || document.body);
32724         }
32725         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32726             return;
32727         }
32728         this.startValue = v;
32729         this.field.setValue(v);
32730         if(this.autoSize){
32731             var sz = this.boundEl.getSize();
32732             switch(this.autoSize){
32733                 case "width":
32734                 this.setSize(sz.width,  "");
32735                 break;
32736                 case "height":
32737                 this.setSize("",  sz.height);
32738                 break;
32739                 default:
32740                 this.setSize(sz.width,  sz.height);
32741             }
32742         }
32743         this.el.alignTo(this.boundEl, this.alignment);
32744         this.editing = true;
32745         if(Roo.QuickTips){
32746             Roo.QuickTips.disable();
32747         }
32748         this.show();
32749     },
32750
32751     /**
32752      * Sets the height and width of this editor.
32753      * @param {Number} width The new width
32754      * @param {Number} height The new height
32755      */
32756     setSize : function(w, h){
32757         this.field.setSize(w, h);
32758         if(this.el){
32759             this.el.sync();
32760         }
32761     },
32762
32763     /**
32764      * Realigns the editor to the bound field based on the current alignment config value.
32765      */
32766     realign : function(){
32767         this.el.alignTo(this.boundEl, this.alignment);
32768     },
32769
32770     /**
32771      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32772      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32773      */
32774     completeEdit : function(remainVisible){
32775         if(!this.editing){
32776             return;
32777         }
32778         var v = this.getValue();
32779         if(this.revertInvalid !== false && !this.field.isValid()){
32780             v = this.startValue;
32781             this.cancelEdit(true);
32782         }
32783         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32784             this.editing = false;
32785             this.hide();
32786             return;
32787         }
32788         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32789             this.editing = false;
32790             if(this.updateEl && this.boundEl){
32791                 this.boundEl.update(v);
32792             }
32793             if(remainVisible !== true){
32794                 this.hide();
32795             }
32796             this.fireEvent("complete", this, v, this.startValue);
32797         }
32798     },
32799
32800     // private
32801     onShow : function(){
32802         this.el.show();
32803         if(this.hideEl !== false){
32804             this.boundEl.hide();
32805         }
32806         this.field.show();
32807         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32808             this.fixIEFocus = true;
32809             this.deferredFocus.defer(50, this);
32810         }else{
32811             this.field.focus();
32812         }
32813         this.fireEvent("startedit", this.boundEl, this.startValue);
32814     },
32815
32816     deferredFocus : function(){
32817         if(this.editing){
32818             this.field.focus();
32819         }
32820     },
32821
32822     /**
32823      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32824      * reverted to the original starting value.
32825      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32826      * cancel (defaults to false)
32827      */
32828     cancelEdit : function(remainVisible){
32829         if(this.editing){
32830             this.setValue(this.startValue);
32831             if(remainVisible !== true){
32832                 this.hide();
32833             }
32834         }
32835     },
32836
32837     // private
32838     onBlur : function(){
32839         if(this.allowBlur !== true && this.editing){
32840             this.completeEdit();
32841         }
32842     },
32843
32844     // private
32845     onHide : function(){
32846         if(this.editing){
32847             this.completeEdit();
32848             return;
32849         }
32850         this.field.blur();
32851         if(this.field.collapse){
32852             this.field.collapse();
32853         }
32854         this.el.hide();
32855         if(this.hideEl !== false){
32856             this.boundEl.show();
32857         }
32858         if(Roo.QuickTips){
32859             Roo.QuickTips.enable();
32860         }
32861     },
32862
32863     /**
32864      * Sets the data value of the editor
32865      * @param {Mixed} value Any valid value supported by the underlying field
32866      */
32867     setValue : function(v){
32868         this.field.setValue(v);
32869     },
32870
32871     /**
32872      * Gets the data value of the editor
32873      * @return {Mixed} The data value
32874      */
32875     getValue : function(){
32876         return this.field.getValue();
32877     }
32878 });/*
32879  * Based on:
32880  * Ext JS Library 1.1.1
32881  * Copyright(c) 2006-2007, Ext JS, LLC.
32882  *
32883  * Originally Released Under LGPL - original licence link has changed is not relivant.
32884  *
32885  * Fork - LGPL
32886  * <script type="text/javascript">
32887  */
32888  
32889 /**
32890  * @class Roo.BasicDialog
32891  * @extends Roo.util.Observable
32892  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32893  * <pre><code>
32894 var dlg = new Roo.BasicDialog("my-dlg", {
32895     height: 200,
32896     width: 300,
32897     minHeight: 100,
32898     minWidth: 150,
32899     modal: true,
32900     proxyDrag: true,
32901     shadow: true
32902 });
32903 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32904 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32905 dlg.addButton('Cancel', dlg.hide, dlg);
32906 dlg.show();
32907 </code></pre>
32908   <b>A Dialog should always be a direct child of the body element.</b>
32909  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32910  * @cfg {String} title Default text to display in the title bar (defaults to null)
32911  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32912  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32913  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32914  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32915  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32916  * (defaults to null with no animation)
32917  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32918  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32919  * property for valid values (defaults to 'all')
32920  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32921  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32922  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
32923  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
32924  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
32925  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
32926  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
32927  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
32928  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
32929  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
32930  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
32931  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
32932  * draggable = true (defaults to false)
32933  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
32934  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
32935  * shadow (defaults to false)
32936  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
32937  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
32938  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
32939  * @cfg {Array} buttons Array of buttons
32940  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
32941  * @constructor
32942  * Create a new BasicDialog.
32943  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
32944  * @param {Object} config Configuration options
32945  */
32946 Roo.BasicDialog = function(el, config){
32947     this.el = Roo.get(el);
32948     var dh = Roo.DomHelper;
32949     if(!this.el && config && config.autoCreate){
32950         if(typeof config.autoCreate == "object"){
32951             if(!config.autoCreate.id){
32952                 config.autoCreate.id = el;
32953             }
32954             this.el = dh.append(document.body,
32955                         config.autoCreate, true);
32956         }else{
32957             this.el = dh.append(document.body,
32958                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
32959         }
32960     }
32961     el = this.el;
32962     el.setDisplayed(true);
32963     el.hide = this.hideAction;
32964     this.id = el.id;
32965     el.addClass("x-dlg");
32966
32967     Roo.apply(this, config);
32968
32969     this.proxy = el.createProxy("x-dlg-proxy");
32970     this.proxy.hide = this.hideAction;
32971     this.proxy.setOpacity(.5);
32972     this.proxy.hide();
32973
32974     if(config.width){
32975         el.setWidth(config.width);
32976     }
32977     if(config.height){
32978         el.setHeight(config.height);
32979     }
32980     this.size = el.getSize();
32981     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
32982         this.xy = [config.x,config.y];
32983     }else{
32984         this.xy = el.getCenterXY(true);
32985     }
32986     /** The header element @type Roo.Element */
32987     this.header = el.child("> .x-dlg-hd");
32988     /** The body element @type Roo.Element */
32989     this.body = el.child("> .x-dlg-bd");
32990     /** The footer element @type Roo.Element */
32991     this.footer = el.child("> .x-dlg-ft");
32992
32993     if(!this.header){
32994         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
32995     }
32996     if(!this.body){
32997         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
32998     }
32999
33000     this.header.unselectable();
33001     if(this.title){
33002         this.header.update(this.title);
33003     }
33004     // this element allows the dialog to be focused for keyboard event
33005     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33006     this.focusEl.swallowEvent("click", true);
33007
33008     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33009
33010     // wrap the body and footer for special rendering
33011     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33012     if(this.footer){
33013         this.bwrap.dom.appendChild(this.footer.dom);
33014     }
33015
33016     this.bg = this.el.createChild({
33017         tag: "div", cls:"x-dlg-bg",
33018         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33019     });
33020     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33021
33022
33023     if(this.autoScroll !== false && !this.autoTabs){
33024         this.body.setStyle("overflow", "auto");
33025     }
33026
33027     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33028
33029     if(this.closable !== false){
33030         this.el.addClass("x-dlg-closable");
33031         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33032         this.close.on("click", this.closeClick, this);
33033         this.close.addClassOnOver("x-dlg-close-over");
33034     }
33035     if(this.collapsible !== false){
33036         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33037         this.collapseBtn.on("click", this.collapseClick, this);
33038         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33039         this.header.on("dblclick", this.collapseClick, this);
33040     }
33041     if(this.resizable !== false){
33042         this.el.addClass("x-dlg-resizable");
33043         this.resizer = new Roo.Resizable(el, {
33044             minWidth: this.minWidth || 80,
33045             minHeight:this.minHeight || 80,
33046             handles: this.resizeHandles || "all",
33047             pinned: true
33048         });
33049         this.resizer.on("beforeresize", this.beforeResize, this);
33050         this.resizer.on("resize", this.onResize, this);
33051     }
33052     if(this.draggable !== false){
33053         el.addClass("x-dlg-draggable");
33054         if (!this.proxyDrag) {
33055             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33056         }
33057         else {
33058             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33059         }
33060         dd.setHandleElId(this.header.id);
33061         dd.endDrag = this.endMove.createDelegate(this);
33062         dd.startDrag = this.startMove.createDelegate(this);
33063         dd.onDrag = this.onDrag.createDelegate(this);
33064         dd.scroll = false;
33065         this.dd = dd;
33066     }
33067     if(this.modal){
33068         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33069         this.mask.enableDisplayMode("block");
33070         this.mask.hide();
33071         this.el.addClass("x-dlg-modal");
33072     }
33073     if(this.shadow){
33074         this.shadow = new Roo.Shadow({
33075             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33076             offset : this.shadowOffset
33077         });
33078     }else{
33079         this.shadowOffset = 0;
33080     }
33081     if(Roo.useShims && this.shim !== false){
33082         this.shim = this.el.createShim();
33083         this.shim.hide = this.hideAction;
33084         this.shim.hide();
33085     }else{
33086         this.shim = false;
33087     }
33088     if(this.autoTabs){
33089         this.initTabs();
33090     }
33091     if (this.buttons) { 
33092         var bts= this.buttons;
33093         this.buttons = [];
33094         Roo.each(bts, function(b) {
33095             this.addButton(b);
33096         }, this);
33097     }
33098     
33099     
33100     this.addEvents({
33101         /**
33102          * @event keydown
33103          * Fires when a key is pressed
33104          * @param {Roo.BasicDialog} this
33105          * @param {Roo.EventObject} e
33106          */
33107         "keydown" : true,
33108         /**
33109          * @event move
33110          * Fires when this dialog is moved by the user.
33111          * @param {Roo.BasicDialog} this
33112          * @param {Number} x The new page X
33113          * @param {Number} y The new page Y
33114          */
33115         "move" : true,
33116         /**
33117          * @event resize
33118          * Fires when this dialog is resized by the user.
33119          * @param {Roo.BasicDialog} this
33120          * @param {Number} width The new width
33121          * @param {Number} height The new height
33122          */
33123         "resize" : true,
33124         /**
33125          * @event beforehide
33126          * Fires before this dialog is hidden.
33127          * @param {Roo.BasicDialog} this
33128          */
33129         "beforehide" : true,
33130         /**
33131          * @event hide
33132          * Fires when this dialog is hidden.
33133          * @param {Roo.BasicDialog} this
33134          */
33135         "hide" : true,
33136         /**
33137          * @event beforeshow
33138          * Fires before this dialog is shown.
33139          * @param {Roo.BasicDialog} this
33140          */
33141         "beforeshow" : true,
33142         /**
33143          * @event show
33144          * Fires when this dialog is shown.
33145          * @param {Roo.BasicDialog} this
33146          */
33147         "show" : true
33148     });
33149     el.on("keydown", this.onKeyDown, this);
33150     el.on("mousedown", this.toFront, this);
33151     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33152     this.el.hide();
33153     Roo.DialogManager.register(this);
33154     Roo.BasicDialog.superclass.constructor.call(this);
33155 };
33156
33157 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33158     shadowOffset: Roo.isIE ? 6 : 5,
33159     minHeight: 80,
33160     minWidth: 200,
33161     minButtonWidth: 75,
33162     defaultButton: null,
33163     buttonAlign: "right",
33164     tabTag: 'div',
33165     firstShow: true,
33166
33167     /**
33168      * Sets the dialog title text
33169      * @param {String} text The title text to display
33170      * @return {Roo.BasicDialog} this
33171      */
33172     setTitle : function(text){
33173         this.header.update(text);
33174         return this;
33175     },
33176
33177     // private
33178     closeClick : function(){
33179         this.hide();
33180     },
33181
33182     // private
33183     collapseClick : function(){
33184         this[this.collapsed ? "expand" : "collapse"]();
33185     },
33186
33187     /**
33188      * Collapses the dialog to its minimized state (only the title bar is visible).
33189      * Equivalent to the user clicking the collapse dialog button.
33190      */
33191     collapse : function(){
33192         if(!this.collapsed){
33193             this.collapsed = true;
33194             this.el.addClass("x-dlg-collapsed");
33195             this.restoreHeight = this.el.getHeight();
33196             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33197         }
33198     },
33199
33200     /**
33201      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33202      * clicking the expand dialog button.
33203      */
33204     expand : function(){
33205         if(this.collapsed){
33206             this.collapsed = false;
33207             this.el.removeClass("x-dlg-collapsed");
33208             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33209         }
33210     },
33211
33212     /**
33213      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33214      * @return {Roo.TabPanel} The tabs component
33215      */
33216     initTabs : function(){
33217         var tabs = this.getTabs();
33218         while(tabs.getTab(0)){
33219             tabs.removeTab(0);
33220         }
33221         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33222             var dom = el.dom;
33223             tabs.addTab(Roo.id(dom), dom.title);
33224             dom.title = "";
33225         });
33226         tabs.activate(0);
33227         return tabs;
33228     },
33229
33230     // private
33231     beforeResize : function(){
33232         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33233     },
33234
33235     // private
33236     onResize : function(){
33237         this.refreshSize();
33238         this.syncBodyHeight();
33239         this.adjustAssets();
33240         this.focus();
33241         this.fireEvent("resize", this, this.size.width, this.size.height);
33242     },
33243
33244     // private
33245     onKeyDown : function(e){
33246         if(this.isVisible()){
33247             this.fireEvent("keydown", this, e);
33248         }
33249     },
33250
33251     /**
33252      * Resizes the dialog.
33253      * @param {Number} width
33254      * @param {Number} height
33255      * @return {Roo.BasicDialog} this
33256      */
33257     resizeTo : function(width, height){
33258         this.el.setSize(width, height);
33259         this.size = {width: width, height: height};
33260         this.syncBodyHeight();
33261         if(this.fixedcenter){
33262             this.center();
33263         }
33264         if(this.isVisible()){
33265             this.constrainXY();
33266             this.adjustAssets();
33267         }
33268         this.fireEvent("resize", this, width, height);
33269         return this;
33270     },
33271
33272
33273     /**
33274      * Resizes the dialog to fit the specified content size.
33275      * @param {Number} width
33276      * @param {Number} height
33277      * @return {Roo.BasicDialog} this
33278      */
33279     setContentSize : function(w, h){
33280         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33281         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33282         //if(!this.el.isBorderBox()){
33283             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33284             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33285         //}
33286         if(this.tabs){
33287             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33288             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33289         }
33290         this.resizeTo(w, h);
33291         return this;
33292     },
33293
33294     /**
33295      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33296      * executed in response to a particular key being pressed while the dialog is active.
33297      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33298      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33299      * @param {Function} fn The function to call
33300      * @param {Object} scope (optional) The scope of the function
33301      * @return {Roo.BasicDialog} this
33302      */
33303     addKeyListener : function(key, fn, scope){
33304         var keyCode, shift, ctrl, alt;
33305         if(typeof key == "object" && !(key instanceof Array)){
33306             keyCode = key["key"];
33307             shift = key["shift"];
33308             ctrl = key["ctrl"];
33309             alt = key["alt"];
33310         }else{
33311             keyCode = key;
33312         }
33313         var handler = function(dlg, e){
33314             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33315                 var k = e.getKey();
33316                 if(keyCode instanceof Array){
33317                     for(var i = 0, len = keyCode.length; i < len; i++){
33318                         if(keyCode[i] == k){
33319                           fn.call(scope || window, dlg, k, e);
33320                           return;
33321                         }
33322                     }
33323                 }else{
33324                     if(k == keyCode){
33325                         fn.call(scope || window, dlg, k, e);
33326                     }
33327                 }
33328             }
33329         };
33330         this.on("keydown", handler);
33331         return this;
33332     },
33333
33334     /**
33335      * Returns the TabPanel component (creates it if it doesn't exist).
33336      * Note: If you wish to simply check for the existence of tabs without creating them,
33337      * check for a null 'tabs' property.
33338      * @return {Roo.TabPanel} The tabs component
33339      */
33340     getTabs : function(){
33341         if(!this.tabs){
33342             this.el.addClass("x-dlg-auto-tabs");
33343             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33344             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33345         }
33346         return this.tabs;
33347     },
33348
33349     /**
33350      * Adds a button to the footer section of the dialog.
33351      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33352      * object or a valid Roo.DomHelper element config
33353      * @param {Function} handler The function called when the button is clicked
33354      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33355      * @return {Roo.Button} The new button
33356      */
33357     addButton : function(config, handler, scope){
33358         var dh = Roo.DomHelper;
33359         if(!this.footer){
33360             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33361         }
33362         if(!this.btnContainer){
33363             var tb = this.footer.createChild({
33364
33365                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33366                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33367             }, null, true);
33368             this.btnContainer = tb.firstChild.firstChild.firstChild;
33369         }
33370         var bconfig = {
33371             handler: handler,
33372             scope: scope,
33373             minWidth: this.minButtonWidth,
33374             hideParent:true
33375         };
33376         if(typeof config == "string"){
33377             bconfig.text = config;
33378         }else{
33379             if(config.tag){
33380                 bconfig.dhconfig = config;
33381             }else{
33382                 Roo.apply(bconfig, config);
33383             }
33384         }
33385         var fc = false;
33386         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33387             bconfig.position = Math.max(0, bconfig.position);
33388             fc = this.btnContainer.childNodes[bconfig.position];
33389         }
33390          
33391         var btn = new Roo.Button(
33392             fc ? 
33393                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33394                 : this.btnContainer.appendChild(document.createElement("td")),
33395             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33396             bconfig
33397         );
33398         this.syncBodyHeight();
33399         if(!this.buttons){
33400             /**
33401              * Array of all the buttons that have been added to this dialog via addButton
33402              * @type Array
33403              */
33404             this.buttons = [];
33405         }
33406         this.buttons.push(btn);
33407         return btn;
33408     },
33409
33410     /**
33411      * Sets the default button to be focused when the dialog is displayed.
33412      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33413      * @return {Roo.BasicDialog} this
33414      */
33415     setDefaultButton : function(btn){
33416         this.defaultButton = btn;
33417         return this;
33418     },
33419
33420     // private
33421     getHeaderFooterHeight : function(safe){
33422         var height = 0;
33423         if(this.header){
33424            height += this.header.getHeight();
33425         }
33426         if(this.footer){
33427            var fm = this.footer.getMargins();
33428             height += (this.footer.getHeight()+fm.top+fm.bottom);
33429         }
33430         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33431         height += this.centerBg.getPadding("tb");
33432         return height;
33433     },
33434
33435     // private
33436     syncBodyHeight : function()
33437     {
33438         var bd = this.body, // the text
33439             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33440             bw = this.bwrap;
33441         var height = this.size.height - this.getHeaderFooterHeight(false);
33442         bd.setHeight(height-bd.getMargins("tb"));
33443         var hh = this.header.getHeight();
33444         var h = this.size.height-hh;
33445         cb.setHeight(h);
33446         
33447         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33448         bw.setHeight(h-cb.getPadding("tb"));
33449         
33450         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33451         bd.setWidth(bw.getWidth(true));
33452         if(this.tabs){
33453             this.tabs.syncHeight();
33454             if(Roo.isIE){
33455                 this.tabs.el.repaint();
33456             }
33457         }
33458     },
33459
33460     /**
33461      * Restores the previous state of the dialog if Roo.state is configured.
33462      * @return {Roo.BasicDialog} this
33463      */
33464     restoreState : function(){
33465         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33466         if(box && box.width){
33467             this.xy = [box.x, box.y];
33468             this.resizeTo(box.width, box.height);
33469         }
33470         return this;
33471     },
33472
33473     // private
33474     beforeShow : function(){
33475         this.expand();
33476         if(this.fixedcenter){
33477             this.xy = this.el.getCenterXY(true);
33478         }
33479         if(this.modal){
33480             Roo.get(document.body).addClass("x-body-masked");
33481             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33482             this.mask.show();
33483         }
33484         this.constrainXY();
33485     },
33486
33487     // private
33488     animShow : function(){
33489         var b = Roo.get(this.animateTarget).getBox();
33490         this.proxy.setSize(b.width, b.height);
33491         this.proxy.setLocation(b.x, b.y);
33492         this.proxy.show();
33493         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33494                     true, .35, this.showEl.createDelegate(this));
33495     },
33496
33497     /**
33498      * Shows the dialog.
33499      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33500      * @return {Roo.BasicDialog} this
33501      */
33502     show : function(animateTarget){
33503         if (this.fireEvent("beforeshow", this) === false){
33504             return;
33505         }
33506         if(this.syncHeightBeforeShow){
33507             this.syncBodyHeight();
33508         }else if(this.firstShow){
33509             this.firstShow = false;
33510             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33511         }
33512         this.animateTarget = animateTarget || this.animateTarget;
33513         if(!this.el.isVisible()){
33514             this.beforeShow();
33515             if(this.animateTarget && Roo.get(this.animateTarget)){
33516                 this.animShow();
33517             }else{
33518                 this.showEl();
33519             }
33520         }
33521         return this;
33522     },
33523
33524     // private
33525     showEl : function(){
33526         this.proxy.hide();
33527         this.el.setXY(this.xy);
33528         this.el.show();
33529         this.adjustAssets(true);
33530         this.toFront();
33531         this.focus();
33532         // IE peekaboo bug - fix found by Dave Fenwick
33533         if(Roo.isIE){
33534             this.el.repaint();
33535         }
33536         this.fireEvent("show", this);
33537     },
33538
33539     /**
33540      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33541      * dialog itself will receive focus.
33542      */
33543     focus : function(){
33544         if(this.defaultButton){
33545             this.defaultButton.focus();
33546         }else{
33547             this.focusEl.focus();
33548         }
33549     },
33550
33551     // private
33552     constrainXY : function(){
33553         if(this.constraintoviewport !== false){
33554             if(!this.viewSize){
33555                 if(this.container){
33556                     var s = this.container.getSize();
33557                     this.viewSize = [s.width, s.height];
33558                 }else{
33559                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33560                 }
33561             }
33562             var s = Roo.get(this.container||document).getScroll();
33563
33564             var x = this.xy[0], y = this.xy[1];
33565             var w = this.size.width, h = this.size.height;
33566             var vw = this.viewSize[0], vh = this.viewSize[1];
33567             // only move it if it needs it
33568             var moved = false;
33569             // first validate right/bottom
33570             if(x + w > vw+s.left){
33571                 x = vw - w;
33572                 moved = true;
33573             }
33574             if(y + h > vh+s.top){
33575                 y = vh - h;
33576                 moved = true;
33577             }
33578             // then make sure top/left isn't negative
33579             if(x < s.left){
33580                 x = s.left;
33581                 moved = true;
33582             }
33583             if(y < s.top){
33584                 y = s.top;
33585                 moved = true;
33586             }
33587             if(moved){
33588                 // cache xy
33589                 this.xy = [x, y];
33590                 if(this.isVisible()){
33591                     this.el.setLocation(x, y);
33592                     this.adjustAssets();
33593                 }
33594             }
33595         }
33596     },
33597
33598     // private
33599     onDrag : function(){
33600         if(!this.proxyDrag){
33601             this.xy = this.el.getXY();
33602             this.adjustAssets();
33603         }
33604     },
33605
33606     // private
33607     adjustAssets : function(doShow){
33608         var x = this.xy[0], y = this.xy[1];
33609         var w = this.size.width, h = this.size.height;
33610         if(doShow === true){
33611             if(this.shadow){
33612                 this.shadow.show(this.el);
33613             }
33614             if(this.shim){
33615                 this.shim.show();
33616             }
33617         }
33618         if(this.shadow && this.shadow.isVisible()){
33619             this.shadow.show(this.el);
33620         }
33621         if(this.shim && this.shim.isVisible()){
33622             this.shim.setBounds(x, y, w, h);
33623         }
33624     },
33625
33626     // private
33627     adjustViewport : function(w, h){
33628         if(!w || !h){
33629             w = Roo.lib.Dom.getViewWidth();
33630             h = Roo.lib.Dom.getViewHeight();
33631         }
33632         // cache the size
33633         this.viewSize = [w, h];
33634         if(this.modal && this.mask.isVisible()){
33635             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33636             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33637         }
33638         if(this.isVisible()){
33639             this.constrainXY();
33640         }
33641     },
33642
33643     /**
33644      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33645      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33646      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33647      */
33648     destroy : function(removeEl){
33649         if(this.isVisible()){
33650             this.animateTarget = null;
33651             this.hide();
33652         }
33653         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33654         if(this.tabs){
33655             this.tabs.destroy(removeEl);
33656         }
33657         Roo.destroy(
33658              this.shim,
33659              this.proxy,
33660              this.resizer,
33661              this.close,
33662              this.mask
33663         );
33664         if(this.dd){
33665             this.dd.unreg();
33666         }
33667         if(this.buttons){
33668            for(var i = 0, len = this.buttons.length; i < len; i++){
33669                this.buttons[i].destroy();
33670            }
33671         }
33672         this.el.removeAllListeners();
33673         if(removeEl === true){
33674             this.el.update("");
33675             this.el.remove();
33676         }
33677         Roo.DialogManager.unregister(this);
33678     },
33679
33680     // private
33681     startMove : function(){
33682         if(this.proxyDrag){
33683             this.proxy.show();
33684         }
33685         if(this.constraintoviewport !== false){
33686             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33687         }
33688     },
33689
33690     // private
33691     endMove : function(){
33692         if(!this.proxyDrag){
33693             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33694         }else{
33695             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33696             this.proxy.hide();
33697         }
33698         this.refreshSize();
33699         this.adjustAssets();
33700         this.focus();
33701         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33702     },
33703
33704     /**
33705      * Brings this dialog to the front of any other visible dialogs
33706      * @return {Roo.BasicDialog} this
33707      */
33708     toFront : function(){
33709         Roo.DialogManager.bringToFront(this);
33710         return this;
33711     },
33712
33713     /**
33714      * Sends this dialog to the back (under) of any other visible dialogs
33715      * @return {Roo.BasicDialog} this
33716      */
33717     toBack : function(){
33718         Roo.DialogManager.sendToBack(this);
33719         return this;
33720     },
33721
33722     /**
33723      * Centers this dialog in the viewport
33724      * @return {Roo.BasicDialog} this
33725      */
33726     center : function(){
33727         var xy = this.el.getCenterXY(true);
33728         this.moveTo(xy[0], xy[1]);
33729         return this;
33730     },
33731
33732     /**
33733      * Moves the dialog's top-left corner to the specified point
33734      * @param {Number} x
33735      * @param {Number} y
33736      * @return {Roo.BasicDialog} this
33737      */
33738     moveTo : function(x, y){
33739         this.xy = [x,y];
33740         if(this.isVisible()){
33741             this.el.setXY(this.xy);
33742             this.adjustAssets();
33743         }
33744         return this;
33745     },
33746
33747     /**
33748      * Aligns the dialog to the specified element
33749      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33750      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33751      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33752      * @return {Roo.BasicDialog} this
33753      */
33754     alignTo : function(element, position, offsets){
33755         this.xy = this.el.getAlignToXY(element, position, offsets);
33756         if(this.isVisible()){
33757             this.el.setXY(this.xy);
33758             this.adjustAssets();
33759         }
33760         return this;
33761     },
33762
33763     /**
33764      * Anchors an element to another element and realigns it when the window is resized.
33765      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33766      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33767      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33768      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33769      * is a number, it is used as the buffer delay (defaults to 50ms).
33770      * @return {Roo.BasicDialog} this
33771      */
33772     anchorTo : function(el, alignment, offsets, monitorScroll){
33773         var action = function(){
33774             this.alignTo(el, alignment, offsets);
33775         };
33776         Roo.EventManager.onWindowResize(action, this);
33777         var tm = typeof monitorScroll;
33778         if(tm != 'undefined'){
33779             Roo.EventManager.on(window, 'scroll', action, this,
33780                 {buffer: tm == 'number' ? monitorScroll : 50});
33781         }
33782         action.call(this);
33783         return this;
33784     },
33785
33786     /**
33787      * Returns true if the dialog is visible
33788      * @return {Boolean}
33789      */
33790     isVisible : function(){
33791         return this.el.isVisible();
33792     },
33793
33794     // private
33795     animHide : function(callback){
33796         var b = Roo.get(this.animateTarget).getBox();
33797         this.proxy.show();
33798         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33799         this.el.hide();
33800         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33801                     this.hideEl.createDelegate(this, [callback]));
33802     },
33803
33804     /**
33805      * Hides the dialog.
33806      * @param {Function} callback (optional) Function to call when the dialog is hidden
33807      * @return {Roo.BasicDialog} this
33808      */
33809     hide : function(callback){
33810         if (this.fireEvent("beforehide", this) === false){
33811             return;
33812         }
33813         if(this.shadow){
33814             this.shadow.hide();
33815         }
33816         if(this.shim) {
33817           this.shim.hide();
33818         }
33819         // sometimes animateTarget seems to get set.. causing problems...
33820         // this just double checks..
33821         if(this.animateTarget && Roo.get(this.animateTarget)) {
33822            this.animHide(callback);
33823         }else{
33824             this.el.hide();
33825             this.hideEl(callback);
33826         }
33827         return this;
33828     },
33829
33830     // private
33831     hideEl : function(callback){
33832         this.proxy.hide();
33833         if(this.modal){
33834             this.mask.hide();
33835             Roo.get(document.body).removeClass("x-body-masked");
33836         }
33837         this.fireEvent("hide", this);
33838         if(typeof callback == "function"){
33839             callback();
33840         }
33841     },
33842
33843     // private
33844     hideAction : function(){
33845         this.setLeft("-10000px");
33846         this.setTop("-10000px");
33847         this.setStyle("visibility", "hidden");
33848     },
33849
33850     // private
33851     refreshSize : function(){
33852         this.size = this.el.getSize();
33853         this.xy = this.el.getXY();
33854         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33855     },
33856
33857     // private
33858     // z-index is managed by the DialogManager and may be overwritten at any time
33859     setZIndex : function(index){
33860         if(this.modal){
33861             this.mask.setStyle("z-index", index);
33862         }
33863         if(this.shim){
33864             this.shim.setStyle("z-index", ++index);
33865         }
33866         if(this.shadow){
33867             this.shadow.setZIndex(++index);
33868         }
33869         this.el.setStyle("z-index", ++index);
33870         if(this.proxy){
33871             this.proxy.setStyle("z-index", ++index);
33872         }
33873         if(this.resizer){
33874             this.resizer.proxy.setStyle("z-index", ++index);
33875         }
33876
33877         this.lastZIndex = index;
33878     },
33879
33880     /**
33881      * Returns the element for this dialog
33882      * @return {Roo.Element} The underlying dialog Element
33883      */
33884     getEl : function(){
33885         return this.el;
33886     }
33887 });
33888
33889 /**
33890  * @class Roo.DialogManager
33891  * Provides global access to BasicDialogs that have been created and
33892  * support for z-indexing (layering) multiple open dialogs.
33893  */
33894 Roo.DialogManager = function(){
33895     var list = {};
33896     var accessList = [];
33897     var front = null;
33898
33899     // private
33900     var sortDialogs = function(d1, d2){
33901         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33902     };
33903
33904     // private
33905     var orderDialogs = function(){
33906         accessList.sort(sortDialogs);
33907         var seed = Roo.DialogManager.zseed;
33908         for(var i = 0, len = accessList.length; i < len; i++){
33909             var dlg = accessList[i];
33910             if(dlg){
33911                 dlg.setZIndex(seed + (i*10));
33912             }
33913         }
33914     };
33915
33916     return {
33917         /**
33918          * The starting z-index for BasicDialogs (defaults to 9000)
33919          * @type Number The z-index value
33920          */
33921         zseed : 9000,
33922
33923         // private
33924         register : function(dlg){
33925             list[dlg.id] = dlg;
33926             accessList.push(dlg);
33927         },
33928
33929         // private
33930         unregister : function(dlg){
33931             delete list[dlg.id];
33932             var i=0;
33933             var len=0;
33934             if(!accessList.indexOf){
33935                 for(  i = 0, len = accessList.length; i < len; i++){
33936                     if(accessList[i] == dlg){
33937                         accessList.splice(i, 1);
33938                         return;
33939                     }
33940                 }
33941             }else{
33942                  i = accessList.indexOf(dlg);
33943                 if(i != -1){
33944                     accessList.splice(i, 1);
33945                 }
33946             }
33947         },
33948
33949         /**
33950          * Gets a registered dialog by id
33951          * @param {String/Object} id The id of the dialog or a dialog
33952          * @return {Roo.BasicDialog} this
33953          */
33954         get : function(id){
33955             return typeof id == "object" ? id : list[id];
33956         },
33957
33958         /**
33959          * Brings the specified dialog to the front
33960          * @param {String/Object} dlg The id of the dialog or a dialog
33961          * @return {Roo.BasicDialog} this
33962          */
33963         bringToFront : function(dlg){
33964             dlg = this.get(dlg);
33965             if(dlg != front){
33966                 front = dlg;
33967                 dlg._lastAccess = new Date().getTime();
33968                 orderDialogs();
33969             }
33970             return dlg;
33971         },
33972
33973         /**
33974          * Sends the specified dialog to the back
33975          * @param {String/Object} dlg The id of the dialog or a dialog
33976          * @return {Roo.BasicDialog} this
33977          */
33978         sendToBack : function(dlg){
33979             dlg = this.get(dlg);
33980             dlg._lastAccess = -(new Date().getTime());
33981             orderDialogs();
33982             return dlg;
33983         },
33984
33985         /**
33986          * Hides all dialogs
33987          */
33988         hideAll : function(){
33989             for(var id in list){
33990                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
33991                     list[id].hide();
33992                 }
33993             }
33994         }
33995     };
33996 }();
33997
33998 /**
33999  * @class Roo.LayoutDialog
34000  * @extends Roo.BasicDialog
34001  * @children Roo.ContentPanel
34002  * @parent builder none
34003  * Dialog which provides adjustments for working with a layout in a Dialog.
34004  * Add your necessary layout config options to the dialog's config.<br>
34005  * Example usage (including a nested layout):
34006  * <pre><code>
34007 if(!dialog){
34008     dialog = new Roo.LayoutDialog("download-dlg", {
34009         modal: true,
34010         width:600,
34011         height:450,
34012         shadow:true,
34013         minWidth:500,
34014         minHeight:350,
34015         autoTabs:true,
34016         proxyDrag:true,
34017         // layout config merges with the dialog config
34018         center:{
34019             tabPosition: "top",
34020             alwaysShowTabs: true
34021         }
34022     });
34023     dialog.addKeyListener(27, dialog.hide, dialog);
34024     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34025     dialog.addButton("Build It!", this.getDownload, this);
34026
34027     // we can even add nested layouts
34028     var innerLayout = new Roo.BorderLayout("dl-inner", {
34029         east: {
34030             initialSize: 200,
34031             autoScroll:true,
34032             split:true
34033         },
34034         center: {
34035             autoScroll:true
34036         }
34037     });
34038     innerLayout.beginUpdate();
34039     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34040     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34041     innerLayout.endUpdate(true);
34042
34043     var layout = dialog.getLayout();
34044     layout.beginUpdate();
34045     layout.add("center", new Roo.ContentPanel("standard-panel",
34046                         {title: "Download the Source", fitToFrame:true}));
34047     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34048                {title: "Build your own roo.js"}));
34049     layout.getRegion("center").showPanel(sp);
34050     layout.endUpdate();
34051 }
34052 </code></pre>
34053     * @constructor
34054     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34055     * @param {Object} config configuration options
34056   */
34057 Roo.LayoutDialog = function(el, cfg){
34058     
34059     var config=  cfg;
34060     if (typeof(cfg) == 'undefined') {
34061         config = Roo.apply({}, el);
34062         // not sure why we use documentElement here.. - it should always be body.
34063         // IE7 borks horribly if we use documentElement.
34064         // webkit also does not like documentElement - it creates a body element...
34065         el = Roo.get( document.body || document.documentElement ).createChild();
34066         //config.autoCreate = true;
34067     }
34068     
34069     
34070     config.autoTabs = false;
34071     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34072     this.body.setStyle({overflow:"hidden", position:"relative"});
34073     this.layout = new Roo.BorderLayout(this.body.dom, config);
34074     this.layout.monitorWindowResize = false;
34075     this.el.addClass("x-dlg-auto-layout");
34076     // fix case when center region overwrites center function
34077     this.center = Roo.BasicDialog.prototype.center;
34078     this.on("show", this.layout.layout, this.layout, true);
34079     if (config.items) {
34080         var xitems = config.items;
34081         delete config.items;
34082         Roo.each(xitems, this.addxtype, this);
34083     }
34084     
34085     
34086 };
34087 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34088     
34089     
34090     /**
34091      * @cfg {Roo.LayoutRegion} east  
34092      */
34093     /**
34094      * @cfg {Roo.LayoutRegion} west
34095      */
34096     /**
34097      * @cfg {Roo.LayoutRegion} south
34098      */
34099     /**
34100      * @cfg {Roo.LayoutRegion} north
34101      */
34102     /**
34103      * @cfg {Roo.LayoutRegion} center
34104      */
34105     /**
34106      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34107      */
34108     
34109     
34110     /**
34111      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34112      * @deprecated
34113      */
34114     endUpdate : function(){
34115         this.layout.endUpdate();
34116     },
34117
34118     /**
34119      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34120      *  @deprecated
34121      */
34122     beginUpdate : function(){
34123         this.layout.beginUpdate();
34124     },
34125
34126     /**
34127      * Get the BorderLayout for this dialog
34128      * @return {Roo.BorderLayout}
34129      */
34130     getLayout : function(){
34131         return this.layout;
34132     },
34133
34134     showEl : function(){
34135         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34136         if(Roo.isIE7){
34137             this.layout.layout();
34138         }
34139     },
34140
34141     // private
34142     // Use the syncHeightBeforeShow config option to control this automatically
34143     syncBodyHeight : function(){
34144         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34145         if(this.layout){this.layout.layout();}
34146     },
34147     
34148       /**
34149      * Add an xtype element (actually adds to the layout.)
34150      * @return {Object} xdata xtype object data.
34151      */
34152     
34153     addxtype : function(c) {
34154         return this.layout.addxtype(c);
34155     }
34156 });/*
34157  * Based on:
34158  * Ext JS Library 1.1.1
34159  * Copyright(c) 2006-2007, Ext JS, LLC.
34160  *
34161  * Originally Released Under LGPL - original licence link has changed is not relivant.
34162  *
34163  * Fork - LGPL
34164  * <script type="text/javascript">
34165  */
34166  
34167 /**
34168  * @class Roo.MessageBox
34169  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34170  * Example usage:
34171  *<pre><code>
34172 // Basic alert:
34173 Roo.Msg.alert('Status', 'Changes saved successfully.');
34174
34175 // Prompt for user data:
34176 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34177     if (btn == 'ok'){
34178         // process text value...
34179     }
34180 });
34181
34182 // Show a dialog using config options:
34183 Roo.Msg.show({
34184    title:'Save Changes?',
34185    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34186    buttons: Roo.Msg.YESNOCANCEL,
34187    fn: processResult,
34188    animEl: 'elId'
34189 });
34190 </code></pre>
34191  * @static
34192  */
34193 Roo.MessageBox = function(){
34194     var dlg, opt, mask, waitTimer;
34195     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34196     var buttons, activeTextEl, bwidth;
34197
34198     // private
34199     var handleButton = function(button){
34200         dlg.hide();
34201         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34202     };
34203
34204     // private
34205     var handleHide = function(){
34206         if(opt && opt.cls){
34207             dlg.el.removeClass(opt.cls);
34208         }
34209         if(waitTimer){
34210             Roo.TaskMgr.stop(waitTimer);
34211             waitTimer = null;
34212         }
34213     };
34214
34215     // private
34216     var updateButtons = function(b){
34217         var width = 0;
34218         if(!b){
34219             buttons["ok"].hide();
34220             buttons["cancel"].hide();
34221             buttons["yes"].hide();
34222             buttons["no"].hide();
34223             dlg.footer.dom.style.display = 'none';
34224             return width;
34225         }
34226         dlg.footer.dom.style.display = '';
34227         for(var k in buttons){
34228             if(typeof buttons[k] != "function"){
34229                 if(b[k]){
34230                     buttons[k].show();
34231                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34232                     width += buttons[k].el.getWidth()+15;
34233                 }else{
34234                     buttons[k].hide();
34235                 }
34236             }
34237         }
34238         return width;
34239     };
34240
34241     // private
34242     var handleEsc = function(d, k, e){
34243         if(opt && opt.closable !== false){
34244             dlg.hide();
34245         }
34246         if(e){
34247             e.stopEvent();
34248         }
34249     };
34250
34251     return {
34252         /**
34253          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34254          * @return {Roo.BasicDialog} The BasicDialog element
34255          */
34256         getDialog : function(){
34257            if(!dlg){
34258                 dlg = new Roo.BasicDialog("x-msg-box", {
34259                     autoCreate : true,
34260                     shadow: true,
34261                     draggable: true,
34262                     resizable:false,
34263                     constraintoviewport:false,
34264                     fixedcenter:true,
34265                     collapsible : false,
34266                     shim:true,
34267                     modal: true,
34268                     width:400, height:100,
34269                     buttonAlign:"center",
34270                     closeClick : function(){
34271                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34272                             handleButton("no");
34273                         }else{
34274                             handleButton("cancel");
34275                         }
34276                     }
34277                 });
34278                 dlg.on("hide", handleHide);
34279                 mask = dlg.mask;
34280                 dlg.addKeyListener(27, handleEsc);
34281                 buttons = {};
34282                 var bt = this.buttonText;
34283                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34284                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34285                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34286                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34287                 bodyEl = dlg.body.createChild({
34288
34289                     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>'
34290                 });
34291                 msgEl = bodyEl.dom.firstChild;
34292                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34293                 textboxEl.enableDisplayMode();
34294                 textboxEl.addKeyListener([10,13], function(){
34295                     if(dlg.isVisible() && opt && opt.buttons){
34296                         if(opt.buttons.ok){
34297                             handleButton("ok");
34298                         }else if(opt.buttons.yes){
34299                             handleButton("yes");
34300                         }
34301                     }
34302                 });
34303                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34304                 textareaEl.enableDisplayMode();
34305                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34306                 progressEl.enableDisplayMode();
34307                 var pf = progressEl.dom.firstChild;
34308                 if (pf) {
34309                     pp = Roo.get(pf.firstChild);
34310                     pp.setHeight(pf.offsetHeight);
34311                 }
34312                 
34313             }
34314             return dlg;
34315         },
34316
34317         /**
34318          * Updates the message box body text
34319          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34320          * the XHTML-compliant non-breaking space character '&amp;#160;')
34321          * @return {Roo.MessageBox} This message box
34322          */
34323         updateText : function(text){
34324             if(!dlg.isVisible() && !opt.width){
34325                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34326             }
34327             msgEl.innerHTML = text || '&#160;';
34328       
34329             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34330             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34331             var w = Math.max(
34332                     Math.min(opt.width || cw , this.maxWidth), 
34333                     Math.max(opt.minWidth || this.minWidth, bwidth)
34334             );
34335             if(opt.prompt){
34336                 activeTextEl.setWidth(w);
34337             }
34338             if(dlg.isVisible()){
34339                 dlg.fixedcenter = false;
34340             }
34341             // to big, make it scroll. = But as usual stupid IE does not support
34342             // !important..
34343             
34344             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34345                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34346                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34347             } else {
34348                 bodyEl.dom.style.height = '';
34349                 bodyEl.dom.style.overflowY = '';
34350             }
34351             if (cw > w) {
34352                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34353             } else {
34354                 bodyEl.dom.style.overflowX = '';
34355             }
34356             
34357             dlg.setContentSize(w, bodyEl.getHeight());
34358             if(dlg.isVisible()){
34359                 dlg.fixedcenter = true;
34360             }
34361             return this;
34362         },
34363
34364         /**
34365          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34366          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34367          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34368          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34369          * @return {Roo.MessageBox} This message box
34370          */
34371         updateProgress : function(value, text){
34372             if(text){
34373                 this.updateText(text);
34374             }
34375             if (pp) { // weird bug on my firefox - for some reason this is not defined
34376                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34377             }
34378             return this;
34379         },        
34380
34381         /**
34382          * Returns true if the message box is currently displayed
34383          * @return {Boolean} True if the message box is visible, else false
34384          */
34385         isVisible : function(){
34386             return dlg && dlg.isVisible();  
34387         },
34388
34389         /**
34390          * Hides the message box if it is displayed
34391          */
34392         hide : function(){
34393             if(this.isVisible()){
34394                 dlg.hide();
34395             }  
34396         },
34397
34398         /**
34399          * Displays a new message box, or reinitializes an existing message box, based on the config options
34400          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34401          * The following config object properties are supported:
34402          * <pre>
34403 Property    Type             Description
34404 ----------  ---------------  ------------------------------------------------------------------------------------
34405 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34406                                    closes (defaults to undefined)
34407 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34408                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34409 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34410                                    progress and wait dialogs will ignore this property and always hide the
34411                                    close button as they can only be closed programmatically.
34412 cls               String           A custom CSS class to apply to the message box element
34413 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34414                                    displayed (defaults to 75)
34415 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34416                                    function will be btn (the name of the button that was clicked, if applicable,
34417                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34418                                    Progress and wait dialogs will ignore this option since they do not respond to
34419                                    user actions and can only be closed programmatically, so any required function
34420                                    should be called by the same code after it closes the dialog.
34421 icon              String           A CSS class that provides a background image to be used as an icon for
34422                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34423 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34424 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34425 modal             Boolean          False to allow user interaction with the page while the message box is
34426                                    displayed (defaults to true)
34427 msg               String           A string that will replace the existing message box body text (defaults
34428                                    to the XHTML-compliant non-breaking space character '&#160;')
34429 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34430 progress          Boolean          True to display a progress bar (defaults to false)
34431 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34432 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34433 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34434 title             String           The title text
34435 value             String           The string value to set into the active textbox element if displayed
34436 wait              Boolean          True to display a progress bar (defaults to false)
34437 width             Number           The width of the dialog in pixels
34438 </pre>
34439          *
34440          * Example usage:
34441          * <pre><code>
34442 Roo.Msg.show({
34443    title: 'Address',
34444    msg: 'Please enter your address:',
34445    width: 300,
34446    buttons: Roo.MessageBox.OKCANCEL,
34447    multiline: true,
34448    fn: saveAddress,
34449    animEl: 'addAddressBtn'
34450 });
34451 </code></pre>
34452          * @param {Object} config Configuration options
34453          * @return {Roo.MessageBox} This message box
34454          */
34455         show : function(options)
34456         {
34457             
34458             // this causes nightmares if you show one dialog after another
34459             // especially on callbacks..
34460              
34461             if(this.isVisible()){
34462                 
34463                 this.hide();
34464                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34465                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34466                 Roo.log("New Dialog Message:" +  options.msg )
34467                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34468                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34469                 
34470             }
34471             var d = this.getDialog();
34472             opt = options;
34473             d.setTitle(opt.title || "&#160;");
34474             d.close.setDisplayed(opt.closable !== false);
34475             activeTextEl = textboxEl;
34476             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34477             if(opt.prompt){
34478                 if(opt.multiline){
34479                     textboxEl.hide();
34480                     textareaEl.show();
34481                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34482                         opt.multiline : this.defaultTextHeight);
34483                     activeTextEl = textareaEl;
34484                 }else{
34485                     textboxEl.show();
34486                     textareaEl.hide();
34487                 }
34488             }else{
34489                 textboxEl.hide();
34490                 textareaEl.hide();
34491             }
34492             progressEl.setDisplayed(opt.progress === true);
34493             this.updateProgress(0);
34494             activeTextEl.dom.value = opt.value || "";
34495             if(opt.prompt){
34496                 dlg.setDefaultButton(activeTextEl);
34497             }else{
34498                 var bs = opt.buttons;
34499                 var db = null;
34500                 if(bs && bs.ok){
34501                     db = buttons["ok"];
34502                 }else if(bs && bs.yes){
34503                     db = buttons["yes"];
34504                 }
34505                 dlg.setDefaultButton(db);
34506             }
34507             bwidth = updateButtons(opt.buttons);
34508             this.updateText(opt.msg);
34509             if(opt.cls){
34510                 d.el.addClass(opt.cls);
34511             }
34512             d.proxyDrag = opt.proxyDrag === true;
34513             d.modal = opt.modal !== false;
34514             d.mask = opt.modal !== false ? mask : false;
34515             if(!d.isVisible()){
34516                 // force it to the end of the z-index stack so it gets a cursor in FF
34517                 document.body.appendChild(dlg.el.dom);
34518                 d.animateTarget = null;
34519                 d.show(options.animEl);
34520             }
34521             return this;
34522         },
34523
34524         /**
34525          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34526          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34527          * and closing the message box when the process is complete.
34528          * @param {String} title The title bar text
34529          * @param {String} msg The message box body text
34530          * @return {Roo.MessageBox} This message box
34531          */
34532         progress : function(title, msg){
34533             this.show({
34534                 title : title,
34535                 msg : msg,
34536                 buttons: false,
34537                 progress:true,
34538                 closable:false,
34539                 minWidth: this.minProgressWidth,
34540                 modal : true
34541             });
34542             return this;
34543         },
34544
34545         /**
34546          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34547          * If a callback function is passed it will be called after the user clicks the button, and the
34548          * id of the button that was clicked will be passed as the only parameter to the callback
34549          * (could also be the top-right close button).
34550          * @param {String} title The title bar text
34551          * @param {String} msg The message box body text
34552          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34553          * @param {Object} scope (optional) The scope of the callback function
34554          * @return {Roo.MessageBox} This message box
34555          */
34556         alert : function(title, msg, fn, scope){
34557             this.show({
34558                 title : title,
34559                 msg : msg,
34560                 buttons: this.OK,
34561                 fn: fn,
34562                 scope : scope,
34563                 modal : true
34564             });
34565             return this;
34566         },
34567
34568         /**
34569          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34570          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34571          * You are responsible for closing the message box when the process is complete.
34572          * @param {String} msg The message box body text
34573          * @param {String} title (optional) The title bar text
34574          * @return {Roo.MessageBox} This message box
34575          */
34576         wait : function(msg, title){
34577             this.show({
34578                 title : title,
34579                 msg : msg,
34580                 buttons: false,
34581                 closable:false,
34582                 progress:true,
34583                 modal:true,
34584                 width:300,
34585                 wait:true
34586             });
34587             waitTimer = Roo.TaskMgr.start({
34588                 run: function(i){
34589                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34590                 },
34591                 interval: 1000
34592             });
34593             return this;
34594         },
34595
34596         /**
34597          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34598          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34599          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34600          * @param {String} title The title bar text
34601          * @param {String} msg The message box body text
34602          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34603          * @param {Object} scope (optional) The scope of the callback function
34604          * @return {Roo.MessageBox} This message box
34605          */
34606         confirm : function(title, msg, fn, scope){
34607             this.show({
34608                 title : title,
34609                 msg : msg,
34610                 buttons: this.YESNO,
34611                 fn: fn,
34612                 scope : scope,
34613                 modal : true
34614             });
34615             return this;
34616         },
34617
34618         /**
34619          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34620          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34621          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34622          * (could also be the top-right close button) and the text that was entered will be passed as the two
34623          * parameters to the callback.
34624          * @param {String} title The title bar text
34625          * @param {String} msg The message box body text
34626          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34627          * @param {Object} scope (optional) The scope of the callback function
34628          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34629          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34630          * @return {Roo.MessageBox} This message box
34631          */
34632         prompt : function(title, msg, fn, scope, multiline){
34633             this.show({
34634                 title : title,
34635                 msg : msg,
34636                 buttons: this.OKCANCEL,
34637                 fn: fn,
34638                 minWidth:250,
34639                 scope : scope,
34640                 prompt:true,
34641                 multiline: multiline,
34642                 modal : true
34643             });
34644             return this;
34645         },
34646
34647         /**
34648          * Button config that displays a single OK button
34649          * @type Object
34650          */
34651         OK : {ok:true},
34652         /**
34653          * Button config that displays Yes and No buttons
34654          * @type Object
34655          */
34656         YESNO : {yes:true, no:true},
34657         /**
34658          * Button config that displays OK and Cancel buttons
34659          * @type Object
34660          */
34661         OKCANCEL : {ok:true, cancel:true},
34662         /**
34663          * Button config that displays Yes, No and Cancel buttons
34664          * @type Object
34665          */
34666         YESNOCANCEL : {yes:true, no:true, cancel:true},
34667
34668         /**
34669          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34670          * @type Number
34671          */
34672         defaultTextHeight : 75,
34673         /**
34674          * The maximum width in pixels of the message box (defaults to 600)
34675          * @type Number
34676          */
34677         maxWidth : 600,
34678         /**
34679          * The minimum width in pixels of the message box (defaults to 100)
34680          * @type Number
34681          */
34682         minWidth : 100,
34683         /**
34684          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34685          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34686          * @type Number
34687          */
34688         minProgressWidth : 250,
34689         /**
34690          * An object containing the default button text strings that can be overriden for localized language support.
34691          * Supported properties are: ok, cancel, yes and no.
34692          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34693          * @type Object
34694          */
34695         buttonText : {
34696             ok : "OK",
34697             cancel : "Cancel",
34698             yes : "Yes",
34699             no : "No"
34700         }
34701     };
34702 }();
34703
34704 /**
34705  * Shorthand for {@link Roo.MessageBox}
34706  */
34707 Roo.Msg = Roo.MessageBox;/*
34708  * Based on:
34709  * Ext JS Library 1.1.1
34710  * Copyright(c) 2006-2007, Ext JS, LLC.
34711  *
34712  * Originally Released Under LGPL - original licence link has changed is not relivant.
34713  *
34714  * Fork - LGPL
34715  * <script type="text/javascript">
34716  */
34717 /**
34718  * @class Roo.QuickTips
34719  * Provides attractive and customizable tooltips for any element.
34720  * @static
34721  */
34722 Roo.QuickTips = function(){
34723     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34724     var ce, bd, xy, dd;
34725     var visible = false, disabled = true, inited = false;
34726     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34727     
34728     var onOver = function(e){
34729         if(disabled){
34730             return;
34731         }
34732         var t = e.getTarget();
34733         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34734             return;
34735         }
34736         if(ce && t == ce.el){
34737             clearTimeout(hideProc);
34738             return;
34739         }
34740         if(t && tagEls[t.id]){
34741             tagEls[t.id].el = t;
34742             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34743             return;
34744         }
34745         var ttp, et = Roo.fly(t);
34746         var ns = cfg.namespace;
34747         if(tm.interceptTitles && t.title){
34748             ttp = t.title;
34749             t.qtip = ttp;
34750             t.removeAttribute("title");
34751             e.preventDefault();
34752         }else{
34753             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34754         }
34755         if(ttp){
34756             showProc = show.defer(tm.showDelay, tm, [{
34757                 el: t, 
34758                 text: ttp.replace(/\\n/g,'<br/>'),
34759                 width: et.getAttributeNS(ns, cfg.width),
34760                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34761                 title: et.getAttributeNS(ns, cfg.title),
34762                     cls: et.getAttributeNS(ns, cfg.cls)
34763             }]);
34764         }
34765     };
34766     
34767     var onOut = function(e){
34768         clearTimeout(showProc);
34769         var t = e.getTarget();
34770         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34771             hideProc = setTimeout(hide, tm.hideDelay);
34772         }
34773     };
34774     
34775     var onMove = function(e){
34776         if(disabled){
34777             return;
34778         }
34779         xy = e.getXY();
34780         xy[1] += 18;
34781         if(tm.trackMouse && ce){
34782             el.setXY(xy);
34783         }
34784     };
34785     
34786     var onDown = function(e){
34787         clearTimeout(showProc);
34788         clearTimeout(hideProc);
34789         if(!e.within(el)){
34790             if(tm.hideOnClick){
34791                 hide();
34792                 tm.disable();
34793                 tm.enable.defer(100, tm);
34794             }
34795         }
34796     };
34797     
34798     var getPad = function(){
34799         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34800     };
34801
34802     var show = function(o){
34803         if(disabled){
34804             return;
34805         }
34806         clearTimeout(dismissProc);
34807         ce = o;
34808         if(removeCls){ // in case manually hidden
34809             el.removeClass(removeCls);
34810             removeCls = null;
34811         }
34812         if(ce.cls){
34813             el.addClass(ce.cls);
34814             removeCls = ce.cls;
34815         }
34816         if(ce.title){
34817             tipTitle.update(ce.title);
34818             tipTitle.show();
34819         }else{
34820             tipTitle.update('');
34821             tipTitle.hide();
34822         }
34823         el.dom.style.width  = tm.maxWidth+'px';
34824         //tipBody.dom.style.width = '';
34825         tipBodyText.update(o.text);
34826         var p = getPad(), w = ce.width;
34827         if(!w){
34828             var td = tipBodyText.dom;
34829             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34830             if(aw > tm.maxWidth){
34831                 w = tm.maxWidth;
34832             }else if(aw < tm.minWidth){
34833                 w = tm.minWidth;
34834             }else{
34835                 w = aw;
34836             }
34837         }
34838         //tipBody.setWidth(w);
34839         el.setWidth(parseInt(w, 10) + p);
34840         if(ce.autoHide === false){
34841             close.setDisplayed(true);
34842             if(dd){
34843                 dd.unlock();
34844             }
34845         }else{
34846             close.setDisplayed(false);
34847             if(dd){
34848                 dd.lock();
34849             }
34850         }
34851         if(xy){
34852             el.avoidY = xy[1]-18;
34853             el.setXY(xy);
34854         }
34855         if(tm.animate){
34856             el.setOpacity(.1);
34857             el.setStyle("visibility", "visible");
34858             el.fadeIn({callback: afterShow});
34859         }else{
34860             afterShow();
34861         }
34862     };
34863     
34864     var afterShow = function(){
34865         if(ce){
34866             el.show();
34867             esc.enable();
34868             if(tm.autoDismiss && ce.autoHide !== false){
34869                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34870             }
34871         }
34872     };
34873     
34874     var hide = function(noanim){
34875         clearTimeout(dismissProc);
34876         clearTimeout(hideProc);
34877         ce = null;
34878         if(el.isVisible()){
34879             esc.disable();
34880             if(noanim !== true && tm.animate){
34881                 el.fadeOut({callback: afterHide});
34882             }else{
34883                 afterHide();
34884             } 
34885         }
34886     };
34887     
34888     var afterHide = function(){
34889         el.hide();
34890         if(removeCls){
34891             el.removeClass(removeCls);
34892             removeCls = null;
34893         }
34894     };
34895     
34896     return {
34897         /**
34898         * @cfg {Number} minWidth
34899         * The minimum width of the quick tip (defaults to 40)
34900         */
34901        minWidth : 40,
34902         /**
34903         * @cfg {Number} maxWidth
34904         * The maximum width of the quick tip (defaults to 300)
34905         */
34906        maxWidth : 300,
34907         /**
34908         * @cfg {Boolean} interceptTitles
34909         * True to automatically use the element's DOM title value if available (defaults to false)
34910         */
34911        interceptTitles : false,
34912         /**
34913         * @cfg {Boolean} trackMouse
34914         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34915         */
34916        trackMouse : false,
34917         /**
34918         * @cfg {Boolean} hideOnClick
34919         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34920         */
34921        hideOnClick : true,
34922         /**
34923         * @cfg {Number} showDelay
34924         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
34925         */
34926        showDelay : 500,
34927         /**
34928         * @cfg {Number} hideDelay
34929         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
34930         */
34931        hideDelay : 200,
34932         /**
34933         * @cfg {Boolean} autoHide
34934         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
34935         * Used in conjunction with hideDelay.
34936         */
34937        autoHide : true,
34938         /**
34939         * @cfg {Boolean}
34940         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
34941         * (defaults to true).  Used in conjunction with autoDismissDelay.
34942         */
34943        autoDismiss : true,
34944         /**
34945         * @cfg {Number}
34946         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
34947         */
34948        autoDismissDelay : 5000,
34949        /**
34950         * @cfg {Boolean} animate
34951         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
34952         */
34953        animate : false,
34954
34955        /**
34956         * @cfg {String} title
34957         * Title text to display (defaults to '').  This can be any valid HTML markup.
34958         */
34959         title: '',
34960        /**
34961         * @cfg {String} text
34962         * Body text to display (defaults to '').  This can be any valid HTML markup.
34963         */
34964         text : '',
34965        /**
34966         * @cfg {String} cls
34967         * A CSS class to apply to the base quick tip element (defaults to '').
34968         */
34969         cls : '',
34970        /**
34971         * @cfg {Number} width
34972         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
34973         * minWidth or maxWidth.
34974         */
34975         width : null,
34976
34977     /**
34978      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
34979      * or display QuickTips in a page.
34980      */
34981        init : function(){
34982           tm = Roo.QuickTips;
34983           cfg = tm.tagConfig;
34984           if(!inited){
34985               if(!Roo.isReady){ // allow calling of init() before onReady
34986                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
34987                   return;
34988               }
34989               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
34990               el.fxDefaults = {stopFx: true};
34991               // maximum custom styling
34992               //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>');
34993               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>');              
34994               tipTitle = el.child('h3');
34995               tipTitle.enableDisplayMode("block");
34996               tipBody = el.child('div.x-tip-bd');
34997               tipBodyText = el.child('div.x-tip-bd-inner');
34998               //bdLeft = el.child('div.x-tip-bd-left');
34999               //bdRight = el.child('div.x-tip-bd-right');
35000               close = el.child('div.x-tip-close');
35001               close.enableDisplayMode("block");
35002               close.on("click", hide);
35003               var d = Roo.get(document);
35004               d.on("mousedown", onDown);
35005               d.on("mouseover", onOver);
35006               d.on("mouseout", onOut);
35007               d.on("mousemove", onMove);
35008               esc = d.addKeyListener(27, hide);
35009               esc.disable();
35010               if(Roo.dd.DD){
35011                   dd = el.initDD("default", null, {
35012                       onDrag : function(){
35013                           el.sync();  
35014                       }
35015                   });
35016                   dd.setHandleElId(tipTitle.id);
35017                   dd.lock();
35018               }
35019               inited = true;
35020           }
35021           this.enable(); 
35022        },
35023
35024     /**
35025      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35026      * are supported:
35027      * <pre>
35028 Property    Type                   Description
35029 ----------  ---------------------  ------------------------------------------------------------------------
35030 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35031      * </ul>
35032      * @param {Object} config The config object
35033      */
35034        register : function(config){
35035            var cs = config instanceof Array ? config : arguments;
35036            for(var i = 0, len = cs.length; i < len; i++) {
35037                var c = cs[i];
35038                var target = c.target;
35039                if(target){
35040                    if(target instanceof Array){
35041                        for(var j = 0, jlen = target.length; j < jlen; j++){
35042                            tagEls[target[j]] = c;
35043                        }
35044                    }else{
35045                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35046                    }
35047                }
35048            }
35049        },
35050
35051     /**
35052      * Removes this quick tip from its element and destroys it.
35053      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35054      */
35055        unregister : function(el){
35056            delete tagEls[Roo.id(el)];
35057        },
35058
35059     /**
35060      * Enable this quick tip.
35061      */
35062        enable : function(){
35063            if(inited && disabled){
35064                locks.pop();
35065                if(locks.length < 1){
35066                    disabled = false;
35067                }
35068            }
35069        },
35070
35071     /**
35072      * Disable this quick tip.
35073      */
35074        disable : function(){
35075           disabled = true;
35076           clearTimeout(showProc);
35077           clearTimeout(hideProc);
35078           clearTimeout(dismissProc);
35079           if(ce){
35080               hide(true);
35081           }
35082           locks.push(1);
35083        },
35084
35085     /**
35086      * Returns true if the quick tip is enabled, else false.
35087      */
35088        isEnabled : function(){
35089             return !disabled;
35090        },
35091
35092         // private
35093        tagConfig : {
35094            namespace : "roo", // was ext?? this may break..
35095            alt_namespace : "ext",
35096            attribute : "qtip",
35097            width : "width",
35098            target : "target",
35099            title : "qtitle",
35100            hide : "hide",
35101            cls : "qclass"
35102        }
35103    };
35104 }();
35105
35106 // backwards compat
35107 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35108  * Based on:
35109  * Ext JS Library 1.1.1
35110  * Copyright(c) 2006-2007, Ext JS, LLC.
35111  *
35112  * Originally Released Under LGPL - original licence link has changed is not relivant.
35113  *
35114  * Fork - LGPL
35115  * <script type="text/javascript">
35116  */
35117  
35118
35119 /**
35120  * @class Roo.tree.TreePanel
35121  * @extends Roo.data.Tree
35122  * @cfg {Roo.tree.TreeNode} root The root node
35123  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35124  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35125  * @cfg {Boolean} enableDD true to enable drag and drop
35126  * @cfg {Boolean} enableDrag true to enable just drag
35127  * @cfg {Boolean} enableDrop true to enable just drop
35128  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35129  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35130  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35131  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35132  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35133  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35134  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35135  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35136  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35137  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35138  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35139  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35140  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35141  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35142  * @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>
35143  * @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>
35144  * 
35145  * @constructor
35146  * @param {String/HTMLElement/Element} el The container element
35147  * @param {Object} config
35148  */
35149 Roo.tree.TreePanel = function(el, config){
35150     var root = false;
35151     var loader = false;
35152     if (config.root) {
35153         root = config.root;
35154         delete config.root;
35155     }
35156     if (config.loader) {
35157         loader = config.loader;
35158         delete config.loader;
35159     }
35160     
35161     Roo.apply(this, config);
35162     Roo.tree.TreePanel.superclass.constructor.call(this);
35163     this.el = Roo.get(el);
35164     this.el.addClass('x-tree');
35165     //console.log(root);
35166     if (root) {
35167         this.setRootNode( Roo.factory(root, Roo.tree));
35168     }
35169     if (loader) {
35170         this.loader = Roo.factory(loader, Roo.tree);
35171     }
35172    /**
35173     * Read-only. The id of the container element becomes this TreePanel's id.
35174     */
35175     this.id = this.el.id;
35176     this.addEvents({
35177         /**
35178         * @event beforeload
35179         * Fires before a node is loaded, return false to cancel
35180         * @param {Node} node The node being loaded
35181         */
35182         "beforeload" : true,
35183         /**
35184         * @event load
35185         * Fires when a node is loaded
35186         * @param {Node} node The node that was loaded
35187         */
35188         "load" : true,
35189         /**
35190         * @event textchange
35191         * Fires when the text for a node is changed
35192         * @param {Node} node The node
35193         * @param {String} text The new text
35194         * @param {String} oldText The old text
35195         */
35196         "textchange" : true,
35197         /**
35198         * @event beforeexpand
35199         * Fires before a node is expanded, return false to cancel.
35200         * @param {Node} node The node
35201         * @param {Boolean} deep
35202         * @param {Boolean} anim
35203         */
35204         "beforeexpand" : true,
35205         /**
35206         * @event beforecollapse
35207         * Fires before a node is collapsed, return false to cancel.
35208         * @param {Node} node The node
35209         * @param {Boolean} deep
35210         * @param {Boolean} anim
35211         */
35212         "beforecollapse" : true,
35213         /**
35214         * @event expand
35215         * Fires when a node is expanded
35216         * @param {Node} node The node
35217         */
35218         "expand" : true,
35219         /**
35220         * @event disabledchange
35221         * Fires when the disabled status of a node changes
35222         * @param {Node} node The node
35223         * @param {Boolean} disabled
35224         */
35225         "disabledchange" : true,
35226         /**
35227         * @event collapse
35228         * Fires when a node is collapsed
35229         * @param {Node} node The node
35230         */
35231         "collapse" : true,
35232         /**
35233         * @event beforeclick
35234         * Fires before click processing on a node. Return false to cancel the default action.
35235         * @param {Node} node The node
35236         * @param {Roo.EventObject} e The event object
35237         */
35238         "beforeclick":true,
35239         /**
35240         * @event checkchange
35241         * Fires when a node with a checkbox's checked property changes
35242         * @param {Node} this This node
35243         * @param {Boolean} checked
35244         */
35245         "checkchange":true,
35246         /**
35247         * @event click
35248         * Fires when a node is clicked
35249         * @param {Node} node The node
35250         * @param {Roo.EventObject} e The event object
35251         */
35252         "click":true,
35253         /**
35254         * @event dblclick
35255         * Fires when a node is double clicked
35256         * @param {Node} node The node
35257         * @param {Roo.EventObject} e The event object
35258         */
35259         "dblclick":true,
35260         /**
35261         * @event contextmenu
35262         * Fires when a node is right clicked
35263         * @param {Node} node The node
35264         * @param {Roo.EventObject} e The event object
35265         */
35266         "contextmenu":true,
35267         /**
35268         * @event beforechildrenrendered
35269         * Fires right before the child nodes for a node are rendered
35270         * @param {Node} node The node
35271         */
35272         "beforechildrenrendered":true,
35273         /**
35274         * @event startdrag
35275         * Fires when a node starts being dragged
35276         * @param {Roo.tree.TreePanel} this
35277         * @param {Roo.tree.TreeNode} node
35278         * @param {event} e The raw browser event
35279         */ 
35280        "startdrag" : true,
35281        /**
35282         * @event enddrag
35283         * Fires when a drag operation is complete
35284         * @param {Roo.tree.TreePanel} this
35285         * @param {Roo.tree.TreeNode} node
35286         * @param {event} e The raw browser event
35287         */
35288        "enddrag" : true,
35289        /**
35290         * @event dragdrop
35291         * Fires when a dragged node is dropped on a valid DD target
35292         * @param {Roo.tree.TreePanel} this
35293         * @param {Roo.tree.TreeNode} node
35294         * @param {DD} dd The dd it was dropped on
35295         * @param {event} e The raw browser event
35296         */
35297        "dragdrop" : true,
35298        /**
35299         * @event beforenodedrop
35300         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35301         * passed to handlers has the following properties:<br />
35302         * <ul style="padding:5px;padding-left:16px;">
35303         * <li>tree - The TreePanel</li>
35304         * <li>target - The node being targeted for the drop</li>
35305         * <li>data - The drag data from the drag source</li>
35306         * <li>point - The point of the drop - append, above or below</li>
35307         * <li>source - The drag source</li>
35308         * <li>rawEvent - Raw mouse event</li>
35309         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35310         * to be inserted by setting them on this object.</li>
35311         * <li>cancel - Set this to true to cancel the drop.</li>
35312         * </ul>
35313         * @param {Object} dropEvent
35314         */
35315        "beforenodedrop" : true,
35316        /**
35317         * @event nodedrop
35318         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35319         * passed to handlers has the following properties:<br />
35320         * <ul style="padding:5px;padding-left:16px;">
35321         * <li>tree - The TreePanel</li>
35322         * <li>target - The node being targeted for the drop</li>
35323         * <li>data - The drag data from the drag source</li>
35324         * <li>point - The point of the drop - append, above or below</li>
35325         * <li>source - The drag source</li>
35326         * <li>rawEvent - Raw mouse event</li>
35327         * <li>dropNode - Dropped node(s).</li>
35328         * </ul>
35329         * @param {Object} dropEvent
35330         */
35331        "nodedrop" : true,
35332         /**
35333         * @event nodedragover
35334         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35335         * passed to handlers has the following properties:<br />
35336         * <ul style="padding:5px;padding-left:16px;">
35337         * <li>tree - The TreePanel</li>
35338         * <li>target - The node being targeted for the drop</li>
35339         * <li>data - The drag data from the drag source</li>
35340         * <li>point - The point of the drop - append, above or below</li>
35341         * <li>source - The drag source</li>
35342         * <li>rawEvent - Raw mouse event</li>
35343         * <li>dropNode - Drop node(s) provided by the source.</li>
35344         * <li>cancel - Set this to true to signal drop not allowed.</li>
35345         * </ul>
35346         * @param {Object} dragOverEvent
35347         */
35348        "nodedragover" : true,
35349        /**
35350         * @event appendnode
35351         * Fires when append node to the tree
35352         * @param {Roo.tree.TreePanel} this
35353         * @param {Roo.tree.TreeNode} node
35354         * @param {Number} index The index of the newly appended node
35355         */
35356        "appendnode" : true
35357         
35358     });
35359     if(this.singleExpand){
35360        this.on("beforeexpand", this.restrictExpand, this);
35361     }
35362     if (this.editor) {
35363         this.editor.tree = this;
35364         this.editor = Roo.factory(this.editor, Roo.tree);
35365     }
35366     
35367     if (this.selModel) {
35368         this.selModel = Roo.factory(this.selModel, Roo.tree);
35369     }
35370    
35371 };
35372 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35373     rootVisible : true,
35374     animate: Roo.enableFx,
35375     lines : true,
35376     enableDD : false,
35377     hlDrop : Roo.enableFx,
35378   
35379     renderer: false,
35380     
35381     rendererTip: false,
35382     // private
35383     restrictExpand : function(node){
35384         var p = node.parentNode;
35385         if(p){
35386             if(p.expandedChild && p.expandedChild.parentNode == p){
35387                 p.expandedChild.collapse();
35388             }
35389             p.expandedChild = node;
35390         }
35391     },
35392
35393     // private override
35394     setRootNode : function(node){
35395         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35396         if(!this.rootVisible){
35397             node.ui = new Roo.tree.RootTreeNodeUI(node);
35398         }
35399         return node;
35400     },
35401
35402     /**
35403      * Returns the container element for this TreePanel
35404      */
35405     getEl : function(){
35406         return this.el;
35407     },
35408
35409     /**
35410      * Returns the default TreeLoader for this TreePanel
35411      */
35412     getLoader : function(){
35413         return this.loader;
35414     },
35415
35416     /**
35417      * Expand all nodes
35418      */
35419     expandAll : function(){
35420         this.root.expand(true);
35421     },
35422
35423     /**
35424      * Collapse all nodes
35425      */
35426     collapseAll : function(){
35427         this.root.collapse(true);
35428     },
35429
35430     /**
35431      * Returns the selection model used by this TreePanel
35432      */
35433     getSelectionModel : function(){
35434         if(!this.selModel){
35435             this.selModel = new Roo.tree.DefaultSelectionModel();
35436         }
35437         return this.selModel;
35438     },
35439
35440     /**
35441      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35442      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35443      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35444      * @return {Array}
35445      */
35446     getChecked : function(a, startNode){
35447         startNode = startNode || this.root;
35448         var r = [];
35449         var f = function(){
35450             if(this.attributes.checked){
35451                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35452             }
35453         }
35454         startNode.cascade(f);
35455         return r;
35456     },
35457
35458     /**
35459      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35460      * @param {String} path
35461      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35462      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35463      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35464      */
35465     expandPath : function(path, attr, callback){
35466         attr = attr || "id";
35467         var keys = path.split(this.pathSeparator);
35468         var curNode = this.root;
35469         if(curNode.attributes[attr] != keys[1]){ // invalid root
35470             if(callback){
35471                 callback(false, null);
35472             }
35473             return;
35474         }
35475         var index = 1;
35476         var f = function(){
35477             if(++index == keys.length){
35478                 if(callback){
35479                     callback(true, curNode);
35480                 }
35481                 return;
35482             }
35483             var c = curNode.findChild(attr, keys[index]);
35484             if(!c){
35485                 if(callback){
35486                     callback(false, curNode);
35487                 }
35488                 return;
35489             }
35490             curNode = c;
35491             c.expand(false, false, f);
35492         };
35493         curNode.expand(false, false, f);
35494     },
35495
35496     /**
35497      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35498      * @param {String} path
35499      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35500      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35501      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35502      */
35503     selectPath : function(path, attr, callback){
35504         attr = attr || "id";
35505         var keys = path.split(this.pathSeparator);
35506         var v = keys.pop();
35507         if(keys.length > 0){
35508             var f = function(success, node){
35509                 if(success && node){
35510                     var n = node.findChild(attr, v);
35511                     if(n){
35512                         n.select();
35513                         if(callback){
35514                             callback(true, n);
35515                         }
35516                     }else if(callback){
35517                         callback(false, n);
35518                     }
35519                 }else{
35520                     if(callback){
35521                         callback(false, n);
35522                     }
35523                 }
35524             };
35525             this.expandPath(keys.join(this.pathSeparator), attr, f);
35526         }else{
35527             this.root.select();
35528             if(callback){
35529                 callback(true, this.root);
35530             }
35531         }
35532     },
35533
35534     getTreeEl : function(){
35535         return this.el;
35536     },
35537
35538     /**
35539      * Trigger rendering of this TreePanel
35540      */
35541     render : function(){
35542         if (this.innerCt) {
35543             return this; // stop it rendering more than once!!
35544         }
35545         
35546         this.innerCt = this.el.createChild({tag:"ul",
35547                cls:"x-tree-root-ct " +
35548                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35549
35550         if(this.containerScroll){
35551             Roo.dd.ScrollManager.register(this.el);
35552         }
35553         if((this.enableDD || this.enableDrop) && !this.dropZone){
35554            /**
35555             * The dropZone used by this tree if drop is enabled
35556             * @type Roo.tree.TreeDropZone
35557             */
35558              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35559                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35560            });
35561         }
35562         if((this.enableDD || this.enableDrag) && !this.dragZone){
35563            /**
35564             * The dragZone used by this tree if drag is enabled
35565             * @type Roo.tree.TreeDragZone
35566             */
35567             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35568                ddGroup: this.ddGroup || "TreeDD",
35569                scroll: this.ddScroll
35570            });
35571         }
35572         this.getSelectionModel().init(this);
35573         if (!this.root) {
35574             Roo.log("ROOT not set in tree");
35575             return this;
35576         }
35577         this.root.render();
35578         if(!this.rootVisible){
35579             this.root.renderChildren();
35580         }
35581         return this;
35582     }
35583 });/*
35584  * Based on:
35585  * Ext JS Library 1.1.1
35586  * Copyright(c) 2006-2007, Ext JS, LLC.
35587  *
35588  * Originally Released Under LGPL - original licence link has changed is not relivant.
35589  *
35590  * Fork - LGPL
35591  * <script type="text/javascript">
35592  */
35593  
35594
35595 /**
35596  * @class Roo.tree.DefaultSelectionModel
35597  * @extends Roo.util.Observable
35598  * The default single selection for a TreePanel.
35599  * @param {Object} cfg Configuration
35600  */
35601 Roo.tree.DefaultSelectionModel = function(cfg){
35602    this.selNode = null;
35603    
35604    
35605    
35606    this.addEvents({
35607        /**
35608         * @event selectionchange
35609         * Fires when the selected node changes
35610         * @param {DefaultSelectionModel} this
35611         * @param {TreeNode} node the new selection
35612         */
35613        "selectionchange" : true,
35614
35615        /**
35616         * @event beforeselect
35617         * Fires before the selected node changes, return false to cancel the change
35618         * @param {DefaultSelectionModel} this
35619         * @param {TreeNode} node the new selection
35620         * @param {TreeNode} node the old selection
35621         */
35622        "beforeselect" : true
35623    });
35624    
35625     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35626 };
35627
35628 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35629     init : function(tree){
35630         this.tree = tree;
35631         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35632         tree.on("click", this.onNodeClick, this);
35633     },
35634     
35635     onNodeClick : function(node, e){
35636         if (e.ctrlKey && this.selNode == node)  {
35637             this.unselect(node);
35638             return;
35639         }
35640         this.select(node);
35641     },
35642     
35643     /**
35644      * Select a node.
35645      * @param {TreeNode} node The node to select
35646      * @return {TreeNode} The selected node
35647      */
35648     select : function(node){
35649         var last = this.selNode;
35650         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35651             if(last){
35652                 last.ui.onSelectedChange(false);
35653             }
35654             this.selNode = node;
35655             node.ui.onSelectedChange(true);
35656             this.fireEvent("selectionchange", this, node, last);
35657         }
35658         return node;
35659     },
35660     
35661     /**
35662      * Deselect a node.
35663      * @param {TreeNode} node The node to unselect
35664      */
35665     unselect : function(node){
35666         if(this.selNode == node){
35667             this.clearSelections();
35668         }    
35669     },
35670     
35671     /**
35672      * Clear all selections
35673      */
35674     clearSelections : function(){
35675         var n = this.selNode;
35676         if(n){
35677             n.ui.onSelectedChange(false);
35678             this.selNode = null;
35679             this.fireEvent("selectionchange", this, null);
35680         }
35681         return n;
35682     },
35683     
35684     /**
35685      * Get the selected node
35686      * @return {TreeNode} The selected node
35687      */
35688     getSelectedNode : function(){
35689         return this.selNode;    
35690     },
35691     
35692     /**
35693      * Returns true if the node is selected
35694      * @param {TreeNode} node The node to check
35695      * @return {Boolean}
35696      */
35697     isSelected : function(node){
35698         return this.selNode == node;  
35699     },
35700
35701     /**
35702      * Selects the node above the selected node in the tree, intelligently walking the nodes
35703      * @return TreeNode The new selection
35704      */
35705     selectPrevious : function(){
35706         var s = this.selNode || this.lastSelNode;
35707         if(!s){
35708             return null;
35709         }
35710         var ps = s.previousSibling;
35711         if(ps){
35712             if(!ps.isExpanded() || ps.childNodes.length < 1){
35713                 return this.select(ps);
35714             } else{
35715                 var lc = ps.lastChild;
35716                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35717                     lc = lc.lastChild;
35718                 }
35719                 return this.select(lc);
35720             }
35721         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35722             return this.select(s.parentNode);
35723         }
35724         return null;
35725     },
35726
35727     /**
35728      * Selects the node above the selected node in the tree, intelligently walking the nodes
35729      * @return TreeNode The new selection
35730      */
35731     selectNext : function(){
35732         var s = this.selNode || this.lastSelNode;
35733         if(!s){
35734             return null;
35735         }
35736         if(s.firstChild && s.isExpanded()){
35737              return this.select(s.firstChild);
35738          }else if(s.nextSibling){
35739              return this.select(s.nextSibling);
35740          }else if(s.parentNode){
35741             var newS = null;
35742             s.parentNode.bubble(function(){
35743                 if(this.nextSibling){
35744                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35745                     return false;
35746                 }
35747             });
35748             return newS;
35749          }
35750         return null;
35751     },
35752
35753     onKeyDown : function(e){
35754         var s = this.selNode || this.lastSelNode;
35755         // undesirable, but required
35756         var sm = this;
35757         if(!s){
35758             return;
35759         }
35760         var k = e.getKey();
35761         switch(k){
35762              case e.DOWN:
35763                  e.stopEvent();
35764                  this.selectNext();
35765              break;
35766              case e.UP:
35767                  e.stopEvent();
35768                  this.selectPrevious();
35769              break;
35770              case e.RIGHT:
35771                  e.preventDefault();
35772                  if(s.hasChildNodes()){
35773                      if(!s.isExpanded()){
35774                          s.expand();
35775                      }else if(s.firstChild){
35776                          this.select(s.firstChild, e);
35777                      }
35778                  }
35779              break;
35780              case e.LEFT:
35781                  e.preventDefault();
35782                  if(s.hasChildNodes() && s.isExpanded()){
35783                      s.collapse();
35784                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35785                      this.select(s.parentNode, e);
35786                  }
35787              break;
35788         };
35789     }
35790 });
35791
35792 /**
35793  * @class Roo.tree.MultiSelectionModel
35794  * @extends Roo.util.Observable
35795  * Multi selection for a TreePanel.
35796  * @param {Object} cfg Configuration
35797  */
35798 Roo.tree.MultiSelectionModel = function(){
35799    this.selNodes = [];
35800    this.selMap = {};
35801    this.addEvents({
35802        /**
35803         * @event selectionchange
35804         * Fires when the selected nodes change
35805         * @param {MultiSelectionModel} this
35806         * @param {Array} nodes Array of the selected nodes
35807         */
35808        "selectionchange" : true
35809    });
35810    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35811    
35812 };
35813
35814 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35815     init : function(tree){
35816         this.tree = tree;
35817         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35818         tree.on("click", this.onNodeClick, this);
35819     },
35820     
35821     onNodeClick : function(node, e){
35822         this.select(node, e, e.ctrlKey);
35823     },
35824     
35825     /**
35826      * Select a node.
35827      * @param {TreeNode} node The node to select
35828      * @param {EventObject} e (optional) An event associated with the selection
35829      * @param {Boolean} keepExisting True to retain existing selections
35830      * @return {TreeNode} The selected node
35831      */
35832     select : function(node, e, keepExisting){
35833         if(keepExisting !== true){
35834             this.clearSelections(true);
35835         }
35836         if(this.isSelected(node)){
35837             this.lastSelNode = node;
35838             return node;
35839         }
35840         this.selNodes.push(node);
35841         this.selMap[node.id] = node;
35842         this.lastSelNode = node;
35843         node.ui.onSelectedChange(true);
35844         this.fireEvent("selectionchange", this, this.selNodes);
35845         return node;
35846     },
35847     
35848     /**
35849      * Deselect a node.
35850      * @param {TreeNode} node The node to unselect
35851      */
35852     unselect : function(node){
35853         if(this.selMap[node.id]){
35854             node.ui.onSelectedChange(false);
35855             var sn = this.selNodes;
35856             var index = -1;
35857             if(sn.indexOf){
35858                 index = sn.indexOf(node);
35859             }else{
35860                 for(var i = 0, len = sn.length; i < len; i++){
35861                     if(sn[i] == node){
35862                         index = i;
35863                         break;
35864                     }
35865                 }
35866             }
35867             if(index != -1){
35868                 this.selNodes.splice(index, 1);
35869             }
35870             delete this.selMap[node.id];
35871             this.fireEvent("selectionchange", this, this.selNodes);
35872         }
35873     },
35874     
35875     /**
35876      * Clear all selections
35877      */
35878     clearSelections : function(suppressEvent){
35879         var sn = this.selNodes;
35880         if(sn.length > 0){
35881             for(var i = 0, len = sn.length; i < len; i++){
35882                 sn[i].ui.onSelectedChange(false);
35883             }
35884             this.selNodes = [];
35885             this.selMap = {};
35886             if(suppressEvent !== true){
35887                 this.fireEvent("selectionchange", this, this.selNodes);
35888             }
35889         }
35890     },
35891     
35892     /**
35893      * Returns true if the node is selected
35894      * @param {TreeNode} node The node to check
35895      * @return {Boolean}
35896      */
35897     isSelected : function(node){
35898         return this.selMap[node.id] ? true : false;  
35899     },
35900     
35901     /**
35902      * Returns an array of the selected nodes
35903      * @return {Array}
35904      */
35905     getSelectedNodes : function(){
35906         return this.selNodes;    
35907     },
35908
35909     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35910
35911     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35912
35913     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35914 });/*
35915  * Based on:
35916  * Ext JS Library 1.1.1
35917  * Copyright(c) 2006-2007, Ext JS, LLC.
35918  *
35919  * Originally Released Under LGPL - original licence link has changed is not relivant.
35920  *
35921  * Fork - LGPL
35922  * <script type="text/javascript">
35923  */
35924  
35925 /**
35926  * @class Roo.tree.TreeNode
35927  * @extends Roo.data.Node
35928  * @cfg {String} text The text for this node
35929  * @cfg {Boolean} expanded true to start the node expanded
35930  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
35931  * @cfg {Boolean} allowDrop false if this node cannot be drop on
35932  * @cfg {Boolean} disabled true to start the node disabled
35933  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
35934  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
35935  * @cfg {String} cls A css class to be added to the node
35936  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
35937  * @cfg {String} href URL of the link used for the node (defaults to #)
35938  * @cfg {String} hrefTarget target frame for the link
35939  * @cfg {String} qtip An Ext QuickTip for the node
35940  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
35941  * @cfg {Boolean} singleClickExpand True for single click expand on this node
35942  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
35943  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
35944  * (defaults to undefined with no checkbox rendered)
35945  * @constructor
35946  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
35947  */
35948 Roo.tree.TreeNode = function(attributes){
35949     attributes = attributes || {};
35950     if(typeof attributes == "string"){
35951         attributes = {text: attributes};
35952     }
35953     this.childrenRendered = false;
35954     this.rendered = false;
35955     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
35956     this.expanded = attributes.expanded === true;
35957     this.isTarget = attributes.isTarget !== false;
35958     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
35959     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
35960
35961     /**
35962      * Read-only. The text for this node. To change it use setText().
35963      * @type String
35964      */
35965     this.text = attributes.text;
35966     /**
35967      * True if this node is disabled.
35968      * @type Boolean
35969      */
35970     this.disabled = attributes.disabled === true;
35971
35972     this.addEvents({
35973         /**
35974         * @event textchange
35975         * Fires when the text for this node is changed
35976         * @param {Node} this This node
35977         * @param {String} text The new text
35978         * @param {String} oldText The old text
35979         */
35980         "textchange" : true,
35981         /**
35982         * @event beforeexpand
35983         * Fires before this node is expanded, return false to cancel.
35984         * @param {Node} this This node
35985         * @param {Boolean} deep
35986         * @param {Boolean} anim
35987         */
35988         "beforeexpand" : true,
35989         /**
35990         * @event beforecollapse
35991         * Fires before this node is collapsed, return false to cancel.
35992         * @param {Node} this This node
35993         * @param {Boolean} deep
35994         * @param {Boolean} anim
35995         */
35996         "beforecollapse" : true,
35997         /**
35998         * @event expand
35999         * Fires when this node is expanded
36000         * @param {Node} this This node
36001         */
36002         "expand" : true,
36003         /**
36004         * @event disabledchange
36005         * Fires when the disabled status of this node changes
36006         * @param {Node} this This node
36007         * @param {Boolean} disabled
36008         */
36009         "disabledchange" : true,
36010         /**
36011         * @event collapse
36012         * Fires when this node is collapsed
36013         * @param {Node} this This node
36014         */
36015         "collapse" : true,
36016         /**
36017         * @event beforeclick
36018         * Fires before click processing. Return false to cancel the default action.
36019         * @param {Node} this This node
36020         * @param {Roo.EventObject} e The event object
36021         */
36022         "beforeclick":true,
36023         /**
36024         * @event checkchange
36025         * Fires when a node with a checkbox's checked property changes
36026         * @param {Node} this This node
36027         * @param {Boolean} checked
36028         */
36029         "checkchange":true,
36030         /**
36031         * @event click
36032         * Fires when this node is clicked
36033         * @param {Node} this This node
36034         * @param {Roo.EventObject} e The event object
36035         */
36036         "click":true,
36037         /**
36038         * @event dblclick
36039         * Fires when this node is double clicked
36040         * @param {Node} this This node
36041         * @param {Roo.EventObject} e The event object
36042         */
36043         "dblclick":true,
36044         /**
36045         * @event contextmenu
36046         * Fires when this node is right clicked
36047         * @param {Node} this This node
36048         * @param {Roo.EventObject} e The event object
36049         */
36050         "contextmenu":true,
36051         /**
36052         * @event beforechildrenrendered
36053         * Fires right before the child nodes for this node are rendered
36054         * @param {Node} this This node
36055         */
36056         "beforechildrenrendered":true
36057     });
36058
36059     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36060
36061     /**
36062      * Read-only. The UI for this node
36063      * @type TreeNodeUI
36064      */
36065     this.ui = new uiClass(this);
36066     
36067     // finally support items[]
36068     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36069         return;
36070     }
36071     
36072     
36073     Roo.each(this.attributes.items, function(c) {
36074         this.appendChild(Roo.factory(c,Roo.Tree));
36075     }, this);
36076     delete this.attributes.items;
36077     
36078     
36079     
36080 };
36081 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36082     preventHScroll: true,
36083     /**
36084      * Returns true if this node is expanded
36085      * @return {Boolean}
36086      */
36087     isExpanded : function(){
36088         return this.expanded;
36089     },
36090
36091     /**
36092      * Returns the UI object for this node
36093      * @return {TreeNodeUI}
36094      */
36095     getUI : function(){
36096         return this.ui;
36097     },
36098
36099     // private override
36100     setFirstChild : function(node){
36101         var of = this.firstChild;
36102         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36103         if(this.childrenRendered && of && node != of){
36104             of.renderIndent(true, true);
36105         }
36106         if(this.rendered){
36107             this.renderIndent(true, true);
36108         }
36109     },
36110
36111     // private override
36112     setLastChild : function(node){
36113         var ol = this.lastChild;
36114         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36115         if(this.childrenRendered && ol && node != ol){
36116             ol.renderIndent(true, true);
36117         }
36118         if(this.rendered){
36119             this.renderIndent(true, true);
36120         }
36121     },
36122
36123     // these methods are overridden to provide lazy rendering support
36124     // private override
36125     appendChild : function()
36126     {
36127         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36128         if(node && this.childrenRendered){
36129             node.render();
36130         }
36131         this.ui.updateExpandIcon();
36132         return node;
36133     },
36134
36135     // private override
36136     removeChild : function(node){
36137         this.ownerTree.getSelectionModel().unselect(node);
36138         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36139         // if it's been rendered remove dom node
36140         if(this.childrenRendered){
36141             node.ui.remove();
36142         }
36143         if(this.childNodes.length < 1){
36144             this.collapse(false, false);
36145         }else{
36146             this.ui.updateExpandIcon();
36147         }
36148         if(!this.firstChild) {
36149             this.childrenRendered = false;
36150         }
36151         return node;
36152     },
36153
36154     // private override
36155     insertBefore : function(node, refNode){
36156         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36157         if(newNode && refNode && this.childrenRendered){
36158             node.render();
36159         }
36160         this.ui.updateExpandIcon();
36161         return newNode;
36162     },
36163
36164     /**
36165      * Sets the text for this node
36166      * @param {String} text
36167      */
36168     setText : function(text){
36169         var oldText = this.text;
36170         this.text = text;
36171         this.attributes.text = text;
36172         if(this.rendered){ // event without subscribing
36173             this.ui.onTextChange(this, text, oldText);
36174         }
36175         this.fireEvent("textchange", this, text, oldText);
36176     },
36177
36178     /**
36179      * Triggers selection of this node
36180      */
36181     select : function(){
36182         this.getOwnerTree().getSelectionModel().select(this);
36183     },
36184
36185     /**
36186      * Triggers deselection of this node
36187      */
36188     unselect : function(){
36189         this.getOwnerTree().getSelectionModel().unselect(this);
36190     },
36191
36192     /**
36193      * Returns true if this node is selected
36194      * @return {Boolean}
36195      */
36196     isSelected : function(){
36197         return this.getOwnerTree().getSelectionModel().isSelected(this);
36198     },
36199
36200     /**
36201      * Expand this node.
36202      * @param {Boolean} deep (optional) True to expand all children as well
36203      * @param {Boolean} anim (optional) false to cancel the default animation
36204      * @param {Function} callback (optional) A callback to be called when
36205      * expanding this node completes (does not wait for deep expand to complete).
36206      * Called with 1 parameter, this node.
36207      */
36208     expand : function(deep, anim, callback){
36209         if(!this.expanded){
36210             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36211                 return;
36212             }
36213             if(!this.childrenRendered){
36214                 this.renderChildren();
36215             }
36216             this.expanded = true;
36217             
36218             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36219                 this.ui.animExpand(function(){
36220                     this.fireEvent("expand", this);
36221                     if(typeof callback == "function"){
36222                         callback(this);
36223                     }
36224                     if(deep === true){
36225                         this.expandChildNodes(true);
36226                     }
36227                 }.createDelegate(this));
36228                 return;
36229             }else{
36230                 this.ui.expand();
36231                 this.fireEvent("expand", this);
36232                 if(typeof callback == "function"){
36233                     callback(this);
36234                 }
36235             }
36236         }else{
36237            if(typeof callback == "function"){
36238                callback(this);
36239            }
36240         }
36241         if(deep === true){
36242             this.expandChildNodes(true);
36243         }
36244     },
36245
36246     isHiddenRoot : function(){
36247         return this.isRoot && !this.getOwnerTree().rootVisible;
36248     },
36249
36250     /**
36251      * Collapse this node.
36252      * @param {Boolean} deep (optional) True to collapse all children as well
36253      * @param {Boolean} anim (optional) false to cancel the default animation
36254      */
36255     collapse : function(deep, anim){
36256         if(this.expanded && !this.isHiddenRoot()){
36257             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36258                 return;
36259             }
36260             this.expanded = false;
36261             if((this.getOwnerTree().animate && anim !== false) || anim){
36262                 this.ui.animCollapse(function(){
36263                     this.fireEvent("collapse", this);
36264                     if(deep === true){
36265                         this.collapseChildNodes(true);
36266                     }
36267                 }.createDelegate(this));
36268                 return;
36269             }else{
36270                 this.ui.collapse();
36271                 this.fireEvent("collapse", this);
36272             }
36273         }
36274         if(deep === true){
36275             var cs = this.childNodes;
36276             for(var i = 0, len = cs.length; i < len; i++) {
36277                 cs[i].collapse(true, false);
36278             }
36279         }
36280     },
36281
36282     // private
36283     delayedExpand : function(delay){
36284         if(!this.expandProcId){
36285             this.expandProcId = this.expand.defer(delay, this);
36286         }
36287     },
36288
36289     // private
36290     cancelExpand : function(){
36291         if(this.expandProcId){
36292             clearTimeout(this.expandProcId);
36293         }
36294         this.expandProcId = false;
36295     },
36296
36297     /**
36298      * Toggles expanded/collapsed state of the node
36299      */
36300     toggle : function(){
36301         if(this.expanded){
36302             this.collapse();
36303         }else{
36304             this.expand();
36305         }
36306     },
36307
36308     /**
36309      * Ensures all parent nodes are expanded
36310      */
36311     ensureVisible : function(callback){
36312         var tree = this.getOwnerTree();
36313         tree.expandPath(this.parentNode.getPath(), false, function(){
36314             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36315             Roo.callback(callback);
36316         }.createDelegate(this));
36317     },
36318
36319     /**
36320      * Expand all child nodes
36321      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36322      */
36323     expandChildNodes : function(deep){
36324         var cs = this.childNodes;
36325         for(var i = 0, len = cs.length; i < len; i++) {
36326                 cs[i].expand(deep);
36327         }
36328     },
36329
36330     /**
36331      * Collapse all child nodes
36332      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36333      */
36334     collapseChildNodes : function(deep){
36335         var cs = this.childNodes;
36336         for(var i = 0, len = cs.length; i < len; i++) {
36337                 cs[i].collapse(deep);
36338         }
36339     },
36340
36341     /**
36342      * Disables this node
36343      */
36344     disable : function(){
36345         this.disabled = true;
36346         this.unselect();
36347         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36348             this.ui.onDisableChange(this, true);
36349         }
36350         this.fireEvent("disabledchange", this, true);
36351     },
36352
36353     /**
36354      * Enables this node
36355      */
36356     enable : function(){
36357         this.disabled = false;
36358         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36359             this.ui.onDisableChange(this, false);
36360         }
36361         this.fireEvent("disabledchange", this, false);
36362     },
36363
36364     // private
36365     renderChildren : function(suppressEvent){
36366         if(suppressEvent !== false){
36367             this.fireEvent("beforechildrenrendered", this);
36368         }
36369         var cs = this.childNodes;
36370         for(var i = 0, len = cs.length; i < len; i++){
36371             cs[i].render(true);
36372         }
36373         this.childrenRendered = true;
36374     },
36375
36376     // private
36377     sort : function(fn, scope){
36378         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36379         if(this.childrenRendered){
36380             var cs = this.childNodes;
36381             for(var i = 0, len = cs.length; i < len; i++){
36382                 cs[i].render(true);
36383             }
36384         }
36385     },
36386
36387     // private
36388     render : function(bulkRender){
36389         this.ui.render(bulkRender);
36390         if(!this.rendered){
36391             this.rendered = true;
36392             if(this.expanded){
36393                 this.expanded = false;
36394                 this.expand(false, false);
36395             }
36396         }
36397     },
36398
36399     // private
36400     renderIndent : function(deep, refresh){
36401         if(refresh){
36402             this.ui.childIndent = null;
36403         }
36404         this.ui.renderIndent();
36405         if(deep === true && this.childrenRendered){
36406             var cs = this.childNodes;
36407             for(var i = 0, len = cs.length; i < len; i++){
36408                 cs[i].renderIndent(true, refresh);
36409             }
36410         }
36411     }
36412 });/*
36413  * Based on:
36414  * Ext JS Library 1.1.1
36415  * Copyright(c) 2006-2007, Ext JS, LLC.
36416  *
36417  * Originally Released Under LGPL - original licence link has changed is not relivant.
36418  *
36419  * Fork - LGPL
36420  * <script type="text/javascript">
36421  */
36422  
36423 /**
36424  * @class Roo.tree.AsyncTreeNode
36425  * @extends Roo.tree.TreeNode
36426  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36427  * @constructor
36428  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36429  */
36430  Roo.tree.AsyncTreeNode = function(config){
36431     this.loaded = false;
36432     this.loading = false;
36433     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36434     /**
36435     * @event beforeload
36436     * Fires before this node is loaded, return false to cancel
36437     * @param {Node} this This node
36438     */
36439     this.addEvents({'beforeload':true, 'load': true});
36440     /**
36441     * @event load
36442     * Fires when this node is loaded
36443     * @param {Node} this This node
36444     */
36445     /**
36446      * The loader used by this node (defaults to using the tree's defined loader)
36447      * @type TreeLoader
36448      * @property loader
36449      */
36450 };
36451 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36452     expand : function(deep, anim, callback){
36453         if(this.loading){ // if an async load is already running, waiting til it's done
36454             var timer;
36455             var f = function(){
36456                 if(!this.loading){ // done loading
36457                     clearInterval(timer);
36458                     this.expand(deep, anim, callback);
36459                 }
36460             }.createDelegate(this);
36461             timer = setInterval(f, 200);
36462             return;
36463         }
36464         if(!this.loaded){
36465             if(this.fireEvent("beforeload", this) === false){
36466                 return;
36467             }
36468             this.loading = true;
36469             this.ui.beforeLoad(this);
36470             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36471             if(loader){
36472                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36473                 return;
36474             }
36475         }
36476         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36477     },
36478     
36479     /**
36480      * Returns true if this node is currently loading
36481      * @return {Boolean}
36482      */
36483     isLoading : function(){
36484         return this.loading;  
36485     },
36486     
36487     loadComplete : function(deep, anim, callback){
36488         this.loading = false;
36489         this.loaded = true;
36490         this.ui.afterLoad(this);
36491         this.fireEvent("load", this);
36492         this.expand(deep, anim, callback);
36493     },
36494     
36495     /**
36496      * Returns true if this node has been loaded
36497      * @return {Boolean}
36498      */
36499     isLoaded : function(){
36500         return this.loaded;
36501     },
36502     
36503     hasChildNodes : function(){
36504         if(!this.isLeaf() && !this.loaded){
36505             return true;
36506         }else{
36507             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36508         }
36509     },
36510
36511     /**
36512      * Trigger a reload for this node
36513      * @param {Function} callback
36514      */
36515     reload : function(callback){
36516         this.collapse(false, false);
36517         while(this.firstChild){
36518             this.removeChild(this.firstChild);
36519         }
36520         this.childrenRendered = false;
36521         this.loaded = false;
36522         if(this.isHiddenRoot()){
36523             this.expanded = false;
36524         }
36525         this.expand(false, false, callback);
36526     }
36527 });/*
36528  * Based on:
36529  * Ext JS Library 1.1.1
36530  * Copyright(c) 2006-2007, Ext JS, LLC.
36531  *
36532  * Originally Released Under LGPL - original licence link has changed is not relivant.
36533  *
36534  * Fork - LGPL
36535  * <script type="text/javascript">
36536  */
36537  
36538 /**
36539  * @class Roo.tree.TreeNodeUI
36540  * @constructor
36541  * @param {Object} node The node to render
36542  * The TreeNode UI implementation is separate from the
36543  * tree implementation. Unless you are customizing the tree UI,
36544  * you should never have to use this directly.
36545  */
36546 Roo.tree.TreeNodeUI = function(node){
36547     this.node = node;
36548     this.rendered = false;
36549     this.animating = false;
36550     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36551 };
36552
36553 Roo.tree.TreeNodeUI.prototype = {
36554     removeChild : function(node){
36555         if(this.rendered){
36556             this.ctNode.removeChild(node.ui.getEl());
36557         }
36558     },
36559
36560     beforeLoad : function(){
36561          this.addClass("x-tree-node-loading");
36562     },
36563
36564     afterLoad : function(){
36565          this.removeClass("x-tree-node-loading");
36566     },
36567
36568     onTextChange : function(node, text, oldText){
36569         if(this.rendered){
36570             this.textNode.innerHTML = text;
36571         }
36572     },
36573
36574     onDisableChange : function(node, state){
36575         this.disabled = state;
36576         if(state){
36577             this.addClass("x-tree-node-disabled");
36578         }else{
36579             this.removeClass("x-tree-node-disabled");
36580         }
36581     },
36582
36583     onSelectedChange : function(state){
36584         if(state){
36585             this.focus();
36586             this.addClass("x-tree-selected");
36587         }else{
36588             //this.blur();
36589             this.removeClass("x-tree-selected");
36590         }
36591     },
36592
36593     onMove : function(tree, node, oldParent, newParent, index, refNode){
36594         this.childIndent = null;
36595         if(this.rendered){
36596             var targetNode = newParent.ui.getContainer();
36597             if(!targetNode){//target not rendered
36598                 this.holder = document.createElement("div");
36599                 this.holder.appendChild(this.wrap);
36600                 return;
36601             }
36602             var insertBefore = refNode ? refNode.ui.getEl() : null;
36603             if(insertBefore){
36604                 targetNode.insertBefore(this.wrap, insertBefore);
36605             }else{
36606                 targetNode.appendChild(this.wrap);
36607             }
36608             this.node.renderIndent(true);
36609         }
36610     },
36611
36612     addClass : function(cls){
36613         if(this.elNode){
36614             Roo.fly(this.elNode).addClass(cls);
36615         }
36616     },
36617
36618     removeClass : function(cls){
36619         if(this.elNode){
36620             Roo.fly(this.elNode).removeClass(cls);
36621         }
36622     },
36623
36624     remove : function(){
36625         if(this.rendered){
36626             this.holder = document.createElement("div");
36627             this.holder.appendChild(this.wrap);
36628         }
36629     },
36630
36631     fireEvent : function(){
36632         return this.node.fireEvent.apply(this.node, arguments);
36633     },
36634
36635     initEvents : function(){
36636         this.node.on("move", this.onMove, this);
36637         var E = Roo.EventManager;
36638         var a = this.anchor;
36639
36640         var el = Roo.fly(a, '_treeui');
36641
36642         if(Roo.isOpera){ // opera render bug ignores the CSS
36643             el.setStyle("text-decoration", "none");
36644         }
36645
36646         el.on("click", this.onClick, this);
36647         el.on("dblclick", this.onDblClick, this);
36648
36649         if(this.checkbox){
36650             Roo.EventManager.on(this.checkbox,
36651                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36652         }
36653
36654         el.on("contextmenu", this.onContextMenu, this);
36655
36656         var icon = Roo.fly(this.iconNode);
36657         icon.on("click", this.onClick, this);
36658         icon.on("dblclick", this.onDblClick, this);
36659         icon.on("contextmenu", this.onContextMenu, this);
36660         E.on(this.ecNode, "click", this.ecClick, this, true);
36661
36662         if(this.node.disabled){
36663             this.addClass("x-tree-node-disabled");
36664         }
36665         if(this.node.hidden){
36666             this.addClass("x-tree-node-disabled");
36667         }
36668         var ot = this.node.getOwnerTree();
36669         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36670         if(dd && (!this.node.isRoot || ot.rootVisible)){
36671             Roo.dd.Registry.register(this.elNode, {
36672                 node: this.node,
36673                 handles: this.getDDHandles(),
36674                 isHandle: false
36675             });
36676         }
36677     },
36678
36679     getDDHandles : function(){
36680         return [this.iconNode, this.textNode];
36681     },
36682
36683     hide : function(){
36684         if(this.rendered){
36685             this.wrap.style.display = "none";
36686         }
36687     },
36688
36689     show : function(){
36690         if(this.rendered){
36691             this.wrap.style.display = "";
36692         }
36693     },
36694
36695     onContextMenu : function(e){
36696         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36697             e.preventDefault();
36698             this.focus();
36699             this.fireEvent("contextmenu", this.node, e);
36700         }
36701     },
36702
36703     onClick : function(e){
36704         if(this.dropping){
36705             e.stopEvent();
36706             return;
36707         }
36708         if(this.fireEvent("beforeclick", this.node, e) !== false){
36709             if(!this.disabled && this.node.attributes.href){
36710                 this.fireEvent("click", this.node, e);
36711                 return;
36712             }
36713             e.preventDefault();
36714             if(this.disabled){
36715                 return;
36716             }
36717
36718             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36719                 this.node.toggle();
36720             }
36721
36722             this.fireEvent("click", this.node, e);
36723         }else{
36724             e.stopEvent();
36725         }
36726     },
36727
36728     onDblClick : function(e){
36729         e.preventDefault();
36730         if(this.disabled){
36731             return;
36732         }
36733         if(this.checkbox){
36734             this.toggleCheck();
36735         }
36736         if(!this.animating && this.node.hasChildNodes()){
36737             this.node.toggle();
36738         }
36739         this.fireEvent("dblclick", this.node, e);
36740     },
36741
36742     onCheckChange : function(){
36743         var checked = this.checkbox.checked;
36744         this.node.attributes.checked = checked;
36745         this.fireEvent('checkchange', this.node, checked);
36746     },
36747
36748     ecClick : function(e){
36749         if(!this.animating && this.node.hasChildNodes()){
36750             this.node.toggle();
36751         }
36752     },
36753
36754     startDrop : function(){
36755         this.dropping = true;
36756     },
36757
36758     // delayed drop so the click event doesn't get fired on a drop
36759     endDrop : function(){
36760        setTimeout(function(){
36761            this.dropping = false;
36762        }.createDelegate(this), 50);
36763     },
36764
36765     expand : function(){
36766         this.updateExpandIcon();
36767         this.ctNode.style.display = "";
36768     },
36769
36770     focus : function(){
36771         if(!this.node.preventHScroll){
36772             try{this.anchor.focus();
36773             }catch(e){}
36774         }else if(!Roo.isIE){
36775             try{
36776                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36777                 var l = noscroll.scrollLeft;
36778                 this.anchor.focus();
36779                 noscroll.scrollLeft = l;
36780             }catch(e){}
36781         }
36782     },
36783
36784     toggleCheck : function(value){
36785         var cb = this.checkbox;
36786         if(cb){
36787             cb.checked = (value === undefined ? !cb.checked : value);
36788         }
36789     },
36790
36791     blur : function(){
36792         try{
36793             this.anchor.blur();
36794         }catch(e){}
36795     },
36796
36797     animExpand : function(callback){
36798         var ct = Roo.get(this.ctNode);
36799         ct.stopFx();
36800         if(!this.node.hasChildNodes()){
36801             this.updateExpandIcon();
36802             this.ctNode.style.display = "";
36803             Roo.callback(callback);
36804             return;
36805         }
36806         this.animating = true;
36807         this.updateExpandIcon();
36808
36809         ct.slideIn('t', {
36810            callback : function(){
36811                this.animating = false;
36812                Roo.callback(callback);
36813             },
36814             scope: this,
36815             duration: this.node.ownerTree.duration || .25
36816         });
36817     },
36818
36819     highlight : function(){
36820         var tree = this.node.getOwnerTree();
36821         Roo.fly(this.wrap).highlight(
36822             tree.hlColor || "C3DAF9",
36823             {endColor: tree.hlBaseColor}
36824         );
36825     },
36826
36827     collapse : function(){
36828         this.updateExpandIcon();
36829         this.ctNode.style.display = "none";
36830     },
36831
36832     animCollapse : function(callback){
36833         var ct = Roo.get(this.ctNode);
36834         ct.enableDisplayMode('block');
36835         ct.stopFx();
36836
36837         this.animating = true;
36838         this.updateExpandIcon();
36839
36840         ct.slideOut('t', {
36841             callback : function(){
36842                this.animating = false;
36843                Roo.callback(callback);
36844             },
36845             scope: this,
36846             duration: this.node.ownerTree.duration || .25
36847         });
36848     },
36849
36850     getContainer : function(){
36851         return this.ctNode;
36852     },
36853
36854     getEl : function(){
36855         return this.wrap;
36856     },
36857
36858     appendDDGhost : function(ghostNode){
36859         ghostNode.appendChild(this.elNode.cloneNode(true));
36860     },
36861
36862     getDDRepairXY : function(){
36863         return Roo.lib.Dom.getXY(this.iconNode);
36864     },
36865
36866     onRender : function(){
36867         this.render();
36868     },
36869
36870     render : function(bulkRender){
36871         var n = this.node, a = n.attributes;
36872         var targetNode = n.parentNode ?
36873               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36874
36875         if(!this.rendered){
36876             this.rendered = true;
36877
36878             this.renderElements(n, a, targetNode, bulkRender);
36879
36880             if(a.qtip){
36881                if(this.textNode.setAttributeNS){
36882                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36883                    if(a.qtipTitle){
36884                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36885                    }
36886                }else{
36887                    this.textNode.setAttribute("ext:qtip", a.qtip);
36888                    if(a.qtipTitle){
36889                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36890                    }
36891                }
36892             }else if(a.qtipCfg){
36893                 a.qtipCfg.target = Roo.id(this.textNode);
36894                 Roo.QuickTips.register(a.qtipCfg);
36895             }
36896             this.initEvents();
36897             if(!this.node.expanded){
36898                 this.updateExpandIcon();
36899             }
36900         }else{
36901             if(bulkRender === true) {
36902                 targetNode.appendChild(this.wrap);
36903             }
36904         }
36905     },
36906
36907     renderElements : function(n, a, targetNode, bulkRender)
36908     {
36909         // add some indent caching, this helps performance when rendering a large tree
36910         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36911         var t = n.getOwnerTree();
36912         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36913         if (typeof(n.attributes.html) != 'undefined') {
36914             txt = n.attributes.html;
36915         }
36916         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36917         var cb = typeof a.checked == 'boolean';
36918         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36919         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36920             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36921             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36922             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
36923             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
36924             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
36925              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
36926                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
36927             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
36928             "</li>"];
36929
36930         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
36931             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
36932                                 n.nextSibling.ui.getEl(), buf.join(""));
36933         }else{
36934             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
36935         }
36936
36937         this.elNode = this.wrap.childNodes[0];
36938         this.ctNode = this.wrap.childNodes[1];
36939         var cs = this.elNode.childNodes;
36940         this.indentNode = cs[0];
36941         this.ecNode = cs[1];
36942         this.iconNode = cs[2];
36943         var index = 3;
36944         if(cb){
36945             this.checkbox = cs[3];
36946             index++;
36947         }
36948         this.anchor = cs[index];
36949         this.textNode = cs[index].firstChild;
36950     },
36951
36952     getAnchor : function(){
36953         return this.anchor;
36954     },
36955
36956     getTextEl : function(){
36957         return this.textNode;
36958     },
36959
36960     getIconEl : function(){
36961         return this.iconNode;
36962     },
36963
36964     isChecked : function(){
36965         return this.checkbox ? this.checkbox.checked : false;
36966     },
36967
36968     updateExpandIcon : function(){
36969         if(this.rendered){
36970             var n = this.node, c1, c2;
36971             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
36972             var hasChild = n.hasChildNodes();
36973             if(hasChild){
36974                 if(n.expanded){
36975                     cls += "-minus";
36976                     c1 = "x-tree-node-collapsed";
36977                     c2 = "x-tree-node-expanded";
36978                 }else{
36979                     cls += "-plus";
36980                     c1 = "x-tree-node-expanded";
36981                     c2 = "x-tree-node-collapsed";
36982                 }
36983                 if(this.wasLeaf){
36984                     this.removeClass("x-tree-node-leaf");
36985                     this.wasLeaf = false;
36986                 }
36987                 if(this.c1 != c1 || this.c2 != c2){
36988                     Roo.fly(this.elNode).replaceClass(c1, c2);
36989                     this.c1 = c1; this.c2 = c2;
36990                 }
36991             }else{
36992                 // this changes non-leafs into leafs if they have no children.
36993                 // it's not very rational behaviour..
36994                 
36995                 if(!this.wasLeaf && this.node.leaf){
36996                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
36997                     delete this.c1;
36998                     delete this.c2;
36999                     this.wasLeaf = true;
37000                 }
37001             }
37002             var ecc = "x-tree-ec-icon "+cls;
37003             if(this.ecc != ecc){
37004                 this.ecNode.className = ecc;
37005                 this.ecc = ecc;
37006             }
37007         }
37008     },
37009
37010     getChildIndent : function(){
37011         if(!this.childIndent){
37012             var buf = [];
37013             var p = this.node;
37014             while(p){
37015                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37016                     if(!p.isLast()) {
37017                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37018                     } else {
37019                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37020                     }
37021                 }
37022                 p = p.parentNode;
37023             }
37024             this.childIndent = buf.join("");
37025         }
37026         return this.childIndent;
37027     },
37028
37029     renderIndent : function(){
37030         if(this.rendered){
37031             var indent = "";
37032             var p = this.node.parentNode;
37033             if(p){
37034                 indent = p.ui.getChildIndent();
37035             }
37036             if(this.indentMarkup != indent){ // don't rerender if not required
37037                 this.indentNode.innerHTML = indent;
37038                 this.indentMarkup = indent;
37039             }
37040             this.updateExpandIcon();
37041         }
37042     }
37043 };
37044
37045 Roo.tree.RootTreeNodeUI = function(){
37046     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37047 };
37048 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37049     render : function(){
37050         if(!this.rendered){
37051             var targetNode = this.node.ownerTree.innerCt.dom;
37052             this.node.expanded = true;
37053             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37054             this.wrap = this.ctNode = targetNode.firstChild;
37055         }
37056     },
37057     collapse : function(){
37058     },
37059     expand : function(){
37060     }
37061 });/*
37062  * Based on:
37063  * Ext JS Library 1.1.1
37064  * Copyright(c) 2006-2007, Ext JS, LLC.
37065  *
37066  * Originally Released Under LGPL - original licence link has changed is not relivant.
37067  *
37068  * Fork - LGPL
37069  * <script type="text/javascript">
37070  */
37071 /**
37072  * @class Roo.tree.TreeLoader
37073  * @extends Roo.util.Observable
37074  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37075  * nodes from a specified URL. The response must be a javascript Array definition
37076  * who's elements are node definition objects. eg:
37077  * <pre><code>
37078 {  success : true,
37079    data :      [
37080    
37081     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37082     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37083     ]
37084 }
37085
37086
37087 </code></pre>
37088  * <br><br>
37089  * The old style respose with just an array is still supported, but not recommended.
37090  * <br><br>
37091  *
37092  * A server request is sent, and child nodes are loaded only when a node is expanded.
37093  * The loading node's id is passed to the server under the parameter name "node" to
37094  * enable the server to produce the correct child nodes.
37095  * <br><br>
37096  * To pass extra parameters, an event handler may be attached to the "beforeload"
37097  * event, and the parameters specified in the TreeLoader's baseParams property:
37098  * <pre><code>
37099     myTreeLoader.on("beforeload", function(treeLoader, node) {
37100         this.baseParams.category = node.attributes.category;
37101     }, this);
37102     
37103 </code></pre>
37104  *
37105  * This would pass an HTTP parameter called "category" to the server containing
37106  * the value of the Node's "category" attribute.
37107  * @constructor
37108  * Creates a new Treeloader.
37109  * @param {Object} config A config object containing config properties.
37110  */
37111 Roo.tree.TreeLoader = function(config){
37112     this.baseParams = {};
37113     this.requestMethod = "POST";
37114     Roo.apply(this, config);
37115
37116     this.addEvents({
37117     
37118         /**
37119          * @event beforeload
37120          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37121          * @param {Object} This TreeLoader object.
37122          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37123          * @param {Object} callback The callback function specified in the {@link #load} call.
37124          */
37125         beforeload : true,
37126         /**
37127          * @event load
37128          * Fires when the node has been successfuly loaded.
37129          * @param {Object} This TreeLoader object.
37130          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37131          * @param {Object} response The response object containing the data from the server.
37132          */
37133         load : true,
37134         /**
37135          * @event loadexception
37136          * Fires if the network request failed.
37137          * @param {Object} This TreeLoader object.
37138          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37139          * @param {Object} response The response object containing the data from the server.
37140          */
37141         loadexception : true,
37142         /**
37143          * @event create
37144          * Fires before a node is created, enabling you to return custom Node types 
37145          * @param {Object} This TreeLoader object.
37146          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37147          */
37148         create : true
37149     });
37150
37151     Roo.tree.TreeLoader.superclass.constructor.call(this);
37152 };
37153
37154 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37155     /**
37156     * @cfg {String} dataUrl The URL from which to request a Json string which
37157     * specifies an array of node definition object representing the child nodes
37158     * to be loaded.
37159     */
37160     /**
37161     * @cfg {String} requestMethod either GET or POST
37162     * defaults to POST (due to BC)
37163     * to be loaded.
37164     */
37165     /**
37166     * @cfg {Object} baseParams (optional) An object containing properties which
37167     * specify HTTP parameters to be passed to each request for child nodes.
37168     */
37169     /**
37170     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37171     * created by this loader. If the attributes sent by the server have an attribute in this object,
37172     * they take priority.
37173     */
37174     /**
37175     * @cfg {Object} uiProviders (optional) An object containing properties which
37176     * 
37177     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37178     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37179     * <i>uiProvider</i> attribute of a returned child node is a string rather
37180     * than a reference to a TreeNodeUI implementation, this that string value
37181     * is used as a property name in the uiProviders object. You can define the provider named
37182     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37183     */
37184     uiProviders : {},
37185
37186     /**
37187     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37188     * child nodes before loading.
37189     */
37190     clearOnLoad : true,
37191
37192     /**
37193     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37194     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37195     * Grid query { data : [ .....] }
37196     */
37197     
37198     root : false,
37199      /**
37200     * @cfg {String} queryParam (optional) 
37201     * Name of the query as it will be passed on the querystring (defaults to 'node')
37202     * eg. the request will be ?node=[id]
37203     */
37204     
37205     
37206     queryParam: false,
37207     
37208     /**
37209      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37210      * This is called automatically when a node is expanded, but may be used to reload
37211      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37212      * @param {Roo.tree.TreeNode} node
37213      * @param {Function} callback
37214      */
37215     load : function(node, callback){
37216         if(this.clearOnLoad){
37217             while(node.firstChild){
37218                 node.removeChild(node.firstChild);
37219             }
37220         }
37221         if(node.attributes.children){ // preloaded json children
37222             var cs = node.attributes.children;
37223             for(var i = 0, len = cs.length; i < len; i++){
37224                 node.appendChild(this.createNode(cs[i]));
37225             }
37226             if(typeof callback == "function"){
37227                 callback();
37228             }
37229         }else if(this.dataUrl){
37230             this.requestData(node, callback);
37231         }
37232     },
37233
37234     getParams: function(node){
37235         var buf = [], bp = this.baseParams;
37236         for(var key in bp){
37237             if(typeof bp[key] != "function"){
37238                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37239             }
37240         }
37241         var n = this.queryParam === false ? 'node' : this.queryParam;
37242         buf.push(n + "=", encodeURIComponent(node.id));
37243         return buf.join("");
37244     },
37245
37246     requestData : function(node, callback){
37247         if(this.fireEvent("beforeload", this, node, callback) !== false){
37248             this.transId = Roo.Ajax.request({
37249                 method:this.requestMethod,
37250                 url: this.dataUrl||this.url,
37251                 success: this.handleResponse,
37252                 failure: this.handleFailure,
37253                 scope: this,
37254                 argument: {callback: callback, node: node},
37255                 params: this.getParams(node)
37256             });
37257         }else{
37258             // if the load is cancelled, make sure we notify
37259             // the node that we are done
37260             if(typeof callback == "function"){
37261                 callback();
37262             }
37263         }
37264     },
37265
37266     isLoading : function(){
37267         return this.transId ? true : false;
37268     },
37269
37270     abort : function(){
37271         if(this.isLoading()){
37272             Roo.Ajax.abort(this.transId);
37273         }
37274     },
37275
37276     // private
37277     createNode : function(attr)
37278     {
37279         // apply baseAttrs, nice idea Corey!
37280         if(this.baseAttrs){
37281             Roo.applyIf(attr, this.baseAttrs);
37282         }
37283         if(this.applyLoader !== false){
37284             attr.loader = this;
37285         }
37286         // uiProvider = depreciated..
37287         
37288         if(typeof(attr.uiProvider) == 'string'){
37289            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37290                 /**  eval:var:attr */ eval(attr.uiProvider);
37291         }
37292         if(typeof(this.uiProviders['default']) != 'undefined') {
37293             attr.uiProvider = this.uiProviders['default'];
37294         }
37295         
37296         this.fireEvent('create', this, attr);
37297         
37298         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37299         return(attr.leaf ?
37300                         new Roo.tree.TreeNode(attr) :
37301                         new Roo.tree.AsyncTreeNode(attr));
37302     },
37303
37304     processResponse : function(response, node, callback)
37305     {
37306         var json = response.responseText;
37307         try {
37308             
37309             var o = Roo.decode(json);
37310             
37311             if (this.root === false && typeof(o.success) != undefined) {
37312                 this.root = 'data'; // the default behaviour for list like data..
37313                 }
37314                 
37315             if (this.root !== false &&  !o.success) {
37316                 // it's a failure condition.
37317                 var a = response.argument;
37318                 this.fireEvent("loadexception", this, a.node, response);
37319                 Roo.log("Load failed - should have a handler really");
37320                 return;
37321             }
37322             
37323             
37324             
37325             if (this.root !== false) {
37326                  o = o[this.root];
37327             }
37328             
37329             for(var i = 0, len = o.length; i < len; i++){
37330                 var n = this.createNode(o[i]);
37331                 if(n){
37332                     node.appendChild(n);
37333                 }
37334             }
37335             if(typeof callback == "function"){
37336                 callback(this, node);
37337             }
37338         }catch(e){
37339             this.handleFailure(response);
37340         }
37341     },
37342
37343     handleResponse : function(response){
37344         this.transId = false;
37345         var a = response.argument;
37346         this.processResponse(response, a.node, a.callback);
37347         this.fireEvent("load", this, a.node, response);
37348     },
37349
37350     handleFailure : function(response)
37351     {
37352         // should handle failure better..
37353         this.transId = false;
37354         var a = response.argument;
37355         this.fireEvent("loadexception", this, a.node, response);
37356         if(typeof a.callback == "function"){
37357             a.callback(this, a.node);
37358         }
37359     }
37360 });/*
37361  * Based on:
37362  * Ext JS Library 1.1.1
37363  * Copyright(c) 2006-2007, Ext JS, LLC.
37364  *
37365  * Originally Released Under LGPL - original licence link has changed is not relivant.
37366  *
37367  * Fork - LGPL
37368  * <script type="text/javascript">
37369  */
37370
37371 /**
37372 * @class Roo.tree.TreeFilter
37373 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37374 * @param {TreePanel} tree
37375 * @param {Object} config (optional)
37376  */
37377 Roo.tree.TreeFilter = function(tree, config){
37378     this.tree = tree;
37379     this.filtered = {};
37380     Roo.apply(this, config);
37381 };
37382
37383 Roo.tree.TreeFilter.prototype = {
37384     clearBlank:false,
37385     reverse:false,
37386     autoClear:false,
37387     remove:false,
37388
37389      /**
37390      * Filter the data by a specific attribute.
37391      * @param {String/RegExp} value Either string that the attribute value
37392      * should start with or a RegExp to test against the attribute
37393      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37394      * @param {TreeNode} startNode (optional) The node to start the filter at.
37395      */
37396     filter : function(value, attr, startNode){
37397         attr = attr || "text";
37398         var f;
37399         if(typeof value == "string"){
37400             var vlen = value.length;
37401             // auto clear empty filter
37402             if(vlen == 0 && this.clearBlank){
37403                 this.clear();
37404                 return;
37405             }
37406             value = value.toLowerCase();
37407             f = function(n){
37408                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37409             };
37410         }else if(value.exec){ // regex?
37411             f = function(n){
37412                 return value.test(n.attributes[attr]);
37413             };
37414         }else{
37415             throw 'Illegal filter type, must be string or regex';
37416         }
37417         this.filterBy(f, null, startNode);
37418         },
37419
37420     /**
37421      * Filter by a function. The passed function will be called with each
37422      * node in the tree (or from the startNode). If the function returns true, the node is kept
37423      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37424      * @param {Function} fn The filter function
37425      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37426      */
37427     filterBy : function(fn, scope, startNode){
37428         startNode = startNode || this.tree.root;
37429         if(this.autoClear){
37430             this.clear();
37431         }
37432         var af = this.filtered, rv = this.reverse;
37433         var f = function(n){
37434             if(n == startNode){
37435                 return true;
37436             }
37437             if(af[n.id]){
37438                 return false;
37439             }
37440             var m = fn.call(scope || n, n);
37441             if(!m || rv){
37442                 af[n.id] = n;
37443                 n.ui.hide();
37444                 return false;
37445             }
37446             return true;
37447         };
37448         startNode.cascade(f);
37449         if(this.remove){
37450            for(var id in af){
37451                if(typeof id != "function"){
37452                    var n = af[id];
37453                    if(n && n.parentNode){
37454                        n.parentNode.removeChild(n);
37455                    }
37456                }
37457            }
37458         }
37459     },
37460
37461     /**
37462      * Clears the current filter. Note: with the "remove" option
37463      * set a filter cannot be cleared.
37464      */
37465     clear : function(){
37466         var t = this.tree;
37467         var af = this.filtered;
37468         for(var id in af){
37469             if(typeof id != "function"){
37470                 var n = af[id];
37471                 if(n){
37472                     n.ui.show();
37473                 }
37474             }
37475         }
37476         this.filtered = {};
37477     }
37478 };
37479 /*
37480  * Based on:
37481  * Ext JS Library 1.1.1
37482  * Copyright(c) 2006-2007, Ext JS, LLC.
37483  *
37484  * Originally Released Under LGPL - original licence link has changed is not relivant.
37485  *
37486  * Fork - LGPL
37487  * <script type="text/javascript">
37488  */
37489  
37490
37491 /**
37492  * @class Roo.tree.TreeSorter
37493  * Provides sorting of nodes in a TreePanel
37494  * 
37495  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37496  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37497  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37498  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37499  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37500  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37501  * @constructor
37502  * @param {TreePanel} tree
37503  * @param {Object} config
37504  */
37505 Roo.tree.TreeSorter = function(tree, config){
37506     Roo.apply(this, config);
37507     tree.on("beforechildrenrendered", this.doSort, this);
37508     tree.on("append", this.updateSort, this);
37509     tree.on("insert", this.updateSort, this);
37510     
37511     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37512     var p = this.property || "text";
37513     var sortType = this.sortType;
37514     var fs = this.folderSort;
37515     var cs = this.caseSensitive === true;
37516     var leafAttr = this.leafAttr || 'leaf';
37517
37518     this.sortFn = function(n1, n2){
37519         if(fs){
37520             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37521                 return 1;
37522             }
37523             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37524                 return -1;
37525             }
37526         }
37527         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37528         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37529         if(v1 < v2){
37530                         return dsc ? +1 : -1;
37531                 }else if(v1 > v2){
37532                         return dsc ? -1 : +1;
37533         }else{
37534                 return 0;
37535         }
37536     };
37537 };
37538
37539 Roo.tree.TreeSorter.prototype = {
37540     doSort : function(node){
37541         node.sort(this.sortFn);
37542     },
37543     
37544     compareNodes : function(n1, n2){
37545         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37546     },
37547     
37548     updateSort : function(tree, node){
37549         if(node.childrenRendered){
37550             this.doSort.defer(1, this, [node]);
37551         }
37552     }
37553 };/*
37554  * Based on:
37555  * Ext JS Library 1.1.1
37556  * Copyright(c) 2006-2007, Ext JS, LLC.
37557  *
37558  * Originally Released Under LGPL - original licence link has changed is not relivant.
37559  *
37560  * Fork - LGPL
37561  * <script type="text/javascript">
37562  */
37563
37564 if(Roo.dd.DropZone){
37565     
37566 Roo.tree.TreeDropZone = function(tree, config){
37567     this.allowParentInsert = false;
37568     this.allowContainerDrop = false;
37569     this.appendOnly = false;
37570     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37571     this.tree = tree;
37572     this.lastInsertClass = "x-tree-no-status";
37573     this.dragOverData = {};
37574 };
37575
37576 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37577     ddGroup : "TreeDD",
37578     scroll:  true,
37579     
37580     expandDelay : 1000,
37581     
37582     expandNode : function(node){
37583         if(node.hasChildNodes() && !node.isExpanded()){
37584             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37585         }
37586     },
37587     
37588     queueExpand : function(node){
37589         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37590     },
37591     
37592     cancelExpand : function(){
37593         if(this.expandProcId){
37594             clearTimeout(this.expandProcId);
37595             this.expandProcId = false;
37596         }
37597     },
37598     
37599     isValidDropPoint : function(n, pt, dd, e, data){
37600         if(!n || !data){ return false; }
37601         var targetNode = n.node;
37602         var dropNode = data.node;
37603         // default drop rules
37604         if(!(targetNode && targetNode.isTarget && pt)){
37605             return false;
37606         }
37607         if(pt == "append" && targetNode.allowChildren === false){
37608             return false;
37609         }
37610         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37611             return false;
37612         }
37613         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37614             return false;
37615         }
37616         // reuse the object
37617         var overEvent = this.dragOverData;
37618         overEvent.tree = this.tree;
37619         overEvent.target = targetNode;
37620         overEvent.data = data;
37621         overEvent.point = pt;
37622         overEvent.source = dd;
37623         overEvent.rawEvent = e;
37624         overEvent.dropNode = dropNode;
37625         overEvent.cancel = false;  
37626         var result = this.tree.fireEvent("nodedragover", overEvent);
37627         return overEvent.cancel === false && result !== false;
37628     },
37629     
37630     getDropPoint : function(e, n, dd)
37631     {
37632         var tn = n.node;
37633         if(tn.isRoot){
37634             return tn.allowChildren !== false ? "append" : false; // always append for root
37635         }
37636         var dragEl = n.ddel;
37637         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37638         var y = Roo.lib.Event.getPageY(e);
37639         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37640         
37641         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37642         var noAppend = tn.allowChildren === false;
37643         if(this.appendOnly || tn.parentNode.allowChildren === false){
37644             return noAppend ? false : "append";
37645         }
37646         var noBelow = false;
37647         if(!this.allowParentInsert){
37648             noBelow = tn.hasChildNodes() && tn.isExpanded();
37649         }
37650         var q = (b - t) / (noAppend ? 2 : 3);
37651         if(y >= t && y < (t + q)){
37652             return "above";
37653         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37654             return "below";
37655         }else{
37656             return "append";
37657         }
37658     },
37659     
37660     onNodeEnter : function(n, dd, e, data)
37661     {
37662         this.cancelExpand();
37663     },
37664     
37665     onNodeOver : function(n, dd, e, data)
37666     {
37667        
37668         var pt = this.getDropPoint(e, n, dd);
37669         var node = n.node;
37670         
37671         // auto node expand check
37672         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37673             this.queueExpand(node);
37674         }else if(pt != "append"){
37675             this.cancelExpand();
37676         }
37677         
37678         // set the insert point style on the target node
37679         var returnCls = this.dropNotAllowed;
37680         if(this.isValidDropPoint(n, pt, dd, e, data)){
37681            if(pt){
37682                var el = n.ddel;
37683                var cls;
37684                if(pt == "above"){
37685                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37686                    cls = "x-tree-drag-insert-above";
37687                }else if(pt == "below"){
37688                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37689                    cls = "x-tree-drag-insert-below";
37690                }else{
37691                    returnCls = "x-tree-drop-ok-append";
37692                    cls = "x-tree-drag-append";
37693                }
37694                if(this.lastInsertClass != cls){
37695                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37696                    this.lastInsertClass = cls;
37697                }
37698            }
37699        }
37700        return returnCls;
37701     },
37702     
37703     onNodeOut : function(n, dd, e, data){
37704         
37705         this.cancelExpand();
37706         this.removeDropIndicators(n);
37707     },
37708     
37709     onNodeDrop : function(n, dd, e, data){
37710         var point = this.getDropPoint(e, n, dd);
37711         var targetNode = n.node;
37712         targetNode.ui.startDrop();
37713         if(!this.isValidDropPoint(n, point, dd, e, data)){
37714             targetNode.ui.endDrop();
37715             return false;
37716         }
37717         // first try to find the drop node
37718         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37719         var dropEvent = {
37720             tree : this.tree,
37721             target: targetNode,
37722             data: data,
37723             point: point,
37724             source: dd,
37725             rawEvent: e,
37726             dropNode: dropNode,
37727             cancel: !dropNode   
37728         };
37729         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37730         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37731             targetNode.ui.endDrop();
37732             return false;
37733         }
37734         // allow target changing
37735         targetNode = dropEvent.target;
37736         if(point == "append" && !targetNode.isExpanded()){
37737             targetNode.expand(false, null, function(){
37738                 this.completeDrop(dropEvent);
37739             }.createDelegate(this));
37740         }else{
37741             this.completeDrop(dropEvent);
37742         }
37743         return true;
37744     },
37745     
37746     completeDrop : function(de){
37747         var ns = de.dropNode, p = de.point, t = de.target;
37748         if(!(ns instanceof Array)){
37749             ns = [ns];
37750         }
37751         var n;
37752         for(var i = 0, len = ns.length; i < len; i++){
37753             n = ns[i];
37754             if(p == "above"){
37755                 t.parentNode.insertBefore(n, t);
37756             }else if(p == "below"){
37757                 t.parentNode.insertBefore(n, t.nextSibling);
37758             }else{
37759                 t.appendChild(n);
37760             }
37761         }
37762         n.ui.focus();
37763         if(this.tree.hlDrop){
37764             n.ui.highlight();
37765         }
37766         t.ui.endDrop();
37767         this.tree.fireEvent("nodedrop", de);
37768     },
37769     
37770     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37771         if(this.tree.hlDrop){
37772             dropNode.ui.focus();
37773             dropNode.ui.highlight();
37774         }
37775         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37776     },
37777     
37778     getTree : function(){
37779         return this.tree;
37780     },
37781     
37782     removeDropIndicators : function(n){
37783         if(n && n.ddel){
37784             var el = n.ddel;
37785             Roo.fly(el).removeClass([
37786                     "x-tree-drag-insert-above",
37787                     "x-tree-drag-insert-below",
37788                     "x-tree-drag-append"]);
37789             this.lastInsertClass = "_noclass";
37790         }
37791     },
37792     
37793     beforeDragDrop : function(target, e, id){
37794         this.cancelExpand();
37795         return true;
37796     },
37797     
37798     afterRepair : function(data){
37799         if(data && Roo.enableFx){
37800             data.node.ui.highlight();
37801         }
37802         this.hideProxy();
37803     } 
37804     
37805 });
37806
37807 }
37808 /*
37809  * Based on:
37810  * Ext JS Library 1.1.1
37811  * Copyright(c) 2006-2007, Ext JS, LLC.
37812  *
37813  * Originally Released Under LGPL - original licence link has changed is not relivant.
37814  *
37815  * Fork - LGPL
37816  * <script type="text/javascript">
37817  */
37818  
37819
37820 if(Roo.dd.DragZone){
37821 Roo.tree.TreeDragZone = function(tree, config){
37822     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37823     this.tree = tree;
37824 };
37825
37826 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37827     ddGroup : "TreeDD",
37828    
37829     onBeforeDrag : function(data, e){
37830         var n = data.node;
37831         return n && n.draggable && !n.disabled;
37832     },
37833      
37834     
37835     onInitDrag : function(e){
37836         var data = this.dragData;
37837         this.tree.getSelectionModel().select(data.node);
37838         this.proxy.update("");
37839         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37840         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37841     },
37842     
37843     getRepairXY : function(e, data){
37844         return data.node.ui.getDDRepairXY();
37845     },
37846     
37847     onEndDrag : function(data, e){
37848         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37849         
37850         
37851     },
37852     
37853     onValidDrop : function(dd, e, id){
37854         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37855         this.hideProxy();
37856     },
37857     
37858     beforeInvalidDrop : function(e, id){
37859         // this scrolls the original position back into view
37860         var sm = this.tree.getSelectionModel();
37861         sm.clearSelections();
37862         sm.select(this.dragData.node);
37863     }
37864 });
37865 }/*
37866  * Based on:
37867  * Ext JS Library 1.1.1
37868  * Copyright(c) 2006-2007, Ext JS, LLC.
37869  *
37870  * Originally Released Under LGPL - original licence link has changed is not relivant.
37871  *
37872  * Fork - LGPL
37873  * <script type="text/javascript">
37874  */
37875 /**
37876  * @class Roo.tree.TreeEditor
37877  * @extends Roo.Editor
37878  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37879  * as the editor field.
37880  * @constructor
37881  * @param {Object} config (used to be the tree panel.)
37882  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37883  * 
37884  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37885  * @cfg {Roo.form.TextField} field [required] The field configuration
37886  *
37887  * 
37888  */
37889 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37890     var tree = config;
37891     var field;
37892     if (oldconfig) { // old style..
37893         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37894     } else {
37895         // new style..
37896         tree = config.tree;
37897         config.field = config.field  || {};
37898         config.field.xtype = 'TextField';
37899         field = Roo.factory(config.field, Roo.form);
37900     }
37901     config = config || {};
37902     
37903     
37904     this.addEvents({
37905         /**
37906          * @event beforenodeedit
37907          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37908          * false from the handler of this event.
37909          * @param {Editor} this
37910          * @param {Roo.tree.Node} node 
37911          */
37912         "beforenodeedit" : true
37913     });
37914     
37915     //Roo.log(config);
37916     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37917
37918     this.tree = tree;
37919
37920     tree.on('beforeclick', this.beforeNodeClick, this);
37921     tree.getTreeEl().on('mousedown', this.hide, this);
37922     this.on('complete', this.updateNode, this);
37923     this.on('beforestartedit', this.fitToTree, this);
37924     this.on('startedit', this.bindScroll, this, {delay:10});
37925     this.on('specialkey', this.onSpecialKey, this);
37926 };
37927
37928 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
37929     /**
37930      * @cfg {String} alignment
37931      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
37932      */
37933     alignment: "l-l",
37934     // inherit
37935     autoSize: false,
37936     /**
37937      * @cfg {Boolean} hideEl
37938      * True to hide the bound element while the editor is displayed (defaults to false)
37939      */
37940     hideEl : false,
37941     /**
37942      * @cfg {String} cls
37943      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
37944      */
37945     cls: "x-small-editor x-tree-editor",
37946     /**
37947      * @cfg {Boolean} shim
37948      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
37949      */
37950     shim:false,
37951     // inherit
37952     shadow:"frame",
37953     /**
37954      * @cfg {Number} maxWidth
37955      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
37956      * the containing tree element's size, it will be automatically limited for you to the container width, taking
37957      * scroll and client offsets into account prior to each edit.
37958      */
37959     maxWidth: 250,
37960
37961     editDelay : 350,
37962
37963     // private
37964     fitToTree : function(ed, el){
37965         var td = this.tree.getTreeEl().dom, nd = el.dom;
37966         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
37967             td.scrollLeft = nd.offsetLeft;
37968         }
37969         var w = Math.min(
37970                 this.maxWidth,
37971                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
37972         this.setSize(w, '');
37973         
37974         return this.fireEvent('beforenodeedit', this, this.editNode);
37975         
37976     },
37977
37978     // private
37979     triggerEdit : function(node){
37980         this.completeEdit();
37981         this.editNode = node;
37982         this.startEdit(node.ui.textNode, node.text);
37983     },
37984
37985     // private
37986     bindScroll : function(){
37987         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
37988     },
37989
37990     // private
37991     beforeNodeClick : function(node, e){
37992         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
37993         this.lastClick = new Date();
37994         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
37995             e.stopEvent();
37996             this.triggerEdit(node);
37997             return false;
37998         }
37999         return true;
38000     },
38001
38002     // private
38003     updateNode : function(ed, value){
38004         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38005         this.editNode.setText(value);
38006     },
38007
38008     // private
38009     onHide : function(){
38010         Roo.tree.TreeEditor.superclass.onHide.call(this);
38011         if(this.editNode){
38012             this.editNode.ui.focus();
38013         }
38014     },
38015
38016     // private
38017     onSpecialKey : function(field, e){
38018         var k = e.getKey();
38019         if(k == e.ESC){
38020             e.stopEvent();
38021             this.cancelEdit();
38022         }else if(k == e.ENTER && !e.hasModifier()){
38023             e.stopEvent();
38024             this.completeEdit();
38025         }
38026     }
38027 });//<Script type="text/javascript">
38028 /*
38029  * Based on:
38030  * Ext JS Library 1.1.1
38031  * Copyright(c) 2006-2007, Ext JS, LLC.
38032  *
38033  * Originally Released Under LGPL - original licence link has changed is not relivant.
38034  *
38035  * Fork - LGPL
38036  * <script type="text/javascript">
38037  */
38038  
38039 /**
38040  * Not documented??? - probably should be...
38041  */
38042
38043 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38044     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38045     
38046     renderElements : function(n, a, targetNode, bulkRender){
38047         //consel.log("renderElements?");
38048         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38049
38050         var t = n.getOwnerTree();
38051         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38052         
38053         var cols = t.columns;
38054         var bw = t.borderWidth;
38055         var c = cols[0];
38056         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38057          var cb = typeof a.checked == "boolean";
38058         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38059         var colcls = 'x-t-' + tid + '-c0';
38060         var buf = [
38061             '<li class="x-tree-node">',
38062             
38063                 
38064                 '<div class="x-tree-node-el ', a.cls,'">',
38065                     // extran...
38066                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38067                 
38068                 
38069                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38070                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38071                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38072                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38073                            (a.iconCls ? ' '+a.iconCls : ''),
38074                            '" unselectable="on" />',
38075                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38076                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38077                              
38078                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38079                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38080                             '<span unselectable="on" qtip="' + tx + '">',
38081                              tx,
38082                              '</span></a>' ,
38083                     '</div>',
38084                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38085                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38086                  ];
38087         for(var i = 1, len = cols.length; i < len; i++){
38088             c = cols[i];
38089             colcls = 'x-t-' + tid + '-c' +i;
38090             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38091             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38092                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38093                       "</div>");
38094          }
38095          
38096          buf.push(
38097             '</a>',
38098             '<div class="x-clear"></div></div>',
38099             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38100             "</li>");
38101         
38102         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38103             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38104                                 n.nextSibling.ui.getEl(), buf.join(""));
38105         }else{
38106             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38107         }
38108         var el = this.wrap.firstChild;
38109         this.elRow = el;
38110         this.elNode = el.firstChild;
38111         this.ranchor = el.childNodes[1];
38112         this.ctNode = this.wrap.childNodes[1];
38113         var cs = el.firstChild.childNodes;
38114         this.indentNode = cs[0];
38115         this.ecNode = cs[1];
38116         this.iconNode = cs[2];
38117         var index = 3;
38118         if(cb){
38119             this.checkbox = cs[3];
38120             index++;
38121         }
38122         this.anchor = cs[index];
38123         
38124         this.textNode = cs[index].firstChild;
38125         
38126         //el.on("click", this.onClick, this);
38127         //el.on("dblclick", this.onDblClick, this);
38128         
38129         
38130        // console.log(this);
38131     },
38132     initEvents : function(){
38133         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38134         
38135             
38136         var a = this.ranchor;
38137
38138         var el = Roo.get(a);
38139
38140         if(Roo.isOpera){ // opera render bug ignores the CSS
38141             el.setStyle("text-decoration", "none");
38142         }
38143
38144         el.on("click", this.onClick, this);
38145         el.on("dblclick", this.onDblClick, this);
38146         el.on("contextmenu", this.onContextMenu, this);
38147         
38148     },
38149     
38150     /*onSelectedChange : function(state){
38151         if(state){
38152             this.focus();
38153             this.addClass("x-tree-selected");
38154         }else{
38155             //this.blur();
38156             this.removeClass("x-tree-selected");
38157         }
38158     },*/
38159     addClass : function(cls){
38160         if(this.elRow){
38161             Roo.fly(this.elRow).addClass(cls);
38162         }
38163         
38164     },
38165     
38166     
38167     removeClass : function(cls){
38168         if(this.elRow){
38169             Roo.fly(this.elRow).removeClass(cls);
38170         }
38171     }
38172
38173     
38174     
38175 });//<Script type="text/javascript">
38176
38177 /*
38178  * Based on:
38179  * Ext JS Library 1.1.1
38180  * Copyright(c) 2006-2007, Ext JS, LLC.
38181  *
38182  * Originally Released Under LGPL - original licence link has changed is not relivant.
38183  *
38184  * Fork - LGPL
38185  * <script type="text/javascript">
38186  */
38187  
38188
38189 /**
38190  * @class Roo.tree.ColumnTree
38191  * @extends Roo.tree.TreePanel
38192  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38193  * @cfg {int} borderWidth  compined right/left border allowance
38194  * @constructor
38195  * @param {String/HTMLElement/Element} el The container element
38196  * @param {Object} config
38197  */
38198 Roo.tree.ColumnTree =  function(el, config)
38199 {
38200    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38201    this.addEvents({
38202         /**
38203         * @event resize
38204         * Fire this event on a container when it resizes
38205         * @param {int} w Width
38206         * @param {int} h Height
38207         */
38208        "resize" : true
38209     });
38210     this.on('resize', this.onResize, this);
38211 };
38212
38213 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38214     //lines:false,
38215     
38216     
38217     borderWidth: Roo.isBorderBox ? 0 : 2, 
38218     headEls : false,
38219     
38220     render : function(){
38221         // add the header.....
38222        
38223         Roo.tree.ColumnTree.superclass.render.apply(this);
38224         
38225         this.el.addClass('x-column-tree');
38226         
38227         this.headers = this.el.createChild(
38228             {cls:'x-tree-headers'},this.innerCt.dom);
38229    
38230         var cols = this.columns, c;
38231         var totalWidth = 0;
38232         this.headEls = [];
38233         var  len = cols.length;
38234         for(var i = 0; i < len; i++){
38235              c = cols[i];
38236              totalWidth += c.width;
38237             this.headEls.push(this.headers.createChild({
38238                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38239                  cn: {
38240                      cls:'x-tree-hd-text',
38241                      html: c.header
38242                  },
38243                  style:'width:'+(c.width-this.borderWidth)+'px;'
38244              }));
38245         }
38246         this.headers.createChild({cls:'x-clear'});
38247         // prevent floats from wrapping when clipped
38248         this.headers.setWidth(totalWidth);
38249         //this.innerCt.setWidth(totalWidth);
38250         this.innerCt.setStyle({ overflow: 'auto' });
38251         this.onResize(this.width, this.height);
38252              
38253         
38254     },
38255     onResize : function(w,h)
38256     {
38257         this.height = h;
38258         this.width = w;
38259         // resize cols..
38260         this.innerCt.setWidth(this.width);
38261         this.innerCt.setHeight(this.height-20);
38262         
38263         // headers...
38264         var cols = this.columns, c;
38265         var totalWidth = 0;
38266         var expEl = false;
38267         var len = cols.length;
38268         for(var i = 0; i < len; i++){
38269             c = cols[i];
38270             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38271                 // it's the expander..
38272                 expEl  = this.headEls[i];
38273                 continue;
38274             }
38275             totalWidth += c.width;
38276             
38277         }
38278         if (expEl) {
38279             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38280         }
38281         this.headers.setWidth(w-20);
38282
38283         
38284         
38285         
38286     }
38287 });
38288 /*
38289  * Based on:
38290  * Ext JS Library 1.1.1
38291  * Copyright(c) 2006-2007, Ext JS, LLC.
38292  *
38293  * Originally Released Under LGPL - original licence link has changed is not relivant.
38294  *
38295  * Fork - LGPL
38296  * <script type="text/javascript">
38297  */
38298  
38299 /**
38300  * @class Roo.menu.Menu
38301  * @extends Roo.util.Observable
38302  * @children Roo.menu.BaseItem
38303  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38304  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38305  * @constructor
38306  * Creates a new Menu
38307  * @param {Object} config Configuration options
38308  */
38309 Roo.menu.Menu = function(config){
38310     
38311     Roo.menu.Menu.superclass.constructor.call(this, config);
38312     
38313     this.id = this.id || Roo.id();
38314     this.addEvents({
38315         /**
38316          * @event beforeshow
38317          * Fires before this menu is displayed
38318          * @param {Roo.menu.Menu} this
38319          */
38320         beforeshow : true,
38321         /**
38322          * @event beforehide
38323          * Fires before this menu is hidden
38324          * @param {Roo.menu.Menu} this
38325          */
38326         beforehide : true,
38327         /**
38328          * @event show
38329          * Fires after this menu is displayed
38330          * @param {Roo.menu.Menu} this
38331          */
38332         show : true,
38333         /**
38334          * @event hide
38335          * Fires after this menu is hidden
38336          * @param {Roo.menu.Menu} this
38337          */
38338         hide : true,
38339         /**
38340          * @event click
38341          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38342          * @param {Roo.menu.Menu} this
38343          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38344          * @param {Roo.EventObject} e
38345          */
38346         click : true,
38347         /**
38348          * @event mouseover
38349          * Fires when the mouse is hovering over this menu
38350          * @param {Roo.menu.Menu} this
38351          * @param {Roo.EventObject} e
38352          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38353          */
38354         mouseover : true,
38355         /**
38356          * @event mouseout
38357          * Fires when the mouse exits this menu
38358          * @param {Roo.menu.Menu} this
38359          * @param {Roo.EventObject} e
38360          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38361          */
38362         mouseout : true,
38363         /**
38364          * @event itemclick
38365          * Fires when a menu item contained in this menu is clicked
38366          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38367          * @param {Roo.EventObject} e
38368          */
38369         itemclick: true
38370     });
38371     if (this.registerMenu) {
38372         Roo.menu.MenuMgr.register(this);
38373     }
38374     
38375     var mis = this.items;
38376     this.items = new Roo.util.MixedCollection();
38377     if(mis){
38378         this.add.apply(this, mis);
38379     }
38380 };
38381
38382 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38383     /**
38384      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38385      */
38386     minWidth : 120,
38387     /**
38388      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38389      * for bottom-right shadow (defaults to "sides")
38390      */
38391     shadow : "sides",
38392     /**
38393      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38394      * this menu (defaults to "tl-tr?")
38395      */
38396     subMenuAlign : "tl-tr?",
38397     /**
38398      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38399      * relative to its element of origin (defaults to "tl-bl?")
38400      */
38401     defaultAlign : "tl-bl?",
38402     /**
38403      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38404      */
38405     allowOtherMenus : false,
38406     /**
38407      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38408      */
38409     registerMenu : true,
38410
38411     hidden:true,
38412
38413     // private
38414     render : function(){
38415         if(this.el){
38416             return;
38417         }
38418         var el = this.el = new Roo.Layer({
38419             cls: "x-menu",
38420             shadow:this.shadow,
38421             constrain: false,
38422             parentEl: this.parentEl || document.body,
38423             zindex:15000
38424         });
38425
38426         this.keyNav = new Roo.menu.MenuNav(this);
38427
38428         if(this.plain){
38429             el.addClass("x-menu-plain");
38430         }
38431         if(this.cls){
38432             el.addClass(this.cls);
38433         }
38434         // generic focus element
38435         this.focusEl = el.createChild({
38436             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38437         });
38438         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38439         //disabling touch- as it's causing issues ..
38440         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38441         ul.on('click'   , this.onClick, this);
38442         
38443         
38444         ul.on("mouseover", this.onMouseOver, this);
38445         ul.on("mouseout", this.onMouseOut, this);
38446         this.items.each(function(item){
38447             if (item.hidden) {
38448                 return;
38449             }
38450             
38451             var li = document.createElement("li");
38452             li.className = "x-menu-list-item";
38453             ul.dom.appendChild(li);
38454             item.render(li, this);
38455         }, this);
38456         this.ul = ul;
38457         this.autoWidth();
38458     },
38459
38460     // private
38461     autoWidth : function(){
38462         var el = this.el, ul = this.ul;
38463         if(!el){
38464             return;
38465         }
38466         var w = this.width;
38467         if(w){
38468             el.setWidth(w);
38469         }else if(Roo.isIE){
38470             el.setWidth(this.minWidth);
38471             var t = el.dom.offsetWidth; // force recalc
38472             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38473         }
38474     },
38475
38476     // private
38477     delayAutoWidth : function(){
38478         if(this.rendered){
38479             if(!this.awTask){
38480                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38481             }
38482             this.awTask.delay(20);
38483         }
38484     },
38485
38486     // private
38487     findTargetItem : function(e){
38488         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38489         if(t && t.menuItemId){
38490             return this.items.get(t.menuItemId);
38491         }
38492     },
38493
38494     // private
38495     onClick : function(e){
38496         Roo.log("menu.onClick");
38497         var t = this.findTargetItem(e);
38498         if(!t){
38499             return;
38500         }
38501         Roo.log(e);
38502         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38503             if(t == this.activeItem && t.shouldDeactivate(e)){
38504                 this.activeItem.deactivate();
38505                 delete this.activeItem;
38506                 return;
38507             }
38508             if(t.canActivate){
38509                 this.setActiveItem(t, true);
38510             }
38511             return;
38512             
38513             
38514         }
38515         
38516         t.onClick(e);
38517         this.fireEvent("click", this, t, e);
38518     },
38519
38520     // private
38521     setActiveItem : function(item, autoExpand){
38522         if(item != this.activeItem){
38523             if(this.activeItem){
38524                 this.activeItem.deactivate();
38525             }
38526             this.activeItem = item;
38527             item.activate(autoExpand);
38528         }else if(autoExpand){
38529             item.expandMenu();
38530         }
38531     },
38532
38533     // private
38534     tryActivate : function(start, step){
38535         var items = this.items;
38536         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38537             var item = items.get(i);
38538             if(!item.disabled && item.canActivate){
38539                 this.setActiveItem(item, false);
38540                 return item;
38541             }
38542         }
38543         return false;
38544     },
38545
38546     // private
38547     onMouseOver : function(e){
38548         var t;
38549         if(t = this.findTargetItem(e)){
38550             if(t.canActivate && !t.disabled){
38551                 this.setActiveItem(t, true);
38552             }
38553         }
38554         this.fireEvent("mouseover", this, e, t);
38555     },
38556
38557     // private
38558     onMouseOut : function(e){
38559         var t;
38560         if(t = this.findTargetItem(e)){
38561             if(t == this.activeItem && t.shouldDeactivate(e)){
38562                 this.activeItem.deactivate();
38563                 delete this.activeItem;
38564             }
38565         }
38566         this.fireEvent("mouseout", this, e, t);
38567     },
38568
38569     /**
38570      * Read-only.  Returns true if the menu is currently displayed, else false.
38571      * @type Boolean
38572      */
38573     isVisible : function(){
38574         return this.el && !this.hidden;
38575     },
38576
38577     /**
38578      * Displays this menu relative to another element
38579      * @param {String/HTMLElement/Roo.Element} element The element to align to
38580      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38581      * the element (defaults to this.defaultAlign)
38582      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38583      */
38584     show : function(el, pos, parentMenu){
38585         this.parentMenu = parentMenu;
38586         if(!this.el){
38587             this.render();
38588         }
38589         this.fireEvent("beforeshow", this);
38590         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38591     },
38592
38593     /**
38594      * Displays this menu at a specific xy position
38595      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38596      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38597      */
38598     showAt : function(xy, parentMenu, /* private: */_e){
38599         this.parentMenu = parentMenu;
38600         if(!this.el){
38601             this.render();
38602         }
38603         if(_e !== false){
38604             this.fireEvent("beforeshow", this);
38605             xy = this.el.adjustForConstraints(xy);
38606         }
38607         this.el.setXY(xy);
38608         this.el.show();
38609         this.hidden = false;
38610         this.focus();
38611         this.fireEvent("show", this);
38612     },
38613
38614     focus : function(){
38615         if(!this.hidden){
38616             this.doFocus.defer(50, this);
38617         }
38618     },
38619
38620     doFocus : function(){
38621         if(!this.hidden){
38622             this.focusEl.focus();
38623         }
38624     },
38625
38626     /**
38627      * Hides this menu and optionally all parent menus
38628      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38629      */
38630     hide : function(deep){
38631         if(this.el && this.isVisible()){
38632             this.fireEvent("beforehide", this);
38633             if(this.activeItem){
38634                 this.activeItem.deactivate();
38635                 this.activeItem = null;
38636             }
38637             this.el.hide();
38638             this.hidden = true;
38639             this.fireEvent("hide", this);
38640         }
38641         if(deep === true && this.parentMenu){
38642             this.parentMenu.hide(true);
38643         }
38644     },
38645
38646     /**
38647      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38648      * Any of the following are valid:
38649      * <ul>
38650      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38651      * <li>An HTMLElement object which will be converted to a menu item</li>
38652      * <li>A menu item config object that will be created as a new menu item</li>
38653      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38654      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38655      * </ul>
38656      * Usage:
38657      * <pre><code>
38658 // Create the menu
38659 var menu = new Roo.menu.Menu();
38660
38661 // Create a menu item to add by reference
38662 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38663
38664 // Add a bunch of items at once using different methods.
38665 // Only the last item added will be returned.
38666 var item = menu.add(
38667     menuItem,                // add existing item by ref
38668     'Dynamic Item',          // new TextItem
38669     '-',                     // new separator
38670     { text: 'Config Item' }  // new item by config
38671 );
38672 </code></pre>
38673      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38674      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38675      */
38676     add : function(){
38677         var a = arguments, l = a.length, item;
38678         for(var i = 0; i < l; i++){
38679             var el = a[i];
38680             if ((typeof(el) == "object") && el.xtype && el.xns) {
38681                 el = Roo.factory(el, Roo.menu);
38682             }
38683             
38684             if(el.render){ // some kind of Item
38685                 item = this.addItem(el);
38686             }else if(typeof el == "string"){ // string
38687                 if(el == "separator" || el == "-"){
38688                     item = this.addSeparator();
38689                 }else{
38690                     item = this.addText(el);
38691                 }
38692             }else if(el.tagName || el.el){ // element
38693                 item = this.addElement(el);
38694             }else if(typeof el == "object"){ // must be menu item config?
38695                 item = this.addMenuItem(el);
38696             }
38697         }
38698         return item;
38699     },
38700
38701     /**
38702      * Returns this menu's underlying {@link Roo.Element} object
38703      * @return {Roo.Element} The element
38704      */
38705     getEl : function(){
38706         if(!this.el){
38707             this.render();
38708         }
38709         return this.el;
38710     },
38711
38712     /**
38713      * Adds a separator bar to the menu
38714      * @return {Roo.menu.Item} The menu item that was added
38715      */
38716     addSeparator : function(){
38717         return this.addItem(new Roo.menu.Separator());
38718     },
38719
38720     /**
38721      * Adds an {@link Roo.Element} object to the menu
38722      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38723      * @return {Roo.menu.Item} The menu item that was added
38724      */
38725     addElement : function(el){
38726         return this.addItem(new Roo.menu.BaseItem(el));
38727     },
38728
38729     /**
38730      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38731      * @param {Roo.menu.Item} item The menu item to add
38732      * @return {Roo.menu.Item} The menu item that was added
38733      */
38734     addItem : function(item){
38735         this.items.add(item);
38736         if(this.ul){
38737             var li = document.createElement("li");
38738             li.className = "x-menu-list-item";
38739             this.ul.dom.appendChild(li);
38740             item.render(li, this);
38741             this.delayAutoWidth();
38742         }
38743         return item;
38744     },
38745
38746     /**
38747      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38748      * @param {Object} config A MenuItem config object
38749      * @return {Roo.menu.Item} The menu item that was added
38750      */
38751     addMenuItem : function(config){
38752         if(!(config instanceof Roo.menu.Item)){
38753             if(typeof config.checked == "boolean"){ // must be check menu item config?
38754                 config = new Roo.menu.CheckItem(config);
38755             }else{
38756                 config = new Roo.menu.Item(config);
38757             }
38758         }
38759         return this.addItem(config);
38760     },
38761
38762     /**
38763      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38764      * @param {String} text The text to display in the menu item
38765      * @return {Roo.menu.Item} The menu item that was added
38766      */
38767     addText : function(text){
38768         return this.addItem(new Roo.menu.TextItem({ text : text }));
38769     },
38770
38771     /**
38772      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38773      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38774      * @param {Roo.menu.Item} item The menu item to add
38775      * @return {Roo.menu.Item} The menu item that was added
38776      */
38777     insert : function(index, item){
38778         this.items.insert(index, item);
38779         if(this.ul){
38780             var li = document.createElement("li");
38781             li.className = "x-menu-list-item";
38782             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38783             item.render(li, this);
38784             this.delayAutoWidth();
38785         }
38786         return item;
38787     },
38788
38789     /**
38790      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38791      * @param {Roo.menu.Item} item The menu item to remove
38792      */
38793     remove : function(item){
38794         this.items.removeKey(item.id);
38795         item.destroy();
38796     },
38797
38798     /**
38799      * Removes and destroys all items in the menu
38800      */
38801     removeAll : function(){
38802         var f;
38803         while(f = this.items.first()){
38804             this.remove(f);
38805         }
38806     }
38807 });
38808
38809 // MenuNav is a private utility class used internally by the Menu
38810 Roo.menu.MenuNav = function(menu){
38811     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38812     this.scope = this.menu = menu;
38813 };
38814
38815 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38816     doRelay : function(e, h){
38817         var k = e.getKey();
38818         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38819             this.menu.tryActivate(0, 1);
38820             return false;
38821         }
38822         return h.call(this.scope || this, e, this.menu);
38823     },
38824
38825     up : function(e, m){
38826         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38827             m.tryActivate(m.items.length-1, -1);
38828         }
38829     },
38830
38831     down : function(e, m){
38832         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38833             m.tryActivate(0, 1);
38834         }
38835     },
38836
38837     right : function(e, m){
38838         if(m.activeItem){
38839             m.activeItem.expandMenu(true);
38840         }
38841     },
38842
38843     left : function(e, m){
38844         m.hide();
38845         if(m.parentMenu && m.parentMenu.activeItem){
38846             m.parentMenu.activeItem.activate();
38847         }
38848     },
38849
38850     enter : function(e, m){
38851         if(m.activeItem){
38852             e.stopPropagation();
38853             m.activeItem.onClick(e);
38854             m.fireEvent("click", this, m.activeItem);
38855             return true;
38856         }
38857     }
38858 });/*
38859  * Based on:
38860  * Ext JS Library 1.1.1
38861  * Copyright(c) 2006-2007, Ext JS, LLC.
38862  *
38863  * Originally Released Under LGPL - original licence link has changed is not relivant.
38864  *
38865  * Fork - LGPL
38866  * <script type="text/javascript">
38867  */
38868  
38869 /**
38870  * @class Roo.menu.MenuMgr
38871  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38872  * @static
38873  */
38874 Roo.menu.MenuMgr = function(){
38875    var menus, active, groups = {}, attached = false, lastShow = new Date();
38876
38877    // private - called when first menu is created
38878    function init(){
38879        menus = {};
38880        active = new Roo.util.MixedCollection();
38881        Roo.get(document).addKeyListener(27, function(){
38882            if(active.length > 0){
38883                hideAll();
38884            }
38885        });
38886    }
38887
38888    // private
38889    function hideAll(){
38890        if(active && active.length > 0){
38891            var c = active.clone();
38892            c.each(function(m){
38893                m.hide();
38894            });
38895        }
38896    }
38897
38898    // private
38899    function onHide(m){
38900        active.remove(m);
38901        if(active.length < 1){
38902            Roo.get(document).un("mousedown", onMouseDown);
38903            attached = false;
38904        }
38905    }
38906
38907    // private
38908    function onShow(m){
38909        var last = active.last();
38910        lastShow = new Date();
38911        active.add(m);
38912        if(!attached){
38913            Roo.get(document).on("mousedown", onMouseDown);
38914            attached = true;
38915        }
38916        if(m.parentMenu){
38917           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38918           m.parentMenu.activeChild = m;
38919        }else if(last && last.isVisible()){
38920           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38921        }
38922    }
38923
38924    // private
38925    function onBeforeHide(m){
38926        if(m.activeChild){
38927            m.activeChild.hide();
38928        }
38929        if(m.autoHideTimer){
38930            clearTimeout(m.autoHideTimer);
38931            delete m.autoHideTimer;
38932        }
38933    }
38934
38935    // private
38936    function onBeforeShow(m){
38937        var pm = m.parentMenu;
38938        if(!pm && !m.allowOtherMenus){
38939            hideAll();
38940        }else if(pm && pm.activeChild && active != m){
38941            pm.activeChild.hide();
38942        }
38943    }
38944
38945    // private
38946    function onMouseDown(e){
38947        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
38948            hideAll();
38949        }
38950    }
38951
38952    // private
38953    function onBeforeCheck(mi, state){
38954        if(state){
38955            var g = groups[mi.group];
38956            for(var i = 0, l = g.length; i < l; i++){
38957                if(g[i] != mi){
38958                    g[i].setChecked(false);
38959                }
38960            }
38961        }
38962    }
38963
38964    return {
38965
38966        /**
38967         * Hides all menus that are currently visible
38968         */
38969        hideAll : function(){
38970             hideAll();  
38971        },
38972
38973        // private
38974        register : function(menu){
38975            if(!menus){
38976                init();
38977            }
38978            menus[menu.id] = menu;
38979            menu.on("beforehide", onBeforeHide);
38980            menu.on("hide", onHide);
38981            menu.on("beforeshow", onBeforeShow);
38982            menu.on("show", onShow);
38983            var g = menu.group;
38984            if(g && menu.events["checkchange"]){
38985                if(!groups[g]){
38986                    groups[g] = [];
38987                }
38988                groups[g].push(menu);
38989                menu.on("checkchange", onCheck);
38990            }
38991        },
38992
38993         /**
38994          * Returns a {@link Roo.menu.Menu} object
38995          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
38996          * be used to generate and return a new Menu instance.
38997          */
38998        get : function(menu){
38999            if(typeof menu == "string"){ // menu id
39000                return menus[menu];
39001            }else if(menu.events){  // menu instance
39002                return menu;
39003            }else if(typeof menu.length == 'number'){ // array of menu items?
39004                return new Roo.menu.Menu({items:menu});
39005            }else{ // otherwise, must be a config
39006                return new Roo.menu.Menu(menu);
39007            }
39008        },
39009
39010        // private
39011        unregister : function(menu){
39012            delete menus[menu.id];
39013            menu.un("beforehide", onBeforeHide);
39014            menu.un("hide", onHide);
39015            menu.un("beforeshow", onBeforeShow);
39016            menu.un("show", onShow);
39017            var g = menu.group;
39018            if(g && menu.events["checkchange"]){
39019                groups[g].remove(menu);
39020                menu.un("checkchange", onCheck);
39021            }
39022        },
39023
39024        // private
39025        registerCheckable : function(menuItem){
39026            var g = menuItem.group;
39027            if(g){
39028                if(!groups[g]){
39029                    groups[g] = [];
39030                }
39031                groups[g].push(menuItem);
39032                menuItem.on("beforecheckchange", onBeforeCheck);
39033            }
39034        },
39035
39036        // private
39037        unregisterCheckable : function(menuItem){
39038            var g = menuItem.group;
39039            if(g){
39040                groups[g].remove(menuItem);
39041                menuItem.un("beforecheckchange", onBeforeCheck);
39042            }
39043        }
39044    };
39045 }();/*
39046  * Based on:
39047  * Ext JS Library 1.1.1
39048  * Copyright(c) 2006-2007, Ext JS, LLC.
39049  *
39050  * Originally Released Under LGPL - original licence link has changed is not relivant.
39051  *
39052  * Fork - LGPL
39053  * <script type="text/javascript">
39054  */
39055  
39056
39057 /**
39058  * @class Roo.menu.BaseItem
39059  * @extends Roo.Component
39060  * @abstract
39061  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39062  * management and base configuration options shared by all menu components.
39063  * @constructor
39064  * Creates a new BaseItem
39065  * @param {Object} config Configuration options
39066  */
39067 Roo.menu.BaseItem = function(config){
39068     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39069
39070     this.addEvents({
39071         /**
39072          * @event click
39073          * Fires when this item is clicked
39074          * @param {Roo.menu.BaseItem} this
39075          * @param {Roo.EventObject} e
39076          */
39077         click: true,
39078         /**
39079          * @event activate
39080          * Fires when this item is activated
39081          * @param {Roo.menu.BaseItem} this
39082          */
39083         activate : true,
39084         /**
39085          * @event deactivate
39086          * Fires when this item is deactivated
39087          * @param {Roo.menu.BaseItem} this
39088          */
39089         deactivate : true
39090     });
39091
39092     if(this.handler){
39093         this.on("click", this.handler, this.scope, true);
39094     }
39095 };
39096
39097 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39098     /**
39099      * @cfg {Function} handler
39100      * A function that will handle the click event of this menu item (defaults to undefined)
39101      */
39102     /**
39103      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39104      */
39105     canActivate : false,
39106     
39107      /**
39108      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39109      */
39110     hidden: false,
39111     
39112     /**
39113      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39114      */
39115     activeClass : "x-menu-item-active",
39116     /**
39117      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39118      */
39119     hideOnClick : true,
39120     /**
39121      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39122      */
39123     hideDelay : 100,
39124
39125     // private
39126     ctype: "Roo.menu.BaseItem",
39127
39128     // private
39129     actionMode : "container",
39130
39131     // private
39132     render : function(container, parentMenu){
39133         this.parentMenu = parentMenu;
39134         Roo.menu.BaseItem.superclass.render.call(this, container);
39135         this.container.menuItemId = this.id;
39136     },
39137
39138     // private
39139     onRender : function(container, position){
39140         this.el = Roo.get(this.el);
39141         container.dom.appendChild(this.el.dom);
39142     },
39143
39144     // private
39145     onClick : function(e){
39146         if(!this.disabled && this.fireEvent("click", this, e) !== false
39147                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39148             this.handleClick(e);
39149         }else{
39150             e.stopEvent();
39151         }
39152     },
39153
39154     // private
39155     activate : function(){
39156         if(this.disabled){
39157             return false;
39158         }
39159         var li = this.container;
39160         li.addClass(this.activeClass);
39161         this.region = li.getRegion().adjust(2, 2, -2, -2);
39162         this.fireEvent("activate", this);
39163         return true;
39164     },
39165
39166     // private
39167     deactivate : function(){
39168         this.container.removeClass(this.activeClass);
39169         this.fireEvent("deactivate", this);
39170     },
39171
39172     // private
39173     shouldDeactivate : function(e){
39174         return !this.region || !this.region.contains(e.getPoint());
39175     },
39176
39177     // private
39178     handleClick : function(e){
39179         if(this.hideOnClick){
39180             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39181         }
39182     },
39183
39184     // private
39185     expandMenu : function(autoActivate){
39186         // do nothing
39187     },
39188
39189     // private
39190     hideMenu : function(){
39191         // do nothing
39192     }
39193 });/*
39194  * Based on:
39195  * Ext JS Library 1.1.1
39196  * Copyright(c) 2006-2007, Ext JS, LLC.
39197  *
39198  * Originally Released Under LGPL - original licence link has changed is not relivant.
39199  *
39200  * Fork - LGPL
39201  * <script type="text/javascript">
39202  */
39203  
39204 /**
39205  * @class Roo.menu.Adapter
39206  * @extends Roo.menu.BaseItem
39207  * @abstract
39208  * 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.
39209  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39210  * @constructor
39211  * Creates a new Adapter
39212  * @param {Object} config Configuration options
39213  */
39214 Roo.menu.Adapter = function(component, config){
39215     Roo.menu.Adapter.superclass.constructor.call(this, config);
39216     this.component = component;
39217 };
39218 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39219     // private
39220     canActivate : true,
39221
39222     // private
39223     onRender : function(container, position){
39224         this.component.render(container);
39225         this.el = this.component.getEl();
39226     },
39227
39228     // private
39229     activate : function(){
39230         if(this.disabled){
39231             return false;
39232         }
39233         this.component.focus();
39234         this.fireEvent("activate", this);
39235         return true;
39236     },
39237
39238     // private
39239     deactivate : function(){
39240         this.fireEvent("deactivate", this);
39241     },
39242
39243     // private
39244     disable : function(){
39245         this.component.disable();
39246         Roo.menu.Adapter.superclass.disable.call(this);
39247     },
39248
39249     // private
39250     enable : function(){
39251         this.component.enable();
39252         Roo.menu.Adapter.superclass.enable.call(this);
39253     }
39254 });/*
39255  * Based on:
39256  * Ext JS Library 1.1.1
39257  * Copyright(c) 2006-2007, Ext JS, LLC.
39258  *
39259  * Originally Released Under LGPL - original licence link has changed is not relivant.
39260  *
39261  * Fork - LGPL
39262  * <script type="text/javascript">
39263  */
39264
39265 /**
39266  * @class Roo.menu.TextItem
39267  * @extends Roo.menu.BaseItem
39268  * Adds a static text string to a menu, usually used as either a heading or group separator.
39269  * Note: old style constructor with text is still supported.
39270  * 
39271  * @constructor
39272  * Creates a new TextItem
39273  * @param {Object} cfg Configuration
39274  */
39275 Roo.menu.TextItem = function(cfg){
39276     if (typeof(cfg) == 'string') {
39277         this.text = cfg;
39278     } else {
39279         Roo.apply(this,cfg);
39280     }
39281     
39282     Roo.menu.TextItem.superclass.constructor.call(this);
39283 };
39284
39285 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39286     /**
39287      * @cfg {String} text Text to show on item.
39288      */
39289     text : '',
39290     
39291     /**
39292      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39293      */
39294     hideOnClick : false,
39295     /**
39296      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39297      */
39298     itemCls : "x-menu-text",
39299
39300     // private
39301     onRender : function(){
39302         var s = document.createElement("span");
39303         s.className = this.itemCls;
39304         s.innerHTML = this.text;
39305         this.el = s;
39306         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39307     }
39308 });/*
39309  * Based on:
39310  * Ext JS Library 1.1.1
39311  * Copyright(c) 2006-2007, Ext JS, LLC.
39312  *
39313  * Originally Released Under LGPL - original licence link has changed is not relivant.
39314  *
39315  * Fork - LGPL
39316  * <script type="text/javascript">
39317  */
39318
39319 /**
39320  * @class Roo.menu.Separator
39321  * @extends Roo.menu.BaseItem
39322  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39323  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39324  * @constructor
39325  * @param {Object} config Configuration options
39326  */
39327 Roo.menu.Separator = function(config){
39328     Roo.menu.Separator.superclass.constructor.call(this, config);
39329 };
39330
39331 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39332     /**
39333      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39334      */
39335     itemCls : "x-menu-sep",
39336     /**
39337      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39338      */
39339     hideOnClick : false,
39340
39341     // private
39342     onRender : function(li){
39343         var s = document.createElement("span");
39344         s.className = this.itemCls;
39345         s.innerHTML = "&#160;";
39346         this.el = s;
39347         li.addClass("x-menu-sep-li");
39348         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39349     }
39350 });/*
39351  * Based on:
39352  * Ext JS Library 1.1.1
39353  * Copyright(c) 2006-2007, Ext JS, LLC.
39354  *
39355  * Originally Released Under LGPL - original licence link has changed is not relivant.
39356  *
39357  * Fork - LGPL
39358  * <script type="text/javascript">
39359  */
39360 /**
39361  * @class Roo.menu.Item
39362  * @extends Roo.menu.BaseItem
39363  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39364  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39365  * activation and click handling.
39366  * @constructor
39367  * Creates a new Item
39368  * @param {Object} config Configuration options
39369  */
39370 Roo.menu.Item = function(config){
39371     Roo.menu.Item.superclass.constructor.call(this, config);
39372     if(this.menu){
39373         this.menu = Roo.menu.MenuMgr.get(this.menu);
39374     }
39375 };
39376 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39377     /**
39378      * @cfg {Roo.menu.Menu} menu
39379      * A Sub menu
39380      */
39381     /**
39382      * @cfg {String} text
39383      * The text to show on the menu item.
39384      */
39385     text: '',
39386      /**
39387      * @cfg {String} HTML to render in menu
39388      * The text to show on the menu item (HTML version).
39389      */
39390     html: '',
39391     /**
39392      * @cfg {String} icon
39393      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39394      */
39395     icon: undefined,
39396     /**
39397      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39398      */
39399     itemCls : "x-menu-item",
39400     /**
39401      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39402      */
39403     canActivate : true,
39404     /**
39405      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39406      */
39407     showDelay: 200,
39408     // doc'd in BaseItem
39409     hideDelay: 200,
39410
39411     // private
39412     ctype: "Roo.menu.Item",
39413     
39414     // private
39415     onRender : function(container, position){
39416         var el = document.createElement("a");
39417         el.hideFocus = true;
39418         el.unselectable = "on";
39419         el.href = this.href || "#";
39420         if(this.hrefTarget){
39421             el.target = this.hrefTarget;
39422         }
39423         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39424         
39425         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39426         
39427         el.innerHTML = String.format(
39428                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39429                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39430         this.el = el;
39431         Roo.menu.Item.superclass.onRender.call(this, container, position);
39432     },
39433
39434     /**
39435      * Sets the text to display in this menu item
39436      * @param {String} text The text to display
39437      * @param {Boolean} isHTML true to indicate text is pure html.
39438      */
39439     setText : function(text, isHTML){
39440         if (isHTML) {
39441             this.html = text;
39442         } else {
39443             this.text = text;
39444             this.html = '';
39445         }
39446         if(this.rendered){
39447             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39448      
39449             this.el.update(String.format(
39450                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39451                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39452             this.parentMenu.autoWidth();
39453         }
39454     },
39455
39456     // private
39457     handleClick : function(e){
39458         if(!this.href){ // if no link defined, stop the event automatically
39459             e.stopEvent();
39460         }
39461         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39462     },
39463
39464     // private
39465     activate : function(autoExpand){
39466         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39467             this.focus();
39468             if(autoExpand){
39469                 this.expandMenu();
39470             }
39471         }
39472         return true;
39473     },
39474
39475     // private
39476     shouldDeactivate : function(e){
39477         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39478             if(this.menu && this.menu.isVisible()){
39479                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39480             }
39481             return true;
39482         }
39483         return false;
39484     },
39485
39486     // private
39487     deactivate : function(){
39488         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39489         this.hideMenu();
39490     },
39491
39492     // private
39493     expandMenu : function(autoActivate){
39494         if(!this.disabled && this.menu){
39495             clearTimeout(this.hideTimer);
39496             delete this.hideTimer;
39497             if(!this.menu.isVisible() && !this.showTimer){
39498                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39499             }else if (this.menu.isVisible() && autoActivate){
39500                 this.menu.tryActivate(0, 1);
39501             }
39502         }
39503     },
39504
39505     // private
39506     deferExpand : function(autoActivate){
39507         delete this.showTimer;
39508         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39509         if(autoActivate){
39510             this.menu.tryActivate(0, 1);
39511         }
39512     },
39513
39514     // private
39515     hideMenu : function(){
39516         clearTimeout(this.showTimer);
39517         delete this.showTimer;
39518         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39519             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39520         }
39521     },
39522
39523     // private
39524     deferHide : function(){
39525         delete this.hideTimer;
39526         this.menu.hide();
39527     }
39528 });/*
39529  * Based on:
39530  * Ext JS Library 1.1.1
39531  * Copyright(c) 2006-2007, Ext JS, LLC.
39532  *
39533  * Originally Released Under LGPL - original licence link has changed is not relivant.
39534  *
39535  * Fork - LGPL
39536  * <script type="text/javascript">
39537  */
39538  
39539 /**
39540  * @class Roo.menu.CheckItem
39541  * @extends Roo.menu.Item
39542  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39543  * @constructor
39544  * Creates a new CheckItem
39545  * @param {Object} config Configuration options
39546  */
39547 Roo.menu.CheckItem = function(config){
39548     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39549     this.addEvents({
39550         /**
39551          * @event beforecheckchange
39552          * Fires before the checked value is set, providing an opportunity to cancel if needed
39553          * @param {Roo.menu.CheckItem} this
39554          * @param {Boolean} checked The new checked value that will be set
39555          */
39556         "beforecheckchange" : true,
39557         /**
39558          * @event checkchange
39559          * Fires after the checked value has been set
39560          * @param {Roo.menu.CheckItem} this
39561          * @param {Boolean} checked The checked value that was set
39562          */
39563         "checkchange" : true
39564     });
39565     if(this.checkHandler){
39566         this.on('checkchange', this.checkHandler, this.scope);
39567     }
39568 };
39569 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39570     /**
39571      * @cfg {String} group
39572      * All check items with the same group name will automatically be grouped into a single-select
39573      * radio button group (defaults to '')
39574      */
39575     /**
39576      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39577      */
39578     itemCls : "x-menu-item x-menu-check-item",
39579     /**
39580      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39581      */
39582     groupClass : "x-menu-group-item",
39583
39584     /**
39585      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39586      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39587      * initialized with checked = true will be rendered as checked.
39588      */
39589     checked: false,
39590
39591     // private
39592     ctype: "Roo.menu.CheckItem",
39593
39594     // private
39595     onRender : function(c){
39596         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39597         if(this.group){
39598             this.el.addClass(this.groupClass);
39599         }
39600         Roo.menu.MenuMgr.registerCheckable(this);
39601         if(this.checked){
39602             this.checked = false;
39603             this.setChecked(true, true);
39604         }
39605     },
39606
39607     // private
39608     destroy : function(){
39609         if(this.rendered){
39610             Roo.menu.MenuMgr.unregisterCheckable(this);
39611         }
39612         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39613     },
39614
39615     /**
39616      * Set the checked state of this item
39617      * @param {Boolean} checked The new checked value
39618      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39619      */
39620     setChecked : function(state, suppressEvent){
39621         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39622             if(this.container){
39623                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39624             }
39625             this.checked = state;
39626             if(suppressEvent !== true){
39627                 this.fireEvent("checkchange", this, state);
39628             }
39629         }
39630     },
39631
39632     // private
39633     handleClick : function(e){
39634        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39635            this.setChecked(!this.checked);
39636        }
39637        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39638     }
39639 });/*
39640  * Based on:
39641  * Ext JS Library 1.1.1
39642  * Copyright(c) 2006-2007, Ext JS, LLC.
39643  *
39644  * Originally Released Under LGPL - original licence link has changed is not relivant.
39645  *
39646  * Fork - LGPL
39647  * <script type="text/javascript">
39648  */
39649  
39650 /**
39651  * @class Roo.menu.DateItem
39652  * @extends Roo.menu.Adapter
39653  * A menu item that wraps the {@link Roo.DatPicker} component.
39654  * @constructor
39655  * Creates a new DateItem
39656  * @param {Object} config Configuration options
39657  */
39658 Roo.menu.DateItem = function(config){
39659     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39660     /** The Roo.DatePicker object @type Roo.DatePicker */
39661     this.picker = this.component;
39662     this.addEvents({select: true});
39663     
39664     this.picker.on("render", function(picker){
39665         picker.getEl().swallowEvent("click");
39666         picker.container.addClass("x-menu-date-item");
39667     });
39668
39669     this.picker.on("select", this.onSelect, this);
39670 };
39671
39672 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39673     // private
39674     onSelect : function(picker, date){
39675         this.fireEvent("select", this, date, picker);
39676         Roo.menu.DateItem.superclass.handleClick.call(this);
39677     }
39678 });/*
39679  * Based on:
39680  * Ext JS Library 1.1.1
39681  * Copyright(c) 2006-2007, Ext JS, LLC.
39682  *
39683  * Originally Released Under LGPL - original licence link has changed is not relivant.
39684  *
39685  * Fork - LGPL
39686  * <script type="text/javascript">
39687  */
39688  
39689 /**
39690  * @class Roo.menu.ColorItem
39691  * @extends Roo.menu.Adapter
39692  * A menu item that wraps the {@link Roo.ColorPalette} component.
39693  * @constructor
39694  * Creates a new ColorItem
39695  * @param {Object} config Configuration options
39696  */
39697 Roo.menu.ColorItem = function(config){
39698     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39699     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39700     this.palette = this.component;
39701     this.relayEvents(this.palette, ["select"]);
39702     if(this.selectHandler){
39703         this.on('select', this.selectHandler, this.scope);
39704     }
39705 };
39706 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39707  * Based on:
39708  * Ext JS Library 1.1.1
39709  * Copyright(c) 2006-2007, Ext JS, LLC.
39710  *
39711  * Originally Released Under LGPL - original licence link has changed is not relivant.
39712  *
39713  * Fork - LGPL
39714  * <script type="text/javascript">
39715  */
39716  
39717
39718 /**
39719  * @class Roo.menu.DateMenu
39720  * @extends Roo.menu.Menu
39721  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39722  * @constructor
39723  * Creates a new DateMenu
39724  * @param {Object} config Configuration options
39725  */
39726 Roo.menu.DateMenu = function(config){
39727     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39728     this.plain = true;
39729     var di = new Roo.menu.DateItem(config);
39730     this.add(di);
39731     /**
39732      * The {@link Roo.DatePicker} instance for this DateMenu
39733      * @type DatePicker
39734      */
39735     this.picker = di.picker;
39736     /**
39737      * @event select
39738      * @param {DatePicker} picker
39739      * @param {Date} date
39740      */
39741     this.relayEvents(di, ["select"]);
39742     this.on('beforeshow', function(){
39743         if(this.picker){
39744             this.picker.hideMonthPicker(false);
39745         }
39746     }, this);
39747 };
39748 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39749     cls:'x-date-menu'
39750 });/*
39751  * Based on:
39752  * Ext JS Library 1.1.1
39753  * Copyright(c) 2006-2007, Ext JS, LLC.
39754  *
39755  * Originally Released Under LGPL - original licence link has changed is not relivant.
39756  *
39757  * Fork - LGPL
39758  * <script type="text/javascript">
39759  */
39760  
39761
39762 /**
39763  * @class Roo.menu.ColorMenu
39764  * @extends Roo.menu.Menu
39765  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39766  * @constructor
39767  * Creates a new ColorMenu
39768  * @param {Object} config Configuration options
39769  */
39770 Roo.menu.ColorMenu = function(config){
39771     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39772     this.plain = true;
39773     var ci = new Roo.menu.ColorItem(config);
39774     this.add(ci);
39775     /**
39776      * The {@link Roo.ColorPalette} instance for this ColorMenu
39777      * @type ColorPalette
39778      */
39779     this.palette = ci.palette;
39780     /**
39781      * @event select
39782      * @param {ColorPalette} palette
39783      * @param {String} color
39784      */
39785     this.relayEvents(ci, ["select"]);
39786 };
39787 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39788  * Based on:
39789  * Ext JS Library 1.1.1
39790  * Copyright(c) 2006-2007, Ext JS, LLC.
39791  *
39792  * Originally Released Under LGPL - original licence link has changed is not relivant.
39793  *
39794  * Fork - LGPL
39795  * <script type="text/javascript">
39796  */
39797  
39798 /**
39799  * @class Roo.form.TextItem
39800  * @extends Roo.BoxComponent
39801  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39802  * @constructor
39803  * Creates a new TextItem
39804  * @param {Object} config Configuration options
39805  */
39806 Roo.form.TextItem = function(config){
39807     Roo.form.TextItem.superclass.constructor.call(this, config);
39808 };
39809
39810 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39811     
39812     /**
39813      * @cfg {String} tag the tag for this item (default div)
39814      */
39815     tag : 'div',
39816     /**
39817      * @cfg {String} html the content for this item
39818      */
39819     html : '',
39820     
39821     getAutoCreate : function()
39822     {
39823         var cfg = {
39824             id: this.id,
39825             tag: this.tag,
39826             html: this.html,
39827             cls: 'x-form-item'
39828         };
39829         
39830         return cfg;
39831         
39832     },
39833     
39834     onRender : function(ct, position)
39835     {
39836         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39837         
39838         if(!this.el){
39839             var cfg = this.getAutoCreate();
39840             if(!cfg.name){
39841                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39842             }
39843             if (!cfg.name.length) {
39844                 delete cfg.name;
39845             }
39846             this.el = ct.createChild(cfg, position);
39847         }
39848     },
39849     /*
39850      * setHTML
39851      * @param {String} html update the Contents of the element.
39852      */
39853     setHTML : function(html)
39854     {
39855         this.fieldEl.dom.innerHTML = html;
39856     }
39857     
39858 });/*
39859  * Based on:
39860  * Ext JS Library 1.1.1
39861  * Copyright(c) 2006-2007, Ext JS, LLC.
39862  *
39863  * Originally Released Under LGPL - original licence link has changed is not relivant.
39864  *
39865  * Fork - LGPL
39866  * <script type="text/javascript">
39867  */
39868  
39869 /**
39870  * @class Roo.form.Field
39871  * @extends Roo.BoxComponent
39872  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39873  * @constructor
39874  * Creates a new Field
39875  * @param {Object} config Configuration options
39876  */
39877 Roo.form.Field = function(config){
39878     Roo.form.Field.superclass.constructor.call(this, config);
39879 };
39880
39881 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39882     /**
39883      * @cfg {String} fieldLabel Label to use when rendering a form.
39884      */
39885        /**
39886      * @cfg {String} qtip Mouse over tip
39887      */
39888      
39889     /**
39890      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39891      */
39892     invalidClass : "x-form-invalid",
39893     /**
39894      * @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")
39895      */
39896     invalidText : "The value in this field is invalid",
39897     /**
39898      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39899      */
39900     focusClass : "x-form-focus",
39901     /**
39902      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39903       automatic validation (defaults to "keyup").
39904      */
39905     validationEvent : "keyup",
39906     /**
39907      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39908      */
39909     validateOnBlur : true,
39910     /**
39911      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39912      */
39913     validationDelay : 250,
39914     /**
39915      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39916      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39917      */
39918     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39919     /**
39920      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39921      */
39922     fieldClass : "x-form-field",
39923     /**
39924      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
39925      *<pre>
39926 Value         Description
39927 -----------   ----------------------------------------------------------------------
39928 qtip          Display a quick tip when the user hovers over the field
39929 title         Display a default browser title attribute popup
39930 under         Add a block div beneath the field containing the error text
39931 side          Add an error icon to the right of the field with a popup on hover
39932 [element id]  Add the error text directly to the innerHTML of the specified element
39933 </pre>
39934      */
39935     msgTarget : 'qtip',
39936     /**
39937      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
39938      */
39939     msgFx : 'normal',
39940
39941     /**
39942      * @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.
39943      */
39944     readOnly : false,
39945
39946     /**
39947      * @cfg {Boolean} disabled True to disable the field (defaults to false).
39948      */
39949     disabled : false,
39950
39951     /**
39952      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
39953      */
39954     inputType : undefined,
39955     
39956     /**
39957      * @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).
39958          */
39959         tabIndex : undefined,
39960         
39961     // private
39962     isFormField : true,
39963
39964     // private
39965     hasFocus : false,
39966     /**
39967      * @property {Roo.Element} fieldEl
39968      * Element Containing the rendered Field (with label etc.)
39969      */
39970     /**
39971      * @cfg {Mixed} value A value to initialize this field with.
39972      */
39973     value : undefined,
39974
39975     /**
39976      * @cfg {String} name The field's HTML name attribute.
39977      */
39978     /**
39979      * @cfg {String} cls A CSS class to apply to the field's underlying element.
39980      */
39981     // private
39982     loadedValue : false,
39983      
39984      
39985         // private ??
39986         initComponent : function(){
39987         Roo.form.Field.superclass.initComponent.call(this);
39988         this.addEvents({
39989             /**
39990              * @event focus
39991              * Fires when this field receives input focus.
39992              * @param {Roo.form.Field} this
39993              */
39994             focus : true,
39995             /**
39996              * @event blur
39997              * Fires when this field loses input focus.
39998              * @param {Roo.form.Field} this
39999              */
40000             blur : true,
40001             /**
40002              * @event specialkey
40003              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40004              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40005              * @param {Roo.form.Field} this
40006              * @param {Roo.EventObject} e The event object
40007              */
40008             specialkey : true,
40009             /**
40010              * @event change
40011              * Fires just before the field blurs if the field value has changed.
40012              * @param {Roo.form.Field} this
40013              * @param {Mixed} newValue The new value
40014              * @param {Mixed} oldValue The original value
40015              */
40016             change : true,
40017             /**
40018              * @event invalid
40019              * Fires after the field has been marked as invalid.
40020              * @param {Roo.form.Field} this
40021              * @param {String} msg The validation message
40022              */
40023             invalid : true,
40024             /**
40025              * @event valid
40026              * Fires after the field has been validated with no errors.
40027              * @param {Roo.form.Field} this
40028              */
40029             valid : true,
40030              /**
40031              * @event keyup
40032              * Fires after the key up
40033              * @param {Roo.form.Field} this
40034              * @param {Roo.EventObject}  e The event Object
40035              */
40036             keyup : true
40037         });
40038     },
40039
40040     /**
40041      * Returns the name attribute of the field if available
40042      * @return {String} name The field name
40043      */
40044     getName: function(){
40045          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40046     },
40047
40048     // private
40049     onRender : function(ct, position){
40050         Roo.form.Field.superclass.onRender.call(this, ct, position);
40051         if(!this.el){
40052             var cfg = this.getAutoCreate();
40053             if(!cfg.name){
40054                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40055             }
40056             if (!cfg.name.length) {
40057                 delete cfg.name;
40058             }
40059             if(this.inputType){
40060                 cfg.type = this.inputType;
40061             }
40062             this.el = ct.createChild(cfg, position);
40063         }
40064         var type = this.el.dom.type;
40065         if(type){
40066             if(type == 'password'){
40067                 type = 'text';
40068             }
40069             this.el.addClass('x-form-'+type);
40070         }
40071         if(this.readOnly){
40072             this.el.dom.readOnly = true;
40073         }
40074         if(this.tabIndex !== undefined){
40075             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40076         }
40077
40078         this.el.addClass([this.fieldClass, this.cls]);
40079         this.initValue();
40080     },
40081
40082     /**
40083      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40084      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40085      * @return {Roo.form.Field} this
40086      */
40087     applyTo : function(target){
40088         this.allowDomMove = false;
40089         this.el = Roo.get(target);
40090         this.render(this.el.dom.parentNode);
40091         return this;
40092     },
40093
40094     // private
40095     initValue : function(){
40096         if(this.value !== undefined){
40097             this.setValue(this.value);
40098         }else if(this.el.dom.value.length > 0){
40099             this.setValue(this.el.dom.value);
40100         }
40101     },
40102
40103     /**
40104      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40105      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40106      */
40107     isDirty : function() {
40108         if(this.disabled) {
40109             return false;
40110         }
40111         return String(this.getValue()) !== String(this.originalValue);
40112     },
40113
40114     /**
40115      * stores the current value in loadedValue
40116      */
40117     resetHasChanged : function()
40118     {
40119         this.loadedValue = String(this.getValue());
40120     },
40121     /**
40122      * checks the current value against the 'loaded' value.
40123      * Note - will return false if 'resetHasChanged' has not been called first.
40124      */
40125     hasChanged : function()
40126     {
40127         if(this.disabled || this.readOnly) {
40128             return false;
40129         }
40130         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40131     },
40132     
40133     
40134     
40135     // private
40136     afterRender : function(){
40137         Roo.form.Field.superclass.afterRender.call(this);
40138         this.initEvents();
40139     },
40140
40141     // private
40142     fireKey : function(e){
40143         //Roo.log('field ' + e.getKey());
40144         if(e.isNavKeyPress()){
40145             this.fireEvent("specialkey", this, e);
40146         }
40147     },
40148
40149     /**
40150      * Resets the current field value to the originally loaded value and clears any validation messages
40151      */
40152     reset : function(){
40153         this.setValue(this.resetValue);
40154         this.originalValue = this.getValue();
40155         this.clearInvalid();
40156     },
40157
40158     // private
40159     initEvents : function(){
40160         // safari killled keypress - so keydown is now used..
40161         this.el.on("keydown" , this.fireKey,  this);
40162         this.el.on("focus", this.onFocus,  this);
40163         this.el.on("blur", this.onBlur,  this);
40164         this.el.relayEvent('keyup', this);
40165
40166         // reference to original value for reset
40167         this.originalValue = this.getValue();
40168         this.resetValue =  this.getValue();
40169     },
40170
40171     // private
40172     onFocus : function(){
40173         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40174             this.el.addClass(this.focusClass);
40175         }
40176         if(!this.hasFocus){
40177             this.hasFocus = true;
40178             this.startValue = this.getValue();
40179             this.fireEvent("focus", this);
40180         }
40181     },
40182
40183     beforeBlur : Roo.emptyFn,
40184
40185     // private
40186     onBlur : function(){
40187         this.beforeBlur();
40188         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40189             this.el.removeClass(this.focusClass);
40190         }
40191         this.hasFocus = false;
40192         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40193             this.validate();
40194         }
40195         var v = this.getValue();
40196         if(String(v) !== String(this.startValue)){
40197             this.fireEvent('change', this, v, this.startValue);
40198         }
40199         this.fireEvent("blur", this);
40200     },
40201
40202     /**
40203      * Returns whether or not the field value is currently valid
40204      * @param {Boolean} preventMark True to disable marking the field invalid
40205      * @return {Boolean} True if the value is valid, else false
40206      */
40207     isValid : function(preventMark){
40208         if(this.disabled){
40209             return true;
40210         }
40211         var restore = this.preventMark;
40212         this.preventMark = preventMark === true;
40213         var v = this.validateValue(this.processValue(this.getRawValue()));
40214         this.preventMark = restore;
40215         return v;
40216     },
40217
40218     /**
40219      * Validates the field value
40220      * @return {Boolean} True if the value is valid, else false
40221      */
40222     validate : function(){
40223         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40224             this.clearInvalid();
40225             return true;
40226         }
40227         return false;
40228     },
40229
40230     processValue : function(value){
40231         return value;
40232     },
40233
40234     // private
40235     // Subclasses should provide the validation implementation by overriding this
40236     validateValue : function(value){
40237         return true;
40238     },
40239
40240     /**
40241      * Mark this field as invalid
40242      * @param {String} msg The validation message
40243      */
40244     markInvalid : function(msg){
40245         if(!this.rendered || this.preventMark){ // not rendered
40246             return;
40247         }
40248         
40249         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40250         
40251         obj.el.addClass(this.invalidClass);
40252         msg = msg || this.invalidText;
40253         switch(this.msgTarget){
40254             case 'qtip':
40255                 obj.el.dom.qtip = msg;
40256                 obj.el.dom.qclass = 'x-form-invalid-tip';
40257                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40258                     Roo.QuickTips.enable();
40259                 }
40260                 break;
40261             case 'title':
40262                 this.el.dom.title = msg;
40263                 break;
40264             case 'under':
40265                 if(!this.errorEl){
40266                     var elp = this.el.findParent('.x-form-element', 5, true);
40267                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40268                     this.errorEl.setWidth(elp.getWidth(true)-20);
40269                 }
40270                 this.errorEl.update(msg);
40271                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40272                 break;
40273             case 'side':
40274                 if(!this.errorIcon){
40275                     var elp = this.el.findParent('.x-form-element', 5, true);
40276                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40277                 }
40278                 this.alignErrorIcon();
40279                 this.errorIcon.dom.qtip = msg;
40280                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40281                 this.errorIcon.show();
40282                 this.on('resize', this.alignErrorIcon, this);
40283                 break;
40284             default:
40285                 var t = Roo.getDom(this.msgTarget);
40286                 t.innerHTML = msg;
40287                 t.style.display = this.msgDisplay;
40288                 break;
40289         }
40290         this.fireEvent('invalid', this, msg);
40291     },
40292
40293     // private
40294     alignErrorIcon : function(){
40295         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40296     },
40297
40298     /**
40299      * Clear any invalid styles/messages for this field
40300      */
40301     clearInvalid : function(){
40302         if(!this.rendered || this.preventMark){ // not rendered
40303             return;
40304         }
40305         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40306         
40307         obj.el.removeClass(this.invalidClass);
40308         switch(this.msgTarget){
40309             case 'qtip':
40310                 obj.el.dom.qtip = '';
40311                 break;
40312             case 'title':
40313                 this.el.dom.title = '';
40314                 break;
40315             case 'under':
40316                 if(this.errorEl){
40317                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40318                 }
40319                 break;
40320             case 'side':
40321                 if(this.errorIcon){
40322                     this.errorIcon.dom.qtip = '';
40323                     this.errorIcon.hide();
40324                     this.un('resize', this.alignErrorIcon, this);
40325                 }
40326                 break;
40327             default:
40328                 var t = Roo.getDom(this.msgTarget);
40329                 t.innerHTML = '';
40330                 t.style.display = 'none';
40331                 break;
40332         }
40333         this.fireEvent('valid', this);
40334     },
40335
40336     /**
40337      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40338      * @return {Mixed} value The field value
40339      */
40340     getRawValue : function(){
40341         var v = this.el.getValue();
40342         
40343         return v;
40344     },
40345
40346     /**
40347      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40348      * @return {Mixed} value The field value
40349      */
40350     getValue : function(){
40351         var v = this.el.getValue();
40352          
40353         return v;
40354     },
40355
40356     /**
40357      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40358      * @param {Mixed} value The value to set
40359      */
40360     setRawValue : function(v){
40361         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40362     },
40363
40364     /**
40365      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40366      * @param {Mixed} value The value to set
40367      */
40368     setValue : function(v){
40369         this.value = v;
40370         if(this.rendered){
40371             this.el.dom.value = (v === null || v === undefined ? '' : v);
40372              this.validate();
40373         }
40374     },
40375
40376     adjustSize : function(w, h){
40377         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40378         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40379         return s;
40380     },
40381
40382     adjustWidth : function(tag, w){
40383         tag = tag.toLowerCase();
40384         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40385             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40386                 if(tag == 'input'){
40387                     return w + 2;
40388                 }
40389                 if(tag == 'textarea'){
40390                     return w-2;
40391                 }
40392             }else if(Roo.isOpera){
40393                 if(tag == 'input'){
40394                     return w + 2;
40395                 }
40396                 if(tag == 'textarea'){
40397                     return w-2;
40398                 }
40399             }
40400         }
40401         return w;
40402     }
40403 });
40404
40405
40406 // anything other than normal should be considered experimental
40407 Roo.form.Field.msgFx = {
40408     normal : {
40409         show: function(msgEl, f){
40410             msgEl.setDisplayed('block');
40411         },
40412
40413         hide : function(msgEl, f){
40414             msgEl.setDisplayed(false).update('');
40415         }
40416     },
40417
40418     slide : {
40419         show: function(msgEl, f){
40420             msgEl.slideIn('t', {stopFx:true});
40421         },
40422
40423         hide : function(msgEl, f){
40424             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40425         }
40426     },
40427
40428     slideRight : {
40429         show: function(msgEl, f){
40430             msgEl.fixDisplay();
40431             msgEl.alignTo(f.el, 'tl-tr');
40432             msgEl.slideIn('l', {stopFx:true});
40433         },
40434
40435         hide : function(msgEl, f){
40436             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40437         }
40438     }
40439 };/*
40440  * Based on:
40441  * Ext JS Library 1.1.1
40442  * Copyright(c) 2006-2007, Ext JS, LLC.
40443  *
40444  * Originally Released Under LGPL - original licence link has changed is not relivant.
40445  *
40446  * Fork - LGPL
40447  * <script type="text/javascript">
40448  */
40449  
40450
40451 /**
40452  * @class Roo.form.TextField
40453  * @extends Roo.form.Field
40454  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40455  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40456  * @constructor
40457  * Creates a new TextField
40458  * @param {Object} config Configuration options
40459  */
40460 Roo.form.TextField = function(config){
40461     Roo.form.TextField.superclass.constructor.call(this, config);
40462     this.addEvents({
40463         /**
40464          * @event autosize
40465          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40466          * according to the default logic, but this event provides a hook for the developer to apply additional
40467          * logic at runtime to resize the field if needed.
40468              * @param {Roo.form.Field} this This text field
40469              * @param {Number} width The new field width
40470              */
40471         autosize : true
40472     });
40473 };
40474
40475 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40476     /**
40477      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40478      */
40479     grow : false,
40480     /**
40481      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40482      */
40483     growMin : 30,
40484     /**
40485      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40486      */
40487     growMax : 800,
40488     /**
40489      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40490      */
40491     vtype : null,
40492     /**
40493      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40494      */
40495     maskRe : null,
40496     /**
40497      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40498      */
40499     disableKeyFilter : false,
40500     /**
40501      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40502      */
40503     allowBlank : true,
40504     /**
40505      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40506      */
40507     minLength : 0,
40508     /**
40509      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40510      */
40511     maxLength : Number.MAX_VALUE,
40512     /**
40513      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40514      */
40515     minLengthText : "The minimum length for this field is {0}",
40516     /**
40517      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40518      */
40519     maxLengthText : "The maximum length for this field is {0}",
40520     /**
40521      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40522      */
40523     selectOnFocus : false,
40524     /**
40525      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40526      */    
40527     allowLeadingSpace : false,
40528     /**
40529      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40530      */
40531     blankText : "This field is required",
40532     /**
40533      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40534      * If available, this function will be called only after the basic validators all return true, and will be passed the
40535      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40536      */
40537     validator : null,
40538     /**
40539      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40540      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40541      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40542      */
40543     regex : null,
40544     /**
40545      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40546      */
40547     regexText : "",
40548     /**
40549      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40550      */
40551     emptyText : null,
40552    
40553
40554     // private
40555     initEvents : function()
40556     {
40557         if (this.emptyText) {
40558             this.el.attr('placeholder', this.emptyText);
40559         }
40560         
40561         Roo.form.TextField.superclass.initEvents.call(this);
40562         if(this.validationEvent == 'keyup'){
40563             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40564             this.el.on('keyup', this.filterValidation, this);
40565         }
40566         else if(this.validationEvent !== false){
40567             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40568         }
40569         
40570         if(this.selectOnFocus){
40571             this.on("focus", this.preFocus, this);
40572         }
40573         if (!this.allowLeadingSpace) {
40574             this.on('blur', this.cleanLeadingSpace, this);
40575         }
40576         
40577         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40578             this.el.on("keypress", this.filterKeys, this);
40579         }
40580         if(this.grow){
40581             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40582             this.el.on("click", this.autoSize,  this);
40583         }
40584         if(this.el.is('input[type=password]') && Roo.isSafari){
40585             this.el.on('keydown', this.SafariOnKeyDown, this);
40586         }
40587     },
40588
40589     processValue : function(value){
40590         if(this.stripCharsRe){
40591             var newValue = value.replace(this.stripCharsRe, '');
40592             if(newValue !== value){
40593                 this.setRawValue(newValue);
40594                 return newValue;
40595             }
40596         }
40597         return value;
40598     },
40599
40600     filterValidation : function(e){
40601         if(!e.isNavKeyPress()){
40602             this.validationTask.delay(this.validationDelay);
40603         }
40604     },
40605
40606     // private
40607     onKeyUp : function(e){
40608         if(!e.isNavKeyPress()){
40609             this.autoSize();
40610         }
40611     },
40612     // private - clean the leading white space
40613     cleanLeadingSpace : function(e)
40614     {
40615         if ( this.inputType == 'file') {
40616             return;
40617         }
40618         
40619         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40620     },
40621     /**
40622      * Resets the current field value to the originally-loaded value and clears any validation messages.
40623      *  
40624      */
40625     reset : function(){
40626         Roo.form.TextField.superclass.reset.call(this);
40627        
40628     }, 
40629     // private
40630     preFocus : function(){
40631         
40632         if(this.selectOnFocus){
40633             this.el.dom.select();
40634         }
40635     },
40636
40637     
40638     // private
40639     filterKeys : function(e){
40640         var k = e.getKey();
40641         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40642             return;
40643         }
40644         var c = e.getCharCode(), cc = String.fromCharCode(c);
40645         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40646             return;
40647         }
40648         if(!this.maskRe.test(cc)){
40649             e.stopEvent();
40650         }
40651     },
40652
40653     setValue : function(v){
40654         
40655         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40656         
40657         this.autoSize();
40658     },
40659
40660     /**
40661      * Validates a value according to the field's validation rules and marks the field as invalid
40662      * if the validation fails
40663      * @param {Mixed} value The value to validate
40664      * @return {Boolean} True if the value is valid, else false
40665      */
40666     validateValue : function(value){
40667         if(value.length < 1)  { // if it's blank
40668              if(this.allowBlank){
40669                 this.clearInvalid();
40670                 return true;
40671              }else{
40672                 this.markInvalid(this.blankText);
40673                 return false;
40674              }
40675         }
40676         if(value.length < this.minLength){
40677             this.markInvalid(String.format(this.minLengthText, this.minLength));
40678             return false;
40679         }
40680         if(value.length > this.maxLength){
40681             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40682             return false;
40683         }
40684         if(this.vtype){
40685             var vt = Roo.form.VTypes;
40686             if(!vt[this.vtype](value, this)){
40687                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40688                 return false;
40689             }
40690         }
40691         if(typeof this.validator == "function"){
40692             var msg = this.validator(value);
40693             if(msg !== true){
40694                 this.markInvalid(msg);
40695                 return false;
40696             }
40697         }
40698         if(this.regex && !this.regex.test(value)){
40699             this.markInvalid(this.regexText);
40700             return false;
40701         }
40702         return true;
40703     },
40704
40705     /**
40706      * Selects text in this field
40707      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40708      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40709      */
40710     selectText : function(start, end){
40711         var v = this.getRawValue();
40712         if(v.length > 0){
40713             start = start === undefined ? 0 : start;
40714             end = end === undefined ? v.length : end;
40715             var d = this.el.dom;
40716             if(d.setSelectionRange){
40717                 d.setSelectionRange(start, end);
40718             }else if(d.createTextRange){
40719                 var range = d.createTextRange();
40720                 range.moveStart("character", start);
40721                 range.moveEnd("character", v.length-end);
40722                 range.select();
40723             }
40724         }
40725     },
40726
40727     /**
40728      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40729      * This only takes effect if grow = true, and fires the autosize event.
40730      */
40731     autoSize : function(){
40732         if(!this.grow || !this.rendered){
40733             return;
40734         }
40735         if(!this.metrics){
40736             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40737         }
40738         var el = this.el;
40739         var v = el.dom.value;
40740         var d = document.createElement('div');
40741         d.appendChild(document.createTextNode(v));
40742         v = d.innerHTML;
40743         d = null;
40744         v += "&#160;";
40745         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40746         this.el.setWidth(w);
40747         this.fireEvent("autosize", this, w);
40748     },
40749     
40750     // private
40751     SafariOnKeyDown : function(event)
40752     {
40753         // this is a workaround for a password hang bug on chrome/ webkit.
40754         
40755         var isSelectAll = false;
40756         
40757         if(this.el.dom.selectionEnd > 0){
40758             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40759         }
40760         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40761             event.preventDefault();
40762             this.setValue('');
40763             return;
40764         }
40765         
40766         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40767             
40768             event.preventDefault();
40769             // this is very hacky as keydown always get's upper case.
40770             
40771             var cc = String.fromCharCode(event.getCharCode());
40772             
40773             
40774             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40775             
40776         }
40777         
40778         
40779     }
40780 });/*
40781  * Based on:
40782  * Ext JS Library 1.1.1
40783  * Copyright(c) 2006-2007, Ext JS, LLC.
40784  *
40785  * Originally Released Under LGPL - original licence link has changed is not relivant.
40786  *
40787  * Fork - LGPL
40788  * <script type="text/javascript">
40789  */
40790  
40791 /**
40792  * @class Roo.form.Hidden
40793  * @extends Roo.form.TextField
40794  * Simple Hidden element used on forms 
40795  * 
40796  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40797  * 
40798  * @constructor
40799  * Creates a new Hidden form element.
40800  * @param {Object} config Configuration options
40801  */
40802
40803
40804
40805 // easy hidden field...
40806 Roo.form.Hidden = function(config){
40807     Roo.form.Hidden.superclass.constructor.call(this, config);
40808 };
40809   
40810 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40811     fieldLabel:      '',
40812     inputType:      'hidden',
40813     width:          50,
40814     allowBlank:     true,
40815     labelSeparator: '',
40816     hidden:         true,
40817     itemCls :       'x-form-item-display-none'
40818
40819
40820 });
40821
40822
40823 /*
40824  * Based on:
40825  * Ext JS Library 1.1.1
40826  * Copyright(c) 2006-2007, Ext JS, LLC.
40827  *
40828  * Originally Released Under LGPL - original licence link has changed is not relivant.
40829  *
40830  * Fork - LGPL
40831  * <script type="text/javascript">
40832  */
40833  
40834 /**
40835  * @class Roo.form.TriggerField
40836  * @extends Roo.form.TextField
40837  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40838  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40839  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40840  * for which you can provide a custom implementation.  For example:
40841  * <pre><code>
40842 var trigger = new Roo.form.TriggerField();
40843 trigger.onTriggerClick = myTriggerFn;
40844 trigger.applyTo('my-field');
40845 </code></pre>
40846  *
40847  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40848  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40849  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40850  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40851  * @constructor
40852  * Create a new TriggerField.
40853  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40854  * to the base TextField)
40855  */
40856 Roo.form.TriggerField = function(config){
40857     this.mimicing = false;
40858     Roo.form.TriggerField.superclass.constructor.call(this, config);
40859 };
40860
40861 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40862     /**
40863      * @cfg {String} triggerClass A CSS class to apply to the trigger
40864      */
40865     /**
40866      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40867      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40868      */
40869     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40870     /**
40871      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40872      */
40873     hideTrigger:false,
40874
40875     /** @cfg {Boolean} grow @hide */
40876     /** @cfg {Number} growMin @hide */
40877     /** @cfg {Number} growMax @hide */
40878
40879     /**
40880      * @hide 
40881      * @method
40882      */
40883     autoSize: Roo.emptyFn,
40884     // private
40885     monitorTab : true,
40886     // private
40887     deferHeight : true,
40888
40889     
40890     actionMode : 'wrap',
40891     // private
40892     onResize : function(w, h){
40893         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40894         if(typeof w == 'number'){
40895             var x = w - this.trigger.getWidth();
40896             this.el.setWidth(this.adjustWidth('input', x));
40897             this.trigger.setStyle('left', x+'px');
40898         }
40899     },
40900
40901     // private
40902     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40903
40904     // private
40905     getResizeEl : function(){
40906         return this.wrap;
40907     },
40908
40909     // private
40910     getPositionEl : function(){
40911         return this.wrap;
40912     },
40913
40914     // private
40915     alignErrorIcon : function(){
40916         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40917     },
40918
40919     // private
40920     onRender : function(ct, position){
40921         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40922         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
40923         this.trigger = this.wrap.createChild(this.triggerConfig ||
40924                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
40925         if(this.hideTrigger){
40926             this.trigger.setDisplayed(false);
40927         }
40928         this.initTrigger();
40929         if(!this.width){
40930             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
40931         }
40932     },
40933
40934     // private
40935     initTrigger : function(){
40936         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
40937         this.trigger.addClassOnOver('x-form-trigger-over');
40938         this.trigger.addClassOnClick('x-form-trigger-click');
40939     },
40940
40941     // private
40942     onDestroy : function(){
40943         if(this.trigger){
40944             this.trigger.removeAllListeners();
40945             this.trigger.remove();
40946         }
40947         if(this.wrap){
40948             this.wrap.remove();
40949         }
40950         Roo.form.TriggerField.superclass.onDestroy.call(this);
40951     },
40952
40953     // private
40954     onFocus : function(){
40955         Roo.form.TriggerField.superclass.onFocus.call(this);
40956         if(!this.mimicing){
40957             this.wrap.addClass('x-trigger-wrap-focus');
40958             this.mimicing = true;
40959             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
40960             if(this.monitorTab){
40961                 this.el.on("keydown", this.checkTab, this);
40962             }
40963         }
40964     },
40965
40966     // private
40967     checkTab : function(e){
40968         if(e.getKey() == e.TAB){
40969             this.triggerBlur();
40970         }
40971     },
40972
40973     // private
40974     onBlur : function(){
40975         // do nothing
40976     },
40977
40978     // private
40979     mimicBlur : function(e, t){
40980         if(!this.wrap.contains(t) && this.validateBlur()){
40981             this.triggerBlur();
40982         }
40983     },
40984
40985     // private
40986     triggerBlur : function(){
40987         this.mimicing = false;
40988         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
40989         if(this.monitorTab){
40990             this.el.un("keydown", this.checkTab, this);
40991         }
40992         this.wrap.removeClass('x-trigger-wrap-focus');
40993         Roo.form.TriggerField.superclass.onBlur.call(this);
40994     },
40995
40996     // private
40997     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
40998     validateBlur : function(e, t){
40999         return true;
41000     },
41001
41002     // private
41003     onDisable : function(){
41004         Roo.form.TriggerField.superclass.onDisable.call(this);
41005         if(this.wrap){
41006             this.wrap.addClass('x-item-disabled');
41007         }
41008     },
41009
41010     // private
41011     onEnable : function(){
41012         Roo.form.TriggerField.superclass.onEnable.call(this);
41013         if(this.wrap){
41014             this.wrap.removeClass('x-item-disabled');
41015         }
41016     },
41017
41018     // private
41019     onShow : function(){
41020         var ae = this.getActionEl();
41021         
41022         if(ae){
41023             ae.dom.style.display = '';
41024             ae.dom.style.visibility = 'visible';
41025         }
41026     },
41027
41028     // private
41029     
41030     onHide : function(){
41031         var ae = this.getActionEl();
41032         ae.dom.style.display = 'none';
41033     },
41034
41035     /**
41036      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41037      * by an implementing function.
41038      * @method
41039      * @param {EventObject} e
41040      */
41041     onTriggerClick : Roo.emptyFn
41042 });
41043
41044 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41045 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41046 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41047 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41048     initComponent : function(){
41049         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41050
41051         this.triggerConfig = {
41052             tag:'span', cls:'x-form-twin-triggers', cn:[
41053             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41054             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41055         ]};
41056     },
41057
41058     getTrigger : function(index){
41059         return this.triggers[index];
41060     },
41061
41062     initTrigger : function(){
41063         var ts = this.trigger.select('.x-form-trigger', true);
41064         this.wrap.setStyle('overflow', 'hidden');
41065         var triggerField = this;
41066         ts.each(function(t, all, index){
41067             t.hide = function(){
41068                 var w = triggerField.wrap.getWidth();
41069                 this.dom.style.display = 'none';
41070                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41071             };
41072             t.show = function(){
41073                 var w = triggerField.wrap.getWidth();
41074                 this.dom.style.display = '';
41075                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41076             };
41077             var triggerIndex = 'Trigger'+(index+1);
41078
41079             if(this['hide'+triggerIndex]){
41080                 t.dom.style.display = 'none';
41081             }
41082             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41083             t.addClassOnOver('x-form-trigger-over');
41084             t.addClassOnClick('x-form-trigger-click');
41085         }, this);
41086         this.triggers = ts.elements;
41087     },
41088
41089     onTrigger1Click : Roo.emptyFn,
41090     onTrigger2Click : Roo.emptyFn
41091 });/*
41092  * Based on:
41093  * Ext JS Library 1.1.1
41094  * Copyright(c) 2006-2007, Ext JS, LLC.
41095  *
41096  * Originally Released Under LGPL - original licence link has changed is not relivant.
41097  *
41098  * Fork - LGPL
41099  * <script type="text/javascript">
41100  */
41101  
41102 /**
41103  * @class Roo.form.TextArea
41104  * @extends Roo.form.TextField
41105  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41106  * support for auto-sizing.
41107  * @constructor
41108  * Creates a new TextArea
41109  * @param {Object} config Configuration options
41110  */
41111 Roo.form.TextArea = function(config){
41112     Roo.form.TextArea.superclass.constructor.call(this, config);
41113     // these are provided exchanges for backwards compat
41114     // minHeight/maxHeight were replaced by growMin/growMax to be
41115     // compatible with TextField growing config values
41116     if(this.minHeight !== undefined){
41117         this.growMin = this.minHeight;
41118     }
41119     if(this.maxHeight !== undefined){
41120         this.growMax = this.maxHeight;
41121     }
41122 };
41123
41124 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41125     /**
41126      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41127      */
41128     growMin : 60,
41129     /**
41130      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41131      */
41132     growMax: 1000,
41133     /**
41134      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41135      * in the field (equivalent to setting overflow: hidden, defaults to false)
41136      */
41137     preventScrollbars: false,
41138     /**
41139      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41140      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41141      */
41142
41143     // private
41144     onRender : function(ct, position){
41145         if(!this.el){
41146             this.defaultAutoCreate = {
41147                 tag: "textarea",
41148                 style:"width:300px;height:60px;",
41149                 autocomplete: "new-password"
41150             };
41151         }
41152         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41153         if(this.grow){
41154             this.textSizeEl = Roo.DomHelper.append(document.body, {
41155                 tag: "pre", cls: "x-form-grow-sizer"
41156             });
41157             if(this.preventScrollbars){
41158                 this.el.setStyle("overflow", "hidden");
41159             }
41160             this.el.setHeight(this.growMin);
41161         }
41162     },
41163
41164     onDestroy : function(){
41165         if(this.textSizeEl){
41166             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41167         }
41168         Roo.form.TextArea.superclass.onDestroy.call(this);
41169     },
41170
41171     // private
41172     onKeyUp : function(e){
41173         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41174             this.autoSize();
41175         }
41176     },
41177
41178     /**
41179      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41180      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41181      */
41182     autoSize : function(){
41183         if(!this.grow || !this.textSizeEl){
41184             return;
41185         }
41186         var el = this.el;
41187         var v = el.dom.value;
41188         var ts = this.textSizeEl;
41189
41190         ts.innerHTML = '';
41191         ts.appendChild(document.createTextNode(v));
41192         v = ts.innerHTML;
41193
41194         Roo.fly(ts).setWidth(this.el.getWidth());
41195         if(v.length < 1){
41196             v = "&#160;&#160;";
41197         }else{
41198             if(Roo.isIE){
41199                 v = v.replace(/\n/g, '<p>&#160;</p>');
41200             }
41201             v += "&#160;\n&#160;";
41202         }
41203         ts.innerHTML = v;
41204         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41205         if(h != this.lastHeight){
41206             this.lastHeight = h;
41207             this.el.setHeight(h);
41208             this.fireEvent("autosize", this, h);
41209         }
41210     }
41211 });/*
41212  * Based on:
41213  * Ext JS Library 1.1.1
41214  * Copyright(c) 2006-2007, Ext JS, LLC.
41215  *
41216  * Originally Released Under LGPL - original licence link has changed is not relivant.
41217  *
41218  * Fork - LGPL
41219  * <script type="text/javascript">
41220  */
41221  
41222
41223 /**
41224  * @class Roo.form.NumberField
41225  * @extends Roo.form.TextField
41226  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41227  * @constructor
41228  * Creates a new NumberField
41229  * @param {Object} config Configuration options
41230  */
41231 Roo.form.NumberField = function(config){
41232     Roo.form.NumberField.superclass.constructor.call(this, config);
41233 };
41234
41235 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41236     /**
41237      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41238      */
41239     fieldClass: "x-form-field x-form-num-field",
41240     /**
41241      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41242      */
41243     allowDecimals : true,
41244     /**
41245      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41246      */
41247     decimalSeparator : ".",
41248     /**
41249      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41250      */
41251     decimalPrecision : 2,
41252     /**
41253      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41254      */
41255     allowNegative : true,
41256     /**
41257      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41258      */
41259     minValue : Number.NEGATIVE_INFINITY,
41260     /**
41261      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41262      */
41263     maxValue : Number.MAX_VALUE,
41264     /**
41265      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41266      */
41267     minText : "The minimum value for this field is {0}",
41268     /**
41269      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41270      */
41271     maxText : "The maximum value for this field is {0}",
41272     /**
41273      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41274      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41275      */
41276     nanText : "{0} is not a valid number",
41277
41278     // private
41279     initEvents : function(){
41280         Roo.form.NumberField.superclass.initEvents.call(this);
41281         var allowed = "0123456789";
41282         if(this.allowDecimals){
41283             allowed += this.decimalSeparator;
41284         }
41285         if(this.allowNegative){
41286             allowed += "-";
41287         }
41288         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41289         var keyPress = function(e){
41290             var k = e.getKey();
41291             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41292                 return;
41293             }
41294             var c = e.getCharCode();
41295             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41296                 e.stopEvent();
41297             }
41298         };
41299         this.el.on("keypress", keyPress, this);
41300     },
41301
41302     // private
41303     validateValue : function(value){
41304         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41305             return false;
41306         }
41307         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41308              return true;
41309         }
41310         var num = this.parseValue(value);
41311         if(isNaN(num)){
41312             this.markInvalid(String.format(this.nanText, value));
41313             return false;
41314         }
41315         if(num < this.minValue){
41316             this.markInvalid(String.format(this.minText, this.minValue));
41317             return false;
41318         }
41319         if(num > this.maxValue){
41320             this.markInvalid(String.format(this.maxText, this.maxValue));
41321             return false;
41322         }
41323         return true;
41324     },
41325
41326     getValue : function(){
41327         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41328     },
41329
41330     // private
41331     parseValue : function(value){
41332         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41333         return isNaN(value) ? '' : value;
41334     },
41335
41336     // private
41337     fixPrecision : function(value){
41338         var nan = isNaN(value);
41339         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41340             return nan ? '' : value;
41341         }
41342         return parseFloat(value).toFixed(this.decimalPrecision);
41343     },
41344
41345     setValue : function(v){
41346         v = this.fixPrecision(v);
41347         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41348     },
41349
41350     // private
41351     decimalPrecisionFcn : function(v){
41352         return Math.floor(v);
41353     },
41354
41355     beforeBlur : function(){
41356         var v = this.parseValue(this.getRawValue());
41357         if(v){
41358             this.setValue(v);
41359         }
41360     }
41361 });/*
41362  * Based on:
41363  * Ext JS Library 1.1.1
41364  * Copyright(c) 2006-2007, Ext JS, LLC.
41365  *
41366  * Originally Released Under LGPL - original licence link has changed is not relivant.
41367  *
41368  * Fork - LGPL
41369  * <script type="text/javascript">
41370  */
41371  
41372 /**
41373  * @class Roo.form.DateField
41374  * @extends Roo.form.TriggerField
41375  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41376 * @constructor
41377 * Create a new DateField
41378 * @param {Object} config
41379  */
41380 Roo.form.DateField = function(config)
41381 {
41382     Roo.form.DateField.superclass.constructor.call(this, config);
41383     
41384       this.addEvents({
41385          
41386         /**
41387          * @event select
41388          * Fires when a date is selected
41389              * @param {Roo.form.DateField} combo This combo box
41390              * @param {Date} date The date selected
41391              */
41392         'select' : true
41393          
41394     });
41395     
41396     
41397     if(typeof this.minValue == "string") {
41398         this.minValue = this.parseDate(this.minValue);
41399     }
41400     if(typeof this.maxValue == "string") {
41401         this.maxValue = this.parseDate(this.maxValue);
41402     }
41403     this.ddMatch = null;
41404     if(this.disabledDates){
41405         var dd = this.disabledDates;
41406         var re = "(?:";
41407         for(var i = 0; i < dd.length; i++){
41408             re += dd[i];
41409             if(i != dd.length-1) {
41410                 re += "|";
41411             }
41412         }
41413         this.ddMatch = new RegExp(re + ")");
41414     }
41415 };
41416
41417 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41418     /**
41419      * @cfg {String} format
41420      * The default date format string which can be overriden for localization support.  The format must be
41421      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41422      */
41423     format : "m/d/y",
41424     /**
41425      * @cfg {String} altFormats
41426      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41427      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41428      */
41429     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41430     /**
41431      * @cfg {Array} disabledDays
41432      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41433      */
41434     disabledDays : null,
41435     /**
41436      * @cfg {String} disabledDaysText
41437      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41438      */
41439     disabledDaysText : "Disabled",
41440     /**
41441      * @cfg {Array} disabledDates
41442      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41443      * expression so they are very powerful. Some examples:
41444      * <ul>
41445      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41446      * <li>["03/08", "09/16"] would disable those days for every year</li>
41447      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41448      * <li>["03/../2006"] would disable every day in March 2006</li>
41449      * <li>["^03"] would disable every day in every March</li>
41450      * </ul>
41451      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41452      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41453      */
41454     disabledDates : null,
41455     /**
41456      * @cfg {String} disabledDatesText
41457      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41458      */
41459     disabledDatesText : "Disabled",
41460     /**
41461      * @cfg {Date/String} minValue
41462      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41463      * valid format (defaults to null).
41464      */
41465     minValue : null,
41466     /**
41467      * @cfg {Date/String} maxValue
41468      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41469      * valid format (defaults to null).
41470      */
41471     maxValue : null,
41472     /**
41473      * @cfg {String} minText
41474      * The error text to display when the date in the cell is before minValue (defaults to
41475      * 'The date in this field must be after {minValue}').
41476      */
41477     minText : "The date in this field must be equal to or after {0}",
41478     /**
41479      * @cfg {String} maxText
41480      * The error text to display when the date in the cell is after maxValue (defaults to
41481      * 'The date in this field must be before {maxValue}').
41482      */
41483     maxText : "The date in this field must be equal to or before {0}",
41484     /**
41485      * @cfg {String} invalidText
41486      * The error text to display when the date in the field is invalid (defaults to
41487      * '{value} is not a valid date - it must be in the format {format}').
41488      */
41489     invalidText : "{0} is not a valid date - it must be in the format {1}",
41490     /**
41491      * @cfg {String} triggerClass
41492      * An additional CSS class used to style the trigger button.  The trigger will always get the
41493      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41494      * which displays a calendar icon).
41495      */
41496     triggerClass : 'x-form-date-trigger',
41497     
41498
41499     /**
41500      * @cfg {Boolean} useIso
41501      * if enabled, then the date field will use a hidden field to store the 
41502      * real value as iso formated date. default (false)
41503      */ 
41504     useIso : false,
41505     /**
41506      * @cfg {String/Object} autoCreate
41507      * A DomHelper element spec, or true for a default element spec (defaults to
41508      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41509      */ 
41510     // private
41511     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41512     
41513     // private
41514     hiddenField: false,
41515     
41516     onRender : function(ct, position)
41517     {
41518         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41519         if (this.useIso) {
41520             //this.el.dom.removeAttribute('name'); 
41521             Roo.log("Changing name?");
41522             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41523             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41524                     'before', true);
41525             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41526             // prevent input submission
41527             this.hiddenName = this.name;
41528         }
41529             
41530             
41531     },
41532     
41533     // private
41534     validateValue : function(value)
41535     {
41536         value = this.formatDate(value);
41537         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41538             Roo.log('super failed');
41539             return false;
41540         }
41541         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41542              return true;
41543         }
41544         var svalue = value;
41545         value = this.parseDate(value);
41546         if(!value){
41547             Roo.log('parse date failed' + svalue);
41548             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41549             return false;
41550         }
41551         var time = value.getTime();
41552         if(this.minValue && time < this.minValue.getTime()){
41553             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41554             return false;
41555         }
41556         if(this.maxValue && time > this.maxValue.getTime()){
41557             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41558             return false;
41559         }
41560         if(this.disabledDays){
41561             var day = value.getDay();
41562             for(var i = 0; i < this.disabledDays.length; i++) {
41563                 if(day === this.disabledDays[i]){
41564                     this.markInvalid(this.disabledDaysText);
41565                     return false;
41566                 }
41567             }
41568         }
41569         var fvalue = this.formatDate(value);
41570         if(this.ddMatch && this.ddMatch.test(fvalue)){
41571             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41572             return false;
41573         }
41574         return true;
41575     },
41576
41577     // private
41578     // Provides logic to override the default TriggerField.validateBlur which just returns true
41579     validateBlur : function(){
41580         return !this.menu || !this.menu.isVisible();
41581     },
41582     
41583     getName: function()
41584     {
41585         // returns hidden if it's set..
41586         if (!this.rendered) {return ''};
41587         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41588         
41589     },
41590
41591     /**
41592      * Returns the current date value of the date field.
41593      * @return {Date} The date value
41594      */
41595     getValue : function(){
41596         
41597         return  this.hiddenField ?
41598                 this.hiddenField.value :
41599                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41600     },
41601
41602     /**
41603      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41604      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41605      * (the default format used is "m/d/y").
41606      * <br />Usage:
41607      * <pre><code>
41608 //All of these calls set the same date value (May 4, 2006)
41609
41610 //Pass a date object:
41611 var dt = new Date('5/4/06');
41612 dateField.setValue(dt);
41613
41614 //Pass a date string (default format):
41615 dateField.setValue('5/4/06');
41616
41617 //Pass a date string (custom format):
41618 dateField.format = 'Y-m-d';
41619 dateField.setValue('2006-5-4');
41620 </code></pre>
41621      * @param {String/Date} date The date or valid date string
41622      */
41623     setValue : function(date){
41624         if (this.hiddenField) {
41625             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41626         }
41627         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41628         // make sure the value field is always stored as a date..
41629         this.value = this.parseDate(date);
41630         
41631         
41632     },
41633
41634     // private
41635     parseDate : function(value){
41636         if(!value || value instanceof Date){
41637             return value;
41638         }
41639         var v = Date.parseDate(value, this.format);
41640          if (!v && this.useIso) {
41641             v = Date.parseDate(value, 'Y-m-d');
41642         }
41643         if(!v && this.altFormats){
41644             if(!this.altFormatsArray){
41645                 this.altFormatsArray = this.altFormats.split("|");
41646             }
41647             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41648                 v = Date.parseDate(value, this.altFormatsArray[i]);
41649             }
41650         }
41651         return v;
41652     },
41653
41654     // private
41655     formatDate : function(date, fmt){
41656         return (!date || !(date instanceof Date)) ?
41657                date : date.dateFormat(fmt || this.format);
41658     },
41659
41660     // private
41661     menuListeners : {
41662         select: function(m, d){
41663             
41664             this.setValue(d);
41665             this.fireEvent('select', this, d);
41666         },
41667         show : function(){ // retain focus styling
41668             this.onFocus();
41669         },
41670         hide : function(){
41671             this.focus.defer(10, this);
41672             var ml = this.menuListeners;
41673             this.menu.un("select", ml.select,  this);
41674             this.menu.un("show", ml.show,  this);
41675             this.menu.un("hide", ml.hide,  this);
41676         }
41677     },
41678
41679     // private
41680     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41681     onTriggerClick : function(){
41682         if(this.disabled){
41683             return;
41684         }
41685         if(this.menu == null){
41686             this.menu = new Roo.menu.DateMenu();
41687         }
41688         Roo.apply(this.menu.picker,  {
41689             showClear: this.allowBlank,
41690             minDate : this.minValue,
41691             maxDate : this.maxValue,
41692             disabledDatesRE : this.ddMatch,
41693             disabledDatesText : this.disabledDatesText,
41694             disabledDays : this.disabledDays,
41695             disabledDaysText : this.disabledDaysText,
41696             format : this.useIso ? 'Y-m-d' : this.format,
41697             minText : String.format(this.minText, this.formatDate(this.minValue)),
41698             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41699         });
41700         this.menu.on(Roo.apply({}, this.menuListeners, {
41701             scope:this
41702         }));
41703         this.menu.picker.setValue(this.getValue() || new Date());
41704         this.menu.show(this.el, "tl-bl?");
41705     },
41706
41707     beforeBlur : function(){
41708         var v = this.parseDate(this.getRawValue());
41709         if(v){
41710             this.setValue(v);
41711         }
41712     },
41713
41714     /*@
41715      * overide
41716      * 
41717      */
41718     isDirty : function() {
41719         if(this.disabled) {
41720             return false;
41721         }
41722         
41723         if(typeof(this.startValue) === 'undefined'){
41724             return false;
41725         }
41726         
41727         return String(this.getValue()) !== String(this.startValue);
41728         
41729     },
41730     // @overide
41731     cleanLeadingSpace : function(e)
41732     {
41733        return;
41734     }
41735     
41736 });/*
41737  * Based on:
41738  * Ext JS Library 1.1.1
41739  * Copyright(c) 2006-2007, Ext JS, LLC.
41740  *
41741  * Originally Released Under LGPL - original licence link has changed is not relivant.
41742  *
41743  * Fork - LGPL
41744  * <script type="text/javascript">
41745  */
41746  
41747 /**
41748  * @class Roo.form.MonthField
41749  * @extends Roo.form.TriggerField
41750  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41751 * @constructor
41752 * Create a new MonthField
41753 * @param {Object} config
41754  */
41755 Roo.form.MonthField = function(config){
41756     
41757     Roo.form.MonthField.superclass.constructor.call(this, config);
41758     
41759       this.addEvents({
41760          
41761         /**
41762          * @event select
41763          * Fires when a date is selected
41764              * @param {Roo.form.MonthFieeld} combo This combo box
41765              * @param {Date} date The date selected
41766              */
41767         'select' : true
41768          
41769     });
41770     
41771     
41772     if(typeof this.minValue == "string") {
41773         this.minValue = this.parseDate(this.minValue);
41774     }
41775     if(typeof this.maxValue == "string") {
41776         this.maxValue = this.parseDate(this.maxValue);
41777     }
41778     this.ddMatch = null;
41779     if(this.disabledDates){
41780         var dd = this.disabledDates;
41781         var re = "(?:";
41782         for(var i = 0; i < dd.length; i++){
41783             re += dd[i];
41784             if(i != dd.length-1) {
41785                 re += "|";
41786             }
41787         }
41788         this.ddMatch = new RegExp(re + ")");
41789     }
41790 };
41791
41792 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41793     /**
41794      * @cfg {String} format
41795      * The default date format string which can be overriden for localization support.  The format must be
41796      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41797      */
41798     format : "M Y",
41799     /**
41800      * @cfg {String} altFormats
41801      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41802      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41803      */
41804     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41805     /**
41806      * @cfg {Array} disabledDays
41807      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41808      */
41809     disabledDays : [0,1,2,3,4,5,6],
41810     /**
41811      * @cfg {String} disabledDaysText
41812      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41813      */
41814     disabledDaysText : "Disabled",
41815     /**
41816      * @cfg {Array} disabledDates
41817      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41818      * expression so they are very powerful. Some examples:
41819      * <ul>
41820      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41821      * <li>["03/08", "09/16"] would disable those days for every year</li>
41822      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41823      * <li>["03/../2006"] would disable every day in March 2006</li>
41824      * <li>["^03"] would disable every day in every March</li>
41825      * </ul>
41826      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41827      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41828      */
41829     disabledDates : null,
41830     /**
41831      * @cfg {String} disabledDatesText
41832      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41833      */
41834     disabledDatesText : "Disabled",
41835     /**
41836      * @cfg {Date/String} minValue
41837      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41838      * valid format (defaults to null).
41839      */
41840     minValue : null,
41841     /**
41842      * @cfg {Date/String} maxValue
41843      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41844      * valid format (defaults to null).
41845      */
41846     maxValue : null,
41847     /**
41848      * @cfg {String} minText
41849      * The error text to display when the date in the cell is before minValue (defaults to
41850      * 'The date in this field must be after {minValue}').
41851      */
41852     minText : "The date in this field must be equal to or after {0}",
41853     /**
41854      * @cfg {String} maxTextf
41855      * The error text to display when the date in the cell is after maxValue (defaults to
41856      * 'The date in this field must be before {maxValue}').
41857      */
41858     maxText : "The date in this field must be equal to or before {0}",
41859     /**
41860      * @cfg {String} invalidText
41861      * The error text to display when the date in the field is invalid (defaults to
41862      * '{value} is not a valid date - it must be in the format {format}').
41863      */
41864     invalidText : "{0} is not a valid date - it must be in the format {1}",
41865     /**
41866      * @cfg {String} triggerClass
41867      * An additional CSS class used to style the trigger button.  The trigger will always get the
41868      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41869      * which displays a calendar icon).
41870      */
41871     triggerClass : 'x-form-date-trigger',
41872     
41873
41874     /**
41875      * @cfg {Boolean} useIso
41876      * if enabled, then the date field will use a hidden field to store the 
41877      * real value as iso formated date. default (true)
41878      */ 
41879     useIso : true,
41880     /**
41881      * @cfg {String/Object} autoCreate
41882      * A DomHelper element spec, or true for a default element spec (defaults to
41883      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41884      */ 
41885     // private
41886     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41887     
41888     // private
41889     hiddenField: false,
41890     
41891     hideMonthPicker : false,
41892     
41893     onRender : function(ct, position)
41894     {
41895         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41896         if (this.useIso) {
41897             this.el.dom.removeAttribute('name'); 
41898             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41899                     'before', true);
41900             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41901             // prevent input submission
41902             this.hiddenName = this.name;
41903         }
41904             
41905             
41906     },
41907     
41908     // private
41909     validateValue : function(value)
41910     {
41911         value = this.formatDate(value);
41912         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41913             return false;
41914         }
41915         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41916              return true;
41917         }
41918         var svalue = value;
41919         value = this.parseDate(value);
41920         if(!value){
41921             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41922             return false;
41923         }
41924         var time = value.getTime();
41925         if(this.minValue && time < this.minValue.getTime()){
41926             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41927             return false;
41928         }
41929         if(this.maxValue && time > this.maxValue.getTime()){
41930             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41931             return false;
41932         }
41933         /*if(this.disabledDays){
41934             var day = value.getDay();
41935             for(var i = 0; i < this.disabledDays.length; i++) {
41936                 if(day === this.disabledDays[i]){
41937                     this.markInvalid(this.disabledDaysText);
41938                     return false;
41939                 }
41940             }
41941         }
41942         */
41943         var fvalue = this.formatDate(value);
41944         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
41945             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41946             return false;
41947         }
41948         */
41949         return true;
41950     },
41951
41952     // private
41953     // Provides logic to override the default TriggerField.validateBlur which just returns true
41954     validateBlur : function(){
41955         return !this.menu || !this.menu.isVisible();
41956     },
41957
41958     /**
41959      * Returns the current date value of the date field.
41960      * @return {Date} The date value
41961      */
41962     getValue : function(){
41963         
41964         
41965         
41966         return  this.hiddenField ?
41967                 this.hiddenField.value :
41968                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
41969     },
41970
41971     /**
41972      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41973      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
41974      * (the default format used is "m/d/y").
41975      * <br />Usage:
41976      * <pre><code>
41977 //All of these calls set the same date value (May 4, 2006)
41978
41979 //Pass a date object:
41980 var dt = new Date('5/4/06');
41981 monthField.setValue(dt);
41982
41983 //Pass a date string (default format):
41984 monthField.setValue('5/4/06');
41985
41986 //Pass a date string (custom format):
41987 monthField.format = 'Y-m-d';
41988 monthField.setValue('2006-5-4');
41989 </code></pre>
41990      * @param {String/Date} date The date or valid date string
41991      */
41992     setValue : function(date){
41993         Roo.log('month setValue' + date);
41994         // can only be first of month..
41995         
41996         var val = this.parseDate(date);
41997         
41998         if (this.hiddenField) {
41999             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42000         }
42001         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42002         this.value = this.parseDate(date);
42003     },
42004
42005     // private
42006     parseDate : function(value){
42007         if(!value || value instanceof Date){
42008             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42009             return value;
42010         }
42011         var v = Date.parseDate(value, this.format);
42012         if (!v && this.useIso) {
42013             v = Date.parseDate(value, 'Y-m-d');
42014         }
42015         if (v) {
42016             // 
42017             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42018         }
42019         
42020         
42021         if(!v && this.altFormats){
42022             if(!this.altFormatsArray){
42023                 this.altFormatsArray = this.altFormats.split("|");
42024             }
42025             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42026                 v = Date.parseDate(value, this.altFormatsArray[i]);
42027             }
42028         }
42029         return v;
42030     },
42031
42032     // private
42033     formatDate : function(date, fmt){
42034         return (!date || !(date instanceof Date)) ?
42035                date : date.dateFormat(fmt || this.format);
42036     },
42037
42038     // private
42039     menuListeners : {
42040         select: function(m, d){
42041             this.setValue(d);
42042             this.fireEvent('select', this, d);
42043         },
42044         show : function(){ // retain focus styling
42045             this.onFocus();
42046         },
42047         hide : function(){
42048             this.focus.defer(10, this);
42049             var ml = this.menuListeners;
42050             this.menu.un("select", ml.select,  this);
42051             this.menu.un("show", ml.show,  this);
42052             this.menu.un("hide", ml.hide,  this);
42053         }
42054     },
42055     // private
42056     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42057     onTriggerClick : function(){
42058         if(this.disabled){
42059             return;
42060         }
42061         if(this.menu == null){
42062             this.menu = new Roo.menu.DateMenu();
42063            
42064         }
42065         
42066         Roo.apply(this.menu.picker,  {
42067             
42068             showClear: this.allowBlank,
42069             minDate : this.minValue,
42070             maxDate : this.maxValue,
42071             disabledDatesRE : this.ddMatch,
42072             disabledDatesText : this.disabledDatesText,
42073             
42074             format : this.useIso ? 'Y-m-d' : this.format,
42075             minText : String.format(this.minText, this.formatDate(this.minValue)),
42076             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42077             
42078         });
42079          this.menu.on(Roo.apply({}, this.menuListeners, {
42080             scope:this
42081         }));
42082        
42083         
42084         var m = this.menu;
42085         var p = m.picker;
42086         
42087         // hide month picker get's called when we called by 'before hide';
42088         
42089         var ignorehide = true;
42090         p.hideMonthPicker  = function(disableAnim){
42091             if (ignorehide) {
42092                 return;
42093             }
42094              if(this.monthPicker){
42095                 Roo.log("hideMonthPicker called");
42096                 if(disableAnim === true){
42097                     this.monthPicker.hide();
42098                 }else{
42099                     this.monthPicker.slideOut('t', {duration:.2});
42100                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42101                     p.fireEvent("select", this, this.value);
42102                     m.hide();
42103                 }
42104             }
42105         }
42106         
42107         Roo.log('picker set value');
42108         Roo.log(this.getValue());
42109         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42110         m.show(this.el, 'tl-bl?');
42111         ignorehide  = false;
42112         // this will trigger hideMonthPicker..
42113         
42114         
42115         // hidden the day picker
42116         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42117         
42118         
42119         
42120       
42121         
42122         p.showMonthPicker.defer(100, p);
42123     
42124         
42125        
42126     },
42127
42128     beforeBlur : function(){
42129         var v = this.parseDate(this.getRawValue());
42130         if(v){
42131             this.setValue(v);
42132         }
42133     }
42134
42135     /** @cfg {Boolean} grow @hide */
42136     /** @cfg {Number} growMin @hide */
42137     /** @cfg {Number} growMax @hide */
42138     /**
42139      * @hide
42140      * @method autoSize
42141      */
42142 });/*
42143  * Based on:
42144  * Ext JS Library 1.1.1
42145  * Copyright(c) 2006-2007, Ext JS, LLC.
42146  *
42147  * Originally Released Under LGPL - original licence link has changed is not relivant.
42148  *
42149  * Fork - LGPL
42150  * <script type="text/javascript">
42151  */
42152  
42153
42154 /**
42155  * @class Roo.form.ComboBox
42156  * @extends Roo.form.TriggerField
42157  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42158  * @constructor
42159  * Create a new ComboBox.
42160  * @param {Object} config Configuration options
42161  */
42162 Roo.form.ComboBox = function(config){
42163     Roo.form.ComboBox.superclass.constructor.call(this, config);
42164     this.addEvents({
42165         /**
42166          * @event expand
42167          * Fires when the dropdown list is expanded
42168              * @param {Roo.form.ComboBox} combo This combo box
42169              */
42170         'expand' : true,
42171         /**
42172          * @event collapse
42173          * Fires when the dropdown list is collapsed
42174              * @param {Roo.form.ComboBox} combo This combo box
42175              */
42176         'collapse' : true,
42177         /**
42178          * @event beforeselect
42179          * Fires before a list item is selected. Return false to cancel the selection.
42180              * @param {Roo.form.ComboBox} combo This combo box
42181              * @param {Roo.data.Record} record The data record returned from the underlying store
42182              * @param {Number} index The index of the selected item in the dropdown list
42183              */
42184         'beforeselect' : true,
42185         /**
42186          * @event select
42187          * Fires when a list item is selected
42188              * @param {Roo.form.ComboBox} combo This combo box
42189              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42190              * @param {Number} index The index of the selected item in the dropdown list
42191              */
42192         'select' : true,
42193         /**
42194          * @event beforequery
42195          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42196          * The event object passed has these properties:
42197              * @param {Roo.form.ComboBox} combo This combo box
42198              * @param {String} query The query
42199              * @param {Boolean} forceAll true to force "all" query
42200              * @param {Boolean} cancel true to cancel the query
42201              * @param {Object} e The query event object
42202              */
42203         'beforequery': true,
42204          /**
42205          * @event add
42206          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42207              * @param {Roo.form.ComboBox} combo This combo box
42208              */
42209         'add' : true,
42210         /**
42211          * @event edit
42212          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42213              * @param {Roo.form.ComboBox} combo This combo box
42214              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42215              */
42216         'edit' : true
42217         
42218         
42219     });
42220     if(this.transform){
42221         this.allowDomMove = false;
42222         var s = Roo.getDom(this.transform);
42223         if(!this.hiddenName){
42224             this.hiddenName = s.name;
42225         }
42226         if(!this.store){
42227             this.mode = 'local';
42228             var d = [], opts = s.options;
42229             for(var i = 0, len = opts.length;i < len; i++){
42230                 var o = opts[i];
42231                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42232                 if(o.selected) {
42233                     this.value = value;
42234                 }
42235                 d.push([value, o.text]);
42236             }
42237             this.store = new Roo.data.SimpleStore({
42238                 'id': 0,
42239                 fields: ['value', 'text'],
42240                 data : d
42241             });
42242             this.valueField = 'value';
42243             this.displayField = 'text';
42244         }
42245         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42246         if(!this.lazyRender){
42247             this.target = true;
42248             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42249             s.parentNode.removeChild(s); // remove it
42250             this.render(this.el.parentNode);
42251         }else{
42252             s.parentNode.removeChild(s); // remove it
42253         }
42254
42255     }
42256     if (this.store) {
42257         this.store = Roo.factory(this.store, Roo.data);
42258     }
42259     
42260     this.selectedIndex = -1;
42261     if(this.mode == 'local'){
42262         if(config.queryDelay === undefined){
42263             this.queryDelay = 10;
42264         }
42265         if(config.minChars === undefined){
42266             this.minChars = 0;
42267         }
42268     }
42269 };
42270
42271 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42272     /**
42273      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42274      */
42275     /**
42276      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42277      * rendering into an Roo.Editor, defaults to false)
42278      */
42279     /**
42280      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42281      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42282      */
42283     /**
42284      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42285      */
42286     /**
42287      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42288      * the dropdown list (defaults to undefined, with no header element)
42289      */
42290
42291      /**
42292      * @cfg {String/Roo.Template} tpl The template to use to render the output
42293      */
42294      
42295     // private
42296     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42297     /**
42298      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42299      */
42300     listWidth: undefined,
42301     /**
42302      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42303      * mode = 'remote' or 'text' if mode = 'local')
42304      */
42305     displayField: undefined,
42306     /**
42307      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42308      * mode = 'remote' or 'value' if mode = 'local'). 
42309      * Note: use of a valueField requires the user make a selection
42310      * in order for a value to be mapped.
42311      */
42312     valueField: undefined,
42313     
42314     
42315     /**
42316      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42317      * field's data value (defaults to the underlying DOM element's name)
42318      */
42319     hiddenName: undefined,
42320     /**
42321      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42322      */
42323     listClass: '',
42324     /**
42325      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42326      */
42327     selectedClass: 'x-combo-selected',
42328     /**
42329      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42330      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42331      * which displays a downward arrow icon).
42332      */
42333     triggerClass : 'x-form-arrow-trigger',
42334     /**
42335      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42336      */
42337     shadow:'sides',
42338     /**
42339      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42340      * anchor positions (defaults to 'tl-bl')
42341      */
42342     listAlign: 'tl-bl?',
42343     /**
42344      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42345      */
42346     maxHeight: 300,
42347     /**
42348      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42349      * query specified by the allQuery config option (defaults to 'query')
42350      */
42351     triggerAction: 'query',
42352     /**
42353      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42354      * (defaults to 4, does not apply if editable = false)
42355      */
42356     minChars : 4,
42357     /**
42358      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42359      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42360      */
42361     typeAhead: false,
42362     /**
42363      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42364      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42365      */
42366     queryDelay: 500,
42367     /**
42368      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42369      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42370      */
42371     pageSize: 0,
42372     /**
42373      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42374      * when editable = true (defaults to false)
42375      */
42376     selectOnFocus:false,
42377     /**
42378      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42379      */
42380     queryParam: 'query',
42381     /**
42382      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42383      * when mode = 'remote' (defaults to 'Loading...')
42384      */
42385     loadingText: 'Loading...',
42386     /**
42387      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42388      */
42389     resizable: false,
42390     /**
42391      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42392      */
42393     handleHeight : 8,
42394     /**
42395      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42396      * traditional select (defaults to true)
42397      */
42398     editable: true,
42399     /**
42400      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42401      */
42402     allQuery: '',
42403     /**
42404      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42405      */
42406     mode: 'remote',
42407     /**
42408      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42409      * listWidth has a higher value)
42410      */
42411     minListWidth : 70,
42412     /**
42413      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42414      * allow the user to set arbitrary text into the field (defaults to false)
42415      */
42416     forceSelection:false,
42417     /**
42418      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42419      * if typeAhead = true (defaults to 250)
42420      */
42421     typeAheadDelay : 250,
42422     /**
42423      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42424      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42425      */
42426     valueNotFoundText : undefined,
42427     /**
42428      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42429      */
42430     blockFocus : false,
42431     
42432     /**
42433      * @cfg {Boolean} disableClear Disable showing of clear button.
42434      */
42435     disableClear : false,
42436     /**
42437      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42438      */
42439     alwaysQuery : false,
42440     
42441     //private
42442     addicon : false,
42443     editicon: false,
42444     
42445     // element that contains real text value.. (when hidden is used..)
42446      
42447     // private
42448     onRender : function(ct, position)
42449     {
42450         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42451         
42452         if(this.hiddenName){
42453             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42454                     'before', true);
42455             this.hiddenField.value =
42456                 this.hiddenValue !== undefined ? this.hiddenValue :
42457                 this.value !== undefined ? this.value : '';
42458
42459             // prevent input submission
42460             this.el.dom.removeAttribute('name');
42461              
42462              
42463         }
42464         
42465         if(Roo.isGecko){
42466             this.el.dom.setAttribute('autocomplete', 'off');
42467         }
42468
42469         var cls = 'x-combo-list';
42470
42471         this.list = new Roo.Layer({
42472             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42473         });
42474
42475         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42476         this.list.setWidth(lw);
42477         this.list.swallowEvent('mousewheel');
42478         this.assetHeight = 0;
42479
42480         if(this.title){
42481             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42482             this.assetHeight += this.header.getHeight();
42483         }
42484
42485         this.innerList = this.list.createChild({cls:cls+'-inner'});
42486         this.innerList.on('mouseover', this.onViewOver, this);
42487         this.innerList.on('mousemove', this.onViewMove, this);
42488         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42489         
42490         if(this.allowBlank && !this.pageSize && !this.disableClear){
42491             this.footer = this.list.createChild({cls:cls+'-ft'});
42492             this.pageTb = new Roo.Toolbar(this.footer);
42493            
42494         }
42495         if(this.pageSize){
42496             this.footer = this.list.createChild({cls:cls+'-ft'});
42497             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42498                     {pageSize: this.pageSize});
42499             
42500         }
42501         
42502         if (this.pageTb && this.allowBlank && !this.disableClear) {
42503             var _this = this;
42504             this.pageTb.add(new Roo.Toolbar.Fill(), {
42505                 cls: 'x-btn-icon x-btn-clear',
42506                 text: '&#160;',
42507                 handler: function()
42508                 {
42509                     _this.collapse();
42510                     _this.clearValue();
42511                     _this.onSelect(false, -1);
42512                 }
42513             });
42514         }
42515         if (this.footer) {
42516             this.assetHeight += this.footer.getHeight();
42517         }
42518         
42519
42520         if(!this.tpl){
42521             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42522         }
42523
42524         this.view = new Roo.View(this.innerList, this.tpl, {
42525             singleSelect:true,
42526             store: this.store,
42527             selectedClass: this.selectedClass
42528         });
42529
42530         this.view.on('click', this.onViewClick, this);
42531
42532         this.store.on('beforeload', this.onBeforeLoad, this);
42533         this.store.on('load', this.onLoad, this);
42534         this.store.on('loadexception', this.onLoadException, this);
42535
42536         if(this.resizable){
42537             this.resizer = new Roo.Resizable(this.list,  {
42538                pinned:true, handles:'se'
42539             });
42540             this.resizer.on('resize', function(r, w, h){
42541                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42542                 this.listWidth = w;
42543                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42544                 this.restrictHeight();
42545             }, this);
42546             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42547         }
42548         if(!this.editable){
42549             this.editable = true;
42550             this.setEditable(false);
42551         }  
42552         
42553         
42554         if (typeof(this.events.add.listeners) != 'undefined') {
42555             
42556             this.addicon = this.wrap.createChild(
42557                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42558        
42559             this.addicon.on('click', function(e) {
42560                 this.fireEvent('add', this);
42561             }, this);
42562         }
42563         if (typeof(this.events.edit.listeners) != 'undefined') {
42564             
42565             this.editicon = this.wrap.createChild(
42566                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42567             if (this.addicon) {
42568                 this.editicon.setStyle('margin-left', '40px');
42569             }
42570             this.editicon.on('click', function(e) {
42571                 
42572                 // we fire even  if inothing is selected..
42573                 this.fireEvent('edit', this, this.lastData );
42574                 
42575             }, this);
42576         }
42577         
42578         
42579         
42580     },
42581
42582     // private
42583     initEvents : function(){
42584         Roo.form.ComboBox.superclass.initEvents.call(this);
42585
42586         this.keyNav = new Roo.KeyNav(this.el, {
42587             "up" : function(e){
42588                 this.inKeyMode = true;
42589                 this.selectPrev();
42590             },
42591
42592             "down" : function(e){
42593                 if(!this.isExpanded()){
42594                     this.onTriggerClick();
42595                 }else{
42596                     this.inKeyMode = true;
42597                     this.selectNext();
42598                 }
42599             },
42600
42601             "enter" : function(e){
42602                 this.onViewClick();
42603                 //return true;
42604             },
42605
42606             "esc" : function(e){
42607                 this.collapse();
42608             },
42609
42610             "tab" : function(e){
42611                 this.onViewClick(false);
42612                 this.fireEvent("specialkey", this, e);
42613                 return true;
42614             },
42615
42616             scope : this,
42617
42618             doRelay : function(foo, bar, hname){
42619                 if(hname == 'down' || this.scope.isExpanded()){
42620                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42621                 }
42622                 return true;
42623             },
42624
42625             forceKeyDown: true
42626         });
42627         this.queryDelay = Math.max(this.queryDelay || 10,
42628                 this.mode == 'local' ? 10 : 250);
42629         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42630         if(this.typeAhead){
42631             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42632         }
42633         if(this.editable !== false){
42634             this.el.on("keyup", this.onKeyUp, this);
42635         }
42636         if(this.forceSelection){
42637             this.on('blur', this.doForce, this);
42638         }
42639     },
42640
42641     onDestroy : function(){
42642         if(this.view){
42643             this.view.setStore(null);
42644             this.view.el.removeAllListeners();
42645             this.view.el.remove();
42646             this.view.purgeListeners();
42647         }
42648         if(this.list){
42649             this.list.destroy();
42650         }
42651         if(this.store){
42652             this.store.un('beforeload', this.onBeforeLoad, this);
42653             this.store.un('load', this.onLoad, this);
42654             this.store.un('loadexception', this.onLoadException, this);
42655         }
42656         Roo.form.ComboBox.superclass.onDestroy.call(this);
42657     },
42658
42659     // private
42660     fireKey : function(e){
42661         if(e.isNavKeyPress() && !this.list.isVisible()){
42662             this.fireEvent("specialkey", this, e);
42663         }
42664     },
42665
42666     // private
42667     onResize: function(w, h){
42668         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42669         
42670         if(typeof w != 'number'){
42671             // we do not handle it!?!?
42672             return;
42673         }
42674         var tw = this.trigger.getWidth();
42675         tw += this.addicon ? this.addicon.getWidth() : 0;
42676         tw += this.editicon ? this.editicon.getWidth() : 0;
42677         var x = w - tw;
42678         this.el.setWidth( this.adjustWidth('input', x));
42679             
42680         this.trigger.setStyle('left', x+'px');
42681         
42682         if(this.list && this.listWidth === undefined){
42683             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42684             this.list.setWidth(lw);
42685             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42686         }
42687         
42688     
42689         
42690     },
42691
42692     /**
42693      * Allow or prevent the user from directly editing the field text.  If false is passed,
42694      * the user will only be able to select from the items defined in the dropdown list.  This method
42695      * is the runtime equivalent of setting the 'editable' config option at config time.
42696      * @param {Boolean} value True to allow the user to directly edit the field text
42697      */
42698     setEditable : function(value){
42699         if(value == this.editable){
42700             return;
42701         }
42702         this.editable = value;
42703         if(!value){
42704             this.el.dom.setAttribute('readOnly', true);
42705             this.el.on('mousedown', this.onTriggerClick,  this);
42706             this.el.addClass('x-combo-noedit');
42707         }else{
42708             this.el.dom.setAttribute('readOnly', false);
42709             this.el.un('mousedown', this.onTriggerClick,  this);
42710             this.el.removeClass('x-combo-noedit');
42711         }
42712     },
42713
42714     // private
42715     onBeforeLoad : function(){
42716         if(!this.hasFocus){
42717             return;
42718         }
42719         this.innerList.update(this.loadingText ?
42720                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42721         this.restrictHeight();
42722         this.selectedIndex = -1;
42723     },
42724
42725     // private
42726     onLoad : function(){
42727         if(!this.hasFocus){
42728             return;
42729         }
42730         if(this.store.getCount() > 0){
42731             this.expand();
42732             this.restrictHeight();
42733             if(this.lastQuery == this.allQuery){
42734                 if(this.editable){
42735                     this.el.dom.select();
42736                 }
42737                 if(!this.selectByValue(this.value, true)){
42738                     this.select(0, true);
42739                 }
42740             }else{
42741                 this.selectNext();
42742                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42743                     this.taTask.delay(this.typeAheadDelay);
42744                 }
42745             }
42746         }else{
42747             this.onEmptyResults();
42748         }
42749         //this.el.focus();
42750     },
42751     // private
42752     onLoadException : function()
42753     {
42754         this.collapse();
42755         Roo.log(this.store.reader.jsonData);
42756         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42757             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42758         }
42759         
42760         
42761     },
42762     // private
42763     onTypeAhead : function(){
42764         if(this.store.getCount() > 0){
42765             var r = this.store.getAt(0);
42766             var newValue = r.data[this.displayField];
42767             var len = newValue.length;
42768             var selStart = this.getRawValue().length;
42769             if(selStart != len){
42770                 this.setRawValue(newValue);
42771                 this.selectText(selStart, newValue.length);
42772             }
42773         }
42774     },
42775
42776     // private
42777     onSelect : function(record, index){
42778         if(this.fireEvent('beforeselect', this, record, index) !== false){
42779             this.setFromData(index > -1 ? record.data : false);
42780             this.collapse();
42781             this.fireEvent('select', this, record, index);
42782         }
42783     },
42784
42785     /**
42786      * Returns the currently selected field value or empty string if no value is set.
42787      * @return {String} value The selected value
42788      */
42789     getValue : function(){
42790         if(this.valueField){
42791             return typeof this.value != 'undefined' ? this.value : '';
42792         }
42793         return Roo.form.ComboBox.superclass.getValue.call(this);
42794     },
42795
42796     /**
42797      * Clears any text/value currently set in the field
42798      */
42799     clearValue : function(){
42800         if(this.hiddenField){
42801             this.hiddenField.value = '';
42802         }
42803         this.value = '';
42804         this.setRawValue('');
42805         this.lastSelectionText = '';
42806         
42807     },
42808
42809     /**
42810      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42811      * will be displayed in the field.  If the value does not match the data value of an existing item,
42812      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42813      * Otherwise the field will be blank (although the value will still be set).
42814      * @param {String} value The value to match
42815      */
42816     setValue : function(v){
42817         var text = v;
42818         if(this.valueField){
42819             var r = this.findRecord(this.valueField, v);
42820             if(r){
42821                 text = r.data[this.displayField];
42822             }else if(this.valueNotFoundText !== undefined){
42823                 text = this.valueNotFoundText;
42824             }
42825         }
42826         this.lastSelectionText = text;
42827         if(this.hiddenField){
42828             this.hiddenField.value = v;
42829         }
42830         Roo.form.ComboBox.superclass.setValue.call(this, text);
42831         this.value = v;
42832     },
42833     /**
42834      * @property {Object} the last set data for the element
42835      */
42836     
42837     lastData : false,
42838     /**
42839      * Sets the value of the field based on a object which is related to the record format for the store.
42840      * @param {Object} value the value to set as. or false on reset?
42841      */
42842     setFromData : function(o){
42843         var dv = ''; // display value
42844         var vv = ''; // value value..
42845         this.lastData = o;
42846         if (this.displayField) {
42847             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42848         } else {
42849             // this is an error condition!!!
42850             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42851         }
42852         
42853         if(this.valueField){
42854             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42855         }
42856         if(this.hiddenField){
42857             this.hiddenField.value = vv;
42858             
42859             this.lastSelectionText = dv;
42860             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42861             this.value = vv;
42862             return;
42863         }
42864         // no hidden field.. - we store the value in 'value', but still display
42865         // display field!!!!
42866         this.lastSelectionText = dv;
42867         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42868         this.value = vv;
42869         
42870         
42871     },
42872     // private
42873     reset : function(){
42874         // overridden so that last data is reset..
42875         this.setValue(this.resetValue);
42876         this.originalValue = this.getValue();
42877         this.clearInvalid();
42878         this.lastData = false;
42879         if (this.view) {
42880             this.view.clearSelections();
42881         }
42882     },
42883     // private
42884     findRecord : function(prop, value){
42885         var record;
42886         if(this.store.getCount() > 0){
42887             this.store.each(function(r){
42888                 if(r.data[prop] == value){
42889                     record = r;
42890                     return false;
42891                 }
42892                 return true;
42893             });
42894         }
42895         return record;
42896     },
42897     
42898     getName: function()
42899     {
42900         // returns hidden if it's set..
42901         if (!this.rendered) {return ''};
42902         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42903         
42904     },
42905     // private
42906     onViewMove : function(e, t){
42907         this.inKeyMode = false;
42908     },
42909
42910     // private
42911     onViewOver : function(e, t){
42912         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42913             return;
42914         }
42915         var item = this.view.findItemFromChild(t);
42916         if(item){
42917             var index = this.view.indexOf(item);
42918             this.select(index, false);
42919         }
42920     },
42921
42922     // private
42923     onViewClick : function(doFocus)
42924     {
42925         var index = this.view.getSelectedIndexes()[0];
42926         var r = this.store.getAt(index);
42927         if(r){
42928             this.onSelect(r, index);
42929         }
42930         if(doFocus !== false && !this.blockFocus){
42931             this.el.focus();
42932         }
42933     },
42934
42935     // private
42936     restrictHeight : function(){
42937         this.innerList.dom.style.height = '';
42938         var inner = this.innerList.dom;
42939         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
42940         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
42941         this.list.beginUpdate();
42942         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
42943         this.list.alignTo(this.el, this.listAlign);
42944         this.list.endUpdate();
42945     },
42946
42947     // private
42948     onEmptyResults : function(){
42949         this.collapse();
42950     },
42951
42952     /**
42953      * Returns true if the dropdown list is expanded, else false.
42954      */
42955     isExpanded : function(){
42956         return this.list.isVisible();
42957     },
42958
42959     /**
42960      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
42961      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42962      * @param {String} value The data value of the item to select
42963      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42964      * selected item if it is not currently in view (defaults to true)
42965      * @return {Boolean} True if the value matched an item in the list, else false
42966      */
42967     selectByValue : function(v, scrollIntoView){
42968         if(v !== undefined && v !== null){
42969             var r = this.findRecord(this.valueField || this.displayField, v);
42970             if(r){
42971                 this.select(this.store.indexOf(r), scrollIntoView);
42972                 return true;
42973             }
42974         }
42975         return false;
42976     },
42977
42978     /**
42979      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
42980      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
42981      * @param {Number} index The zero-based index of the list item to select
42982      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
42983      * selected item if it is not currently in view (defaults to true)
42984      */
42985     select : function(index, scrollIntoView){
42986         this.selectedIndex = index;
42987         this.view.select(index);
42988         if(scrollIntoView !== false){
42989             var el = this.view.getNode(index);
42990             if(el){
42991                 this.innerList.scrollChildIntoView(el, false);
42992             }
42993         }
42994     },
42995
42996     // private
42997     selectNext : function(){
42998         var ct = this.store.getCount();
42999         if(ct > 0){
43000             if(this.selectedIndex == -1){
43001                 this.select(0);
43002             }else if(this.selectedIndex < ct-1){
43003                 this.select(this.selectedIndex+1);
43004             }
43005         }
43006     },
43007
43008     // private
43009     selectPrev : function(){
43010         var ct = this.store.getCount();
43011         if(ct > 0){
43012             if(this.selectedIndex == -1){
43013                 this.select(0);
43014             }else if(this.selectedIndex != 0){
43015                 this.select(this.selectedIndex-1);
43016             }
43017         }
43018     },
43019
43020     // private
43021     onKeyUp : function(e){
43022         if(this.editable !== false && !e.isSpecialKey()){
43023             this.lastKey = e.getKey();
43024             this.dqTask.delay(this.queryDelay);
43025         }
43026     },
43027
43028     // private
43029     validateBlur : function(){
43030         return !this.list || !this.list.isVisible();   
43031     },
43032
43033     // private
43034     initQuery : function(){
43035         this.doQuery(this.getRawValue());
43036     },
43037
43038     // private
43039     doForce : function(){
43040         if(this.el.dom.value.length > 0){
43041             this.el.dom.value =
43042                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43043              
43044         }
43045     },
43046
43047     /**
43048      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43049      * query allowing the query action to be canceled if needed.
43050      * @param {String} query The SQL query to execute
43051      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43052      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43053      * saved in the current store (defaults to false)
43054      */
43055     doQuery : function(q, forceAll){
43056         if(q === undefined || q === null){
43057             q = '';
43058         }
43059         var qe = {
43060             query: q,
43061             forceAll: forceAll,
43062             combo: this,
43063             cancel:false
43064         };
43065         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43066             return false;
43067         }
43068         q = qe.query;
43069         forceAll = qe.forceAll;
43070         if(forceAll === true || (q.length >= this.minChars)){
43071             if(this.lastQuery != q || this.alwaysQuery){
43072                 this.lastQuery = q;
43073                 if(this.mode == 'local'){
43074                     this.selectedIndex = -1;
43075                     if(forceAll){
43076                         this.store.clearFilter();
43077                     }else{
43078                         this.store.filter(this.displayField, q);
43079                     }
43080                     this.onLoad();
43081                 }else{
43082                     this.store.baseParams[this.queryParam] = q;
43083                     this.store.load({
43084                         params: this.getParams(q)
43085                     });
43086                     this.expand();
43087                 }
43088             }else{
43089                 this.selectedIndex = -1;
43090                 this.onLoad();   
43091             }
43092         }
43093     },
43094
43095     // private
43096     getParams : function(q){
43097         var p = {};
43098         //p[this.queryParam] = q;
43099         if(this.pageSize){
43100             p.start = 0;
43101             p.limit = this.pageSize;
43102         }
43103         return p;
43104     },
43105
43106     /**
43107      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43108      */
43109     collapse : function(){
43110         if(!this.isExpanded()){
43111             return;
43112         }
43113         this.list.hide();
43114         Roo.get(document).un('mousedown', this.collapseIf, this);
43115         Roo.get(document).un('mousewheel', this.collapseIf, this);
43116         if (!this.editable) {
43117             Roo.get(document).un('keydown', this.listKeyPress, this);
43118         }
43119         this.fireEvent('collapse', this);
43120     },
43121
43122     // private
43123     collapseIf : function(e){
43124         if(!e.within(this.wrap) && !e.within(this.list)){
43125             this.collapse();
43126         }
43127     },
43128
43129     /**
43130      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43131      */
43132     expand : function(){
43133         if(this.isExpanded() || !this.hasFocus){
43134             return;
43135         }
43136         this.list.alignTo(this.el, this.listAlign);
43137         this.list.show();
43138         Roo.get(document).on('mousedown', this.collapseIf, this);
43139         Roo.get(document).on('mousewheel', this.collapseIf, this);
43140         if (!this.editable) {
43141             Roo.get(document).on('keydown', this.listKeyPress, this);
43142         }
43143         
43144         this.fireEvent('expand', this);
43145     },
43146
43147     // private
43148     // Implements the default empty TriggerField.onTriggerClick function
43149     onTriggerClick : function(){
43150         if(this.disabled){
43151             return;
43152         }
43153         if(this.isExpanded()){
43154             this.collapse();
43155             if (!this.blockFocus) {
43156                 this.el.focus();
43157             }
43158             
43159         }else {
43160             this.hasFocus = true;
43161             if(this.triggerAction == 'all') {
43162                 this.doQuery(this.allQuery, true);
43163             } else {
43164                 this.doQuery(this.getRawValue());
43165             }
43166             if (!this.blockFocus) {
43167                 this.el.focus();
43168             }
43169         }
43170     },
43171     listKeyPress : function(e)
43172     {
43173         //Roo.log('listkeypress');
43174         // scroll to first matching element based on key pres..
43175         if (e.isSpecialKey()) {
43176             return false;
43177         }
43178         var k = String.fromCharCode(e.getKey()).toUpperCase();
43179         //Roo.log(k);
43180         var match  = false;
43181         var csel = this.view.getSelectedNodes();
43182         var cselitem = false;
43183         if (csel.length) {
43184             var ix = this.view.indexOf(csel[0]);
43185             cselitem  = this.store.getAt(ix);
43186             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43187                 cselitem = false;
43188             }
43189             
43190         }
43191         
43192         this.store.each(function(v) { 
43193             if (cselitem) {
43194                 // start at existing selection.
43195                 if (cselitem.id == v.id) {
43196                     cselitem = false;
43197                 }
43198                 return;
43199             }
43200                 
43201             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43202                 match = this.store.indexOf(v);
43203                 return false;
43204             }
43205         }, this);
43206         
43207         if (match === false) {
43208             return true; // no more action?
43209         }
43210         // scroll to?
43211         this.view.select(match);
43212         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43213         sn.scrollIntoView(sn.dom.parentNode, false);
43214     } 
43215
43216     /** 
43217     * @cfg {Boolean} grow 
43218     * @hide 
43219     */
43220     /** 
43221     * @cfg {Number} growMin 
43222     * @hide 
43223     */
43224     /** 
43225     * @cfg {Number} growMax 
43226     * @hide 
43227     */
43228     /**
43229      * @hide
43230      * @method autoSize
43231      */
43232 });/*
43233  * Copyright(c) 2010-2012, Roo J Solutions Limited
43234  *
43235  * Licence LGPL
43236  *
43237  */
43238
43239 /**
43240  * @class Roo.form.ComboBoxArray
43241  * @extends Roo.form.TextField
43242  * A facebook style adder... for lists of email / people / countries  etc...
43243  * pick multiple items from a combo box, and shows each one.
43244  *
43245  *  Fred [x]  Brian [x]  [Pick another |v]
43246  *
43247  *
43248  *  For this to work: it needs various extra information
43249  *    - normal combo problay has
43250  *      name, hiddenName
43251  *    + displayField, valueField
43252  *
43253  *    For our purpose...
43254  *
43255  *
43256  *   If we change from 'extends' to wrapping...
43257  *   
43258  *  
43259  *
43260  
43261  
43262  * @constructor
43263  * Create a new ComboBoxArray.
43264  * @param {Object} config Configuration options
43265  */
43266  
43267
43268 Roo.form.ComboBoxArray = function(config)
43269 {
43270     this.addEvents({
43271         /**
43272          * @event beforeremove
43273          * Fires before remove the value from the list
43274              * @param {Roo.form.ComboBoxArray} _self This combo box array
43275              * @param {Roo.form.ComboBoxArray.Item} item removed item
43276              */
43277         'beforeremove' : true,
43278         /**
43279          * @event remove
43280          * Fires when remove the value from the list
43281              * @param {Roo.form.ComboBoxArray} _self This combo box array
43282              * @param {Roo.form.ComboBoxArray.Item} item removed item
43283              */
43284         'remove' : true
43285         
43286         
43287     });
43288     
43289     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43290     
43291     this.items = new Roo.util.MixedCollection(false);
43292     
43293     // construct the child combo...
43294     
43295     
43296     
43297     
43298    
43299     
43300 }
43301
43302  
43303 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43304
43305     /**
43306      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43307      */
43308     
43309     lastData : false,
43310     
43311     // behavies liek a hiddne field
43312     inputType:      'hidden',
43313     /**
43314      * @cfg {Number} width The width of the box that displays the selected element
43315      */ 
43316     width:          300,
43317
43318     
43319     
43320     /**
43321      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43322      */
43323     name : false,
43324     /**
43325      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43326      */
43327     hiddenName : false,
43328       /**
43329      * @cfg {String} seperator    The value seperator normally ',' 
43330      */
43331     seperator : ',',
43332     
43333     // private the array of items that are displayed..
43334     items  : false,
43335     // private - the hidden field el.
43336     hiddenEl : false,
43337     // private - the filed el..
43338     el : false,
43339     
43340     //validateValue : function() { return true; }, // all values are ok!
43341     //onAddClick: function() { },
43342     
43343     onRender : function(ct, position) 
43344     {
43345         
43346         // create the standard hidden element
43347         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43348         
43349         
43350         // give fake names to child combo;
43351         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43352         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43353         
43354         this.combo = Roo.factory(this.combo, Roo.form);
43355         this.combo.onRender(ct, position);
43356         if (typeof(this.combo.width) != 'undefined') {
43357             this.combo.onResize(this.combo.width,0);
43358         }
43359         
43360         this.combo.initEvents();
43361         
43362         // assigned so form know we need to do this..
43363         this.store          = this.combo.store;
43364         this.valueField     = this.combo.valueField;
43365         this.displayField   = this.combo.displayField ;
43366         
43367         
43368         this.combo.wrap.addClass('x-cbarray-grp');
43369         
43370         var cbwrap = this.combo.wrap.createChild(
43371             {tag: 'div', cls: 'x-cbarray-cb'},
43372             this.combo.el.dom
43373         );
43374         
43375              
43376         this.hiddenEl = this.combo.wrap.createChild({
43377             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43378         });
43379         this.el = this.combo.wrap.createChild({
43380             tag: 'input',  type:'hidden' , name: this.name, value : ''
43381         });
43382          //   this.el.dom.removeAttribute("name");
43383         
43384         
43385         this.outerWrap = this.combo.wrap;
43386         this.wrap = cbwrap;
43387         
43388         this.outerWrap.setWidth(this.width);
43389         this.outerWrap.dom.removeChild(this.el.dom);
43390         
43391         this.wrap.dom.appendChild(this.el.dom);
43392         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43393         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43394         
43395         this.combo.trigger.setStyle('position','relative');
43396         this.combo.trigger.setStyle('left', '0px');
43397         this.combo.trigger.setStyle('top', '2px');
43398         
43399         this.combo.el.setStyle('vertical-align', 'text-bottom');
43400         
43401         //this.trigger.setStyle('vertical-align', 'top');
43402         
43403         // this should use the code from combo really... on('add' ....)
43404         if (this.adder) {
43405             
43406         
43407             this.adder = this.outerWrap.createChild(
43408                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43409             var _t = this;
43410             this.adder.on('click', function(e) {
43411                 _t.fireEvent('adderclick', this, e);
43412             }, _t);
43413         }
43414         //var _t = this;
43415         //this.adder.on('click', this.onAddClick, _t);
43416         
43417         
43418         this.combo.on('select', function(cb, rec, ix) {
43419             this.addItem(rec.data);
43420             
43421             cb.setValue('');
43422             cb.el.dom.value = '';
43423             //cb.lastData = rec.data;
43424             // add to list
43425             
43426         }, this);
43427         
43428         
43429     },
43430     
43431     
43432     getName: function()
43433     {
43434         // returns hidden if it's set..
43435         if (!this.rendered) {return ''};
43436         return  this.hiddenName ? this.hiddenName : this.name;
43437         
43438     },
43439     
43440     
43441     onResize: function(w, h){
43442         
43443         return;
43444         // not sure if this is needed..
43445         //this.combo.onResize(w,h);
43446         
43447         if(typeof w != 'number'){
43448             // we do not handle it!?!?
43449             return;
43450         }
43451         var tw = this.combo.trigger.getWidth();
43452         tw += this.addicon ? this.addicon.getWidth() : 0;
43453         tw += this.editicon ? this.editicon.getWidth() : 0;
43454         var x = w - tw;
43455         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43456             
43457         this.combo.trigger.setStyle('left', '0px');
43458         
43459         if(this.list && this.listWidth === undefined){
43460             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43461             this.list.setWidth(lw);
43462             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43463         }
43464         
43465     
43466         
43467     },
43468     
43469     addItem: function(rec)
43470     {
43471         var valueField = this.combo.valueField;
43472         var displayField = this.combo.displayField;
43473         
43474         if (this.items.indexOfKey(rec[valueField]) > -1) {
43475             //console.log("GOT " + rec.data.id);
43476             return;
43477         }
43478         
43479         var x = new Roo.form.ComboBoxArray.Item({
43480             //id : rec[this.idField],
43481             data : rec,
43482             displayField : displayField ,
43483             tipField : displayField ,
43484             cb : this
43485         });
43486         // use the 
43487         this.items.add(rec[valueField],x);
43488         // add it before the element..
43489         this.updateHiddenEl();
43490         x.render(this.outerWrap, this.wrap.dom);
43491         // add the image handler..
43492     },
43493     
43494     updateHiddenEl : function()
43495     {
43496         this.validate();
43497         if (!this.hiddenEl) {
43498             return;
43499         }
43500         var ar = [];
43501         var idField = this.combo.valueField;
43502         
43503         this.items.each(function(f) {
43504             ar.push(f.data[idField]);
43505         });
43506         this.hiddenEl.dom.value = ar.join(this.seperator);
43507         this.validate();
43508     },
43509     
43510     reset : function()
43511     {
43512         this.items.clear();
43513         
43514         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43515            el.remove();
43516         });
43517         
43518         this.el.dom.value = '';
43519         if (this.hiddenEl) {
43520             this.hiddenEl.dom.value = '';
43521         }
43522         
43523     },
43524     getValue: function()
43525     {
43526         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43527     },
43528     setValue: function(v) // not a valid action - must use addItems..
43529     {
43530         
43531         this.reset();
43532          
43533         if (this.store.isLocal && (typeof(v) == 'string')) {
43534             // then we can use the store to find the values..
43535             // comma seperated at present.. this needs to allow JSON based encoding..
43536             this.hiddenEl.value  = v;
43537             var v_ar = [];
43538             Roo.each(v.split(this.seperator), function(k) {
43539                 Roo.log("CHECK " + this.valueField + ',' + k);
43540                 var li = this.store.query(this.valueField, k);
43541                 if (!li.length) {
43542                     return;
43543                 }
43544                 var add = {};
43545                 add[this.valueField] = k;
43546                 add[this.displayField] = li.item(0).data[this.displayField];
43547                 
43548                 this.addItem(add);
43549             }, this) 
43550              
43551         }
43552         if (typeof(v) == 'object' ) {
43553             // then let's assume it's an array of objects..
43554             Roo.each(v, function(l) {
43555                 var add = l;
43556                 if (typeof(l) == 'string') {
43557                     add = {};
43558                     add[this.valueField] = l;
43559                     add[this.displayField] = l
43560                 }
43561                 this.addItem(add);
43562             }, this);
43563              
43564         }
43565         
43566         
43567     },
43568     setFromData: function(v)
43569     {
43570         // this recieves an object, if setValues is called.
43571         this.reset();
43572         this.el.dom.value = v[this.displayField];
43573         this.hiddenEl.dom.value = v[this.valueField];
43574         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43575             return;
43576         }
43577         var kv = v[this.valueField];
43578         var dv = v[this.displayField];
43579         kv = typeof(kv) != 'string' ? '' : kv;
43580         dv = typeof(dv) != 'string' ? '' : dv;
43581         
43582         
43583         var keys = kv.split(this.seperator);
43584         var display = dv.split(this.seperator);
43585         for (var i = 0 ; i < keys.length; i++) {
43586             add = {};
43587             add[this.valueField] = keys[i];
43588             add[this.displayField] = display[i];
43589             this.addItem(add);
43590         }
43591       
43592         
43593     },
43594     
43595     /**
43596      * Validates the combox array value
43597      * @return {Boolean} True if the value is valid, else false
43598      */
43599     validate : function(){
43600         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43601             this.clearInvalid();
43602             return true;
43603         }
43604         return false;
43605     },
43606     
43607     validateValue : function(value){
43608         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43609         
43610     },
43611     
43612     /*@
43613      * overide
43614      * 
43615      */
43616     isDirty : function() {
43617         if(this.disabled) {
43618             return false;
43619         }
43620         
43621         try {
43622             var d = Roo.decode(String(this.originalValue));
43623         } catch (e) {
43624             return String(this.getValue()) !== String(this.originalValue);
43625         }
43626         
43627         var originalValue = [];
43628         
43629         for (var i = 0; i < d.length; i++){
43630             originalValue.push(d[i][this.valueField]);
43631         }
43632         
43633         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43634         
43635     }
43636     
43637 });
43638
43639
43640
43641 /**
43642  * @class Roo.form.ComboBoxArray.Item
43643  * @extends Roo.BoxComponent
43644  * A selected item in the list
43645  *  Fred [x]  Brian [x]  [Pick another |v]
43646  * 
43647  * @constructor
43648  * Create a new item.
43649  * @param {Object} config Configuration options
43650  */
43651  
43652 Roo.form.ComboBoxArray.Item = function(config) {
43653     config.id = Roo.id();
43654     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43655 }
43656
43657 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43658     data : {},
43659     cb: false,
43660     displayField : false,
43661     tipField : false,
43662     
43663     
43664     defaultAutoCreate : {
43665         tag: 'div',
43666         cls: 'x-cbarray-item',
43667         cn : [ 
43668             { tag: 'div' },
43669             {
43670                 tag: 'img',
43671                 width:16,
43672                 height : 16,
43673                 src : Roo.BLANK_IMAGE_URL ,
43674                 align: 'center'
43675             }
43676         ]
43677         
43678     },
43679     
43680  
43681     onRender : function(ct, position)
43682     {
43683         Roo.form.Field.superclass.onRender.call(this, ct, position);
43684         
43685         if(!this.el){
43686             var cfg = this.getAutoCreate();
43687             this.el = ct.createChild(cfg, position);
43688         }
43689         
43690         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43691         
43692         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43693             this.cb.renderer(this.data) :
43694             String.format('{0}',this.data[this.displayField]);
43695         
43696             
43697         this.el.child('div').dom.setAttribute('qtip',
43698                         String.format('{0}',this.data[this.tipField])
43699         );
43700         
43701         this.el.child('img').on('click', this.remove, this);
43702         
43703     },
43704    
43705     remove : function()
43706     {
43707         if(this.cb.disabled){
43708             return;
43709         }
43710         
43711         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43712             this.cb.items.remove(this);
43713             this.el.child('img').un('click', this.remove, this);
43714             this.el.remove();
43715             this.cb.updateHiddenEl();
43716
43717             this.cb.fireEvent('remove', this.cb, this);
43718         }
43719         
43720     }
43721 });/*
43722  * RooJS Library 1.1.1
43723  * Copyright(c) 2008-2011  Alan Knowles
43724  *
43725  * License - LGPL
43726  */
43727  
43728
43729 /**
43730  * @class Roo.form.ComboNested
43731  * @extends Roo.form.ComboBox
43732  * A combobox for that allows selection of nested items in a list,
43733  * eg.
43734  *
43735  *  Book
43736  *    -> red
43737  *    -> green
43738  *  Table
43739  *    -> square
43740  *      ->red
43741  *      ->green
43742  *    -> rectangle
43743  *      ->green
43744  *      
43745  * 
43746  * @constructor
43747  * Create a new ComboNested
43748  * @param {Object} config Configuration options
43749  */
43750 Roo.form.ComboNested = function(config){
43751     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43752     // should verify some data...
43753     // like
43754     // hiddenName = required..
43755     // displayField = required
43756     // valudField == required
43757     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43758     var _t = this;
43759     Roo.each(req, function(e) {
43760         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43761             throw "Roo.form.ComboNested : missing value for: " + e;
43762         }
43763     });
43764      
43765     
43766 };
43767
43768 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43769    
43770     /*
43771      * @config {Number} max Number of columns to show
43772      */
43773     
43774     maxColumns : 3,
43775    
43776     list : null, // the outermost div..
43777     innerLists : null, // the
43778     views : null,
43779     stores : null,
43780     // private
43781     loadingChildren : false,
43782     
43783     onRender : function(ct, position)
43784     {
43785         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43786         
43787         if(this.hiddenName){
43788             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43789                     'before', true);
43790             this.hiddenField.value =
43791                 this.hiddenValue !== undefined ? this.hiddenValue :
43792                 this.value !== undefined ? this.value : '';
43793
43794             // prevent input submission
43795             this.el.dom.removeAttribute('name');
43796              
43797              
43798         }
43799         
43800         if(Roo.isGecko){
43801             this.el.dom.setAttribute('autocomplete', 'off');
43802         }
43803
43804         var cls = 'x-combo-list';
43805
43806         this.list = new Roo.Layer({
43807             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43808         });
43809
43810         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43811         this.list.setWidth(lw);
43812         this.list.swallowEvent('mousewheel');
43813         this.assetHeight = 0;
43814
43815         if(this.title){
43816             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43817             this.assetHeight += this.header.getHeight();
43818         }
43819         this.innerLists = [];
43820         this.views = [];
43821         this.stores = [];
43822         for (var i =0 ; i < this.maxColumns; i++) {
43823             this.onRenderList( cls, i);
43824         }
43825         
43826         // always needs footer, as we are going to have an 'OK' button.
43827         this.footer = this.list.createChild({cls:cls+'-ft'});
43828         this.pageTb = new Roo.Toolbar(this.footer);  
43829         var _this = this;
43830         this.pageTb.add(  {
43831             
43832             text: 'Done',
43833             handler: function()
43834             {
43835                 _this.collapse();
43836             }
43837         });
43838         
43839         if ( this.allowBlank && !this.disableClear) {
43840             
43841             this.pageTb.add(new Roo.Toolbar.Fill(), {
43842                 cls: 'x-btn-icon x-btn-clear',
43843                 text: '&#160;',
43844                 handler: function()
43845                 {
43846                     _this.collapse();
43847                     _this.clearValue();
43848                     _this.onSelect(false, -1);
43849                 }
43850             });
43851         }
43852         if (this.footer) {
43853             this.assetHeight += this.footer.getHeight();
43854         }
43855         
43856     },
43857     onRenderList : function (  cls, i)
43858     {
43859         
43860         var lw = Math.floor(
43861                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43862         );
43863         
43864         this.list.setWidth(lw); // default to '1'
43865
43866         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43867         //il.on('mouseover', this.onViewOver, this, { list:  i });
43868         //il.on('mousemove', this.onViewMove, this, { list:  i });
43869         il.setWidth(lw);
43870         il.setStyle({ 'overflow-x' : 'hidden'});
43871
43872         if(!this.tpl){
43873             this.tpl = new Roo.Template({
43874                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43875                 isEmpty: function (value, allValues) {
43876                     //Roo.log(value);
43877                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43878                     return dl ? 'has-children' : 'no-children'
43879                 }
43880             });
43881         }
43882         
43883         var store  = this.store;
43884         if (i > 0) {
43885             store  = new Roo.data.SimpleStore({
43886                 //fields : this.store.reader.meta.fields,
43887                 reader : this.store.reader,
43888                 data : [ ]
43889             });
43890         }
43891         this.stores[i]  = store;
43892                   
43893         var view = this.views[i] = new Roo.View(
43894             il,
43895             this.tpl,
43896             {
43897                 singleSelect:true,
43898                 store: store,
43899                 selectedClass: this.selectedClass
43900             }
43901         );
43902         view.getEl().setWidth(lw);
43903         view.getEl().setStyle({
43904             position: i < 1 ? 'relative' : 'absolute',
43905             top: 0,
43906             left: (i * lw ) + 'px',
43907             display : i > 0 ? 'none' : 'block'
43908         });
43909         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43910         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43911         //view.on('click', this.onViewClick, this, { list : i });
43912
43913         store.on('beforeload', this.onBeforeLoad, this);
43914         store.on('load',  this.onLoad, this, { list  : i});
43915         store.on('loadexception', this.onLoadException, this);
43916
43917         // hide the other vies..
43918         
43919         
43920         
43921     },
43922       
43923     restrictHeight : function()
43924     {
43925         var mh = 0;
43926         Roo.each(this.innerLists, function(il,i) {
43927             var el = this.views[i].getEl();
43928             el.dom.style.height = '';
43929             var inner = el.dom;
43930             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
43931             // only adjust heights on other ones..
43932             mh = Math.max(h, mh);
43933             if (i < 1) {
43934                 
43935                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43936                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43937                
43938             }
43939             
43940             
43941         }, this);
43942         
43943         this.list.beginUpdate();
43944         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
43945         this.list.alignTo(this.el, this.listAlign);
43946         this.list.endUpdate();
43947         
43948     },
43949      
43950     
43951     // -- store handlers..
43952     // private
43953     onBeforeLoad : function()
43954     {
43955         if(!this.hasFocus){
43956             return;
43957         }
43958         this.innerLists[0].update(this.loadingText ?
43959                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
43960         this.restrictHeight();
43961         this.selectedIndex = -1;
43962     },
43963     // private
43964     onLoad : function(a,b,c,d)
43965     {
43966         if (!this.loadingChildren) {
43967             // then we are loading the top level. - hide the children
43968             for (var i = 1;i < this.views.length; i++) {
43969                 this.views[i].getEl().setStyle({ display : 'none' });
43970             }
43971             var lw = Math.floor(
43972                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43973             );
43974         
43975              this.list.setWidth(lw); // default to '1'
43976
43977             
43978         }
43979         if(!this.hasFocus){
43980             return;
43981         }
43982         
43983         if(this.store.getCount() > 0) {
43984             this.expand();
43985             this.restrictHeight();   
43986         } else {
43987             this.onEmptyResults();
43988         }
43989         
43990         if (!this.loadingChildren) {
43991             this.selectActive();
43992         }
43993         /*
43994         this.stores[1].loadData([]);
43995         this.stores[2].loadData([]);
43996         this.views
43997         */    
43998     
43999         //this.el.focus();
44000     },
44001     
44002     
44003     // private
44004     onLoadException : function()
44005     {
44006         this.collapse();
44007         Roo.log(this.store.reader.jsonData);
44008         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44009             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44010         }
44011         
44012         
44013     },
44014     // no cleaning of leading spaces on blur here.
44015     cleanLeadingSpace : function(e) { },
44016     
44017
44018     onSelectChange : function (view, sels, opts )
44019     {
44020         var ix = view.getSelectedIndexes();
44021          
44022         if (opts.list > this.maxColumns - 2) {
44023             if (view.store.getCount()<  1) {
44024                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44025
44026             } else  {
44027                 if (ix.length) {
44028                     // used to clear ?? but if we are loading unselected 
44029                     this.setFromData(view.store.getAt(ix[0]).data);
44030                 }
44031                 
44032             }
44033             
44034             return;
44035         }
44036         
44037         if (!ix.length) {
44038             // this get's fired when trigger opens..
44039            // this.setFromData({});
44040             var str = this.stores[opts.list+1];
44041             str.data.clear(); // removeall wihtout the fire events..
44042             return;
44043         }
44044         
44045         var rec = view.store.getAt(ix[0]);
44046          
44047         this.setFromData(rec.data);
44048         this.fireEvent('select', this, rec, ix[0]);
44049         
44050         var lw = Math.floor(
44051              (
44052                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44053              ) / this.maxColumns
44054         );
44055         this.loadingChildren = true;
44056         this.stores[opts.list+1].loadDataFromChildren( rec );
44057         this.loadingChildren = false;
44058         var dl = this.stores[opts.list+1]. getTotalCount();
44059         
44060         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44061         
44062         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44063         for (var i = opts.list+2; i < this.views.length;i++) {
44064             this.views[i].getEl().setStyle({ display : 'none' });
44065         }
44066         
44067         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44068         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44069         
44070         if (this.isLoading) {
44071            // this.selectActive(opts.list);
44072         }
44073          
44074     },
44075     
44076     
44077     
44078     
44079     onDoubleClick : function()
44080     {
44081         this.collapse(); //??
44082     },
44083     
44084      
44085     
44086     
44087     
44088     // private
44089     recordToStack : function(store, prop, value, stack)
44090     {
44091         var cstore = new Roo.data.SimpleStore({
44092             //fields : this.store.reader.meta.fields, // we need array reader.. for
44093             reader : this.store.reader,
44094             data : [ ]
44095         });
44096         var _this = this;
44097         var record  = false;
44098         var srec = false;
44099         if(store.getCount() < 1){
44100             return false;
44101         }
44102         store.each(function(r){
44103             if(r.data[prop] == value){
44104                 record = r;
44105             srec = r;
44106                 return false;
44107             }
44108             if (r.data.cn && r.data.cn.length) {
44109                 cstore.loadDataFromChildren( r);
44110                 var cret = _this.recordToStack(cstore, prop, value, stack);
44111                 if (cret !== false) {
44112                     record = cret;
44113                     srec = r;
44114                     return false;
44115                 }
44116             }
44117              
44118             return true;
44119         });
44120         if (record == false) {
44121             return false
44122         }
44123         stack.unshift(srec);
44124         return record;
44125     },
44126     
44127     /*
44128      * find the stack of stores that match our value.
44129      *
44130      * 
44131      */
44132     
44133     selectActive : function ()
44134     {
44135         // if store is not loaded, then we will need to wait for that to happen first.
44136         var stack = [];
44137         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44138         for (var i = 0; i < stack.length; i++ ) {
44139             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44140         }
44141         
44142     }
44143         
44144          
44145     
44146     
44147     
44148     
44149 });/*
44150  * Based on:
44151  * Ext JS Library 1.1.1
44152  * Copyright(c) 2006-2007, Ext JS, LLC.
44153  *
44154  * Originally Released Under LGPL - original licence link has changed is not relivant.
44155  *
44156  * Fork - LGPL
44157  * <script type="text/javascript">
44158  */
44159 /**
44160  * @class Roo.form.Checkbox
44161  * @extends Roo.form.Field
44162  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44163  * @constructor
44164  * Creates a new Checkbox
44165  * @param {Object} config Configuration options
44166  */
44167 Roo.form.Checkbox = function(config){
44168     Roo.form.Checkbox.superclass.constructor.call(this, config);
44169     this.addEvents({
44170         /**
44171          * @event check
44172          * Fires when the checkbox is checked or unchecked.
44173              * @param {Roo.form.Checkbox} this This checkbox
44174              * @param {Boolean} checked The new checked value
44175              */
44176         check : true
44177     });
44178 };
44179
44180 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44181     /**
44182      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44183      */
44184     focusClass : undefined,
44185     /**
44186      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44187      */
44188     fieldClass: "x-form-field",
44189     /**
44190      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44191      */
44192     checked: false,
44193     /**
44194      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44195      * {tag: "input", type: "checkbox", autocomplete: "off"})
44196      */
44197     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44198     /**
44199      * @cfg {String} boxLabel The text that appears beside the checkbox
44200      */
44201     boxLabel : "",
44202     /**
44203      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44204      */  
44205     inputValue : '1',
44206     /**
44207      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44208      */
44209      valueOff: '0', // value when not checked..
44210
44211     actionMode : 'viewEl', 
44212     //
44213     // private
44214     itemCls : 'x-menu-check-item x-form-item',
44215     groupClass : 'x-menu-group-item',
44216     inputType : 'hidden',
44217     
44218     
44219     inSetChecked: false, // check that we are not calling self...
44220     
44221     inputElement: false, // real input element?
44222     basedOn: false, // ????
44223     
44224     isFormField: true, // not sure where this is needed!!!!
44225
44226     onResize : function(){
44227         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44228         if(!this.boxLabel){
44229             this.el.alignTo(this.wrap, 'c-c');
44230         }
44231     },
44232
44233     initEvents : function(){
44234         Roo.form.Checkbox.superclass.initEvents.call(this);
44235         this.el.on("click", this.onClick,  this);
44236         this.el.on("change", this.onClick,  this);
44237     },
44238
44239
44240     getResizeEl : function(){
44241         return this.wrap;
44242     },
44243
44244     getPositionEl : function(){
44245         return this.wrap;
44246     },
44247
44248     // private
44249     onRender : function(ct, position){
44250         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44251         /*
44252         if(this.inputValue !== undefined){
44253             this.el.dom.value = this.inputValue;
44254         }
44255         */
44256         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44257         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44258         var viewEl = this.wrap.createChild({ 
44259             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44260         this.viewEl = viewEl;   
44261         this.wrap.on('click', this.onClick,  this); 
44262         
44263         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44264         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44265         
44266         
44267         
44268         if(this.boxLabel){
44269             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44270         //    viewEl.on('click', this.onClick,  this); 
44271         }
44272         //if(this.checked){
44273             this.setChecked(this.checked);
44274         //}else{
44275             //this.checked = this.el.dom;
44276         //}
44277
44278     },
44279
44280     // private
44281     initValue : Roo.emptyFn,
44282
44283     /**
44284      * Returns the checked state of the checkbox.
44285      * @return {Boolean} True if checked, else false
44286      */
44287     getValue : function(){
44288         if(this.el){
44289             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44290         }
44291         return this.valueOff;
44292         
44293     },
44294
44295         // private
44296     onClick : function(){ 
44297         if (this.disabled) {
44298             return;
44299         }
44300         this.setChecked(!this.checked);
44301
44302         //if(this.el.dom.checked != this.checked){
44303         //    this.setValue(this.el.dom.checked);
44304        // }
44305     },
44306
44307     /**
44308      * Sets the checked state of the checkbox.
44309      * On is always based on a string comparison between inputValue and the param.
44310      * @param {Boolean/String} value - the value to set 
44311      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44312      */
44313     setValue : function(v,suppressEvent){
44314         
44315         
44316         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44317         //if(this.el && this.el.dom){
44318         //    this.el.dom.checked = this.checked;
44319         //    this.el.dom.defaultChecked = this.checked;
44320         //}
44321         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44322         //this.fireEvent("check", this, this.checked);
44323     },
44324     // private..
44325     setChecked : function(state,suppressEvent)
44326     {
44327         if (this.inSetChecked) {
44328             this.checked = state;
44329             return;
44330         }
44331         
44332     
44333         if(this.wrap){
44334             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44335         }
44336         this.checked = state;
44337         if(suppressEvent !== true){
44338             this.fireEvent('check', this, state);
44339         }
44340         this.inSetChecked = true;
44341         this.el.dom.value = state ? this.inputValue : this.valueOff;
44342         this.inSetChecked = false;
44343         
44344     },
44345     // handle setting of hidden value by some other method!!?!?
44346     setFromHidden: function()
44347     {
44348         if(!this.el){
44349             return;
44350         }
44351         //console.log("SET FROM HIDDEN");
44352         //alert('setFrom hidden');
44353         this.setValue(this.el.dom.value);
44354     },
44355     
44356     onDestroy : function()
44357     {
44358         if(this.viewEl){
44359             Roo.get(this.viewEl).remove();
44360         }
44361          
44362         Roo.form.Checkbox.superclass.onDestroy.call(this);
44363     },
44364     
44365     setBoxLabel : function(str)
44366     {
44367         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44368     }
44369
44370 });/*
44371  * Based on:
44372  * Ext JS Library 1.1.1
44373  * Copyright(c) 2006-2007, Ext JS, LLC.
44374  *
44375  * Originally Released Under LGPL - original licence link has changed is not relivant.
44376  *
44377  * Fork - LGPL
44378  * <script type="text/javascript">
44379  */
44380  
44381 /**
44382  * @class Roo.form.Radio
44383  * @extends Roo.form.Checkbox
44384  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44385  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44386  * @constructor
44387  * Creates a new Radio
44388  * @param {Object} config Configuration options
44389  */
44390 Roo.form.Radio = function(){
44391     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44392 };
44393 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44394     inputType: 'radio',
44395
44396     /**
44397      * If this radio is part of a group, it will return the selected value
44398      * @return {String}
44399      */
44400     getGroupValue : function(){
44401         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44402     },
44403     
44404     
44405     onRender : function(ct, position){
44406         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44407         
44408         if(this.inputValue !== undefined){
44409             this.el.dom.value = this.inputValue;
44410         }
44411          
44412         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44413         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44414         //var viewEl = this.wrap.createChild({ 
44415         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44416         //this.viewEl = viewEl;   
44417         //this.wrap.on('click', this.onClick,  this); 
44418         
44419         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44420         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44421         
44422         
44423         
44424         if(this.boxLabel){
44425             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44426         //    viewEl.on('click', this.onClick,  this); 
44427         }
44428          if(this.checked){
44429             this.el.dom.checked =   'checked' ;
44430         }
44431          
44432     } 
44433     
44434     
44435 });Roo.htmleditor = {}; 
44436 /**
44437  * @class Roo.htmleditor.Filter
44438  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
44439  * @cfg {DomElement} node The node to iterate and filter
44440  * @cfg {boolean|String|Array} tag Tags to replace 
44441  * @constructor
44442  * Create a new Filter.
44443  * @param {Object} config Configuration options
44444  */
44445
44446
44447
44448 Roo.htmleditor.Filter = function(cfg) {
44449     Roo.apply(this.cfg);
44450     // this does not actually call walk as it's really just a abstract class
44451 }
44452
44453
44454 Roo.htmleditor.Filter.prototype = {
44455     
44456     node: false,
44457     
44458     tag: false,
44459
44460     // overrride to do replace comments.
44461     replaceComment : false,
44462     
44463     // overrride to do replace or do stuff with tags..
44464     replaceTag : false,
44465     
44466     walk : function(dom)
44467     {
44468         Roo.each( Array.from(dom.childNodes), function( e ) {
44469             switch(true) {
44470                 
44471                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
44472                     this.replaceComment(e);
44473                     return;
44474                 
44475                 case e.nodeType != 1: //not a node.
44476                     return;
44477                 
44478                 case this.tag === true: // everything
44479                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
44480                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
44481                     if (this.replaceTag && false === this.replaceTag(e)) {
44482                         return;
44483                     }
44484                     if (e.hasChildNodes()) {
44485                         this.walk(e);
44486                     }
44487                     return;
44488                 
44489                 default:    // tags .. that do not match.
44490                     if (e.hasChildNodes()) {
44491                         this.walk(e);
44492                     }
44493             }
44494             
44495         }, this);
44496         
44497     }
44498 }; 
44499
44500 /**
44501  * @class Roo.htmleditor.FilterAttributes
44502  * clean attributes and  styles including http:// etc.. in attribute
44503  * @constructor
44504 * Run a new Attribute Filter
44505 * @param {Object} config Configuration options
44506  */
44507 Roo.htmleditor.FilterAttributes = function(cfg)
44508 {
44509     Roo.apply(this, cfg);
44510     this.attrib_black = this.attrib_black || [];
44511     this.attrib_clean = this.attrib_clean || [];
44512     this.style_white = this.style_white || [];
44513     this.style_black = this.style_black || [];
44514     this.walk(cfg.node);
44515 }
44516
44517 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
44518 {
44519     tag: true, // all tags
44520     
44521     attrib_black : false, // array
44522     attrib_clean : false,
44523     style_white : false,
44524     style_black : false,
44525      
44526      
44527     replaceTag : function(node)
44528     {
44529         if (!node.attributes || !node.attributes.length) {
44530             return true;
44531         }
44532         
44533         for (var i = node.attributes.length-1; i > -1 ; i--) {
44534             var a = node.attributes[i];
44535             //console.log(a);
44536             
44537             if (a.name.toLowerCase().substr(0,2)=='on')  {
44538                 node.removeAttribute(a.name);
44539                 continue;
44540             }
44541             
44542             
44543             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
44544                 node.removeAttribute(a.name);
44545                 continue;
44546             }
44547             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
44548                 this.cleanAttr(node,a.name,a.value); // fixme..
44549                 continue;
44550             }
44551             if (a.name == 'style') {
44552                 this.cleanStyle(node,a.name,a.value);
44553                 continue;
44554             }
44555             /// clean up MS crap..
44556             // tecnically this should be a list of valid class'es..
44557             
44558             
44559             if (a.name == 'class') {
44560                 if (a.value.match(/^Mso/)) {
44561                     node.removeAttribute('class');
44562                 }
44563                 
44564                 if (a.value.match(/^body$/)) {
44565                     node.removeAttribute('class');
44566                 }
44567                 continue;
44568             }
44569             
44570             
44571             // style cleanup!?
44572             // class cleanup?
44573             
44574         }
44575         return true; // clean children
44576     },
44577         
44578     cleanAttr: function(node, n,v)
44579     {
44580         
44581         if (v.match(/^\./) || v.match(/^\//)) {
44582             return;
44583         }
44584         if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44585             return;
44586         }
44587         if (v.match(/^#/)) {
44588             return;
44589         }
44590         if (v.match(/^\{/)) { // allow template editing.
44591             return;
44592         }
44593 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44594         node.removeAttribute(n);
44595         
44596     },
44597     cleanStyle : function(node,  n,v)
44598     {
44599         if (v.match(/expression/)) { //XSS?? should we even bother..
44600             node.removeAttribute(n);
44601             return;
44602         }
44603         
44604         var parts = v.split(/;/);
44605         var clean = [];
44606         
44607         Roo.each(parts, function(p) {
44608             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44609             if (!p.length) {
44610                 return true;
44611             }
44612             var l = p.split(':').shift().replace(/\s+/g,'');
44613             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44614             
44615             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
44616                 return true;
44617             }
44618             //Roo.log()
44619             // only allow 'c whitelisted system attributes'
44620             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
44621                 return true;
44622             }
44623             
44624             
44625             clean.push(p);
44626             return true;
44627         },this);
44628         if (clean.length) { 
44629             node.setAttribute(n, clean.join(';'));
44630         } else {
44631             node.removeAttribute(n);
44632         }
44633         
44634     }
44635         
44636         
44637         
44638     
44639 });/**
44640  * @class Roo.htmleditor.FilterBlack
44641  * remove blacklisted elements.
44642  * @constructor
44643  * Run a new Blacklisted Filter
44644  * @param {Object} config Configuration options
44645  */
44646
44647 Roo.htmleditor.FilterBlack = function(cfg)
44648 {
44649     Roo.apply(this, cfg);
44650     this.walk(cfg.node);
44651 }
44652
44653 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
44654 {
44655     tag : true, // all elements.
44656    
44657     replace : function(n)
44658     {
44659         n.parentNode.removeChild(n);
44660     }
44661 });
44662 /**
44663  * @class Roo.htmleditor.FilterComment
44664  * remove comments.
44665  * @constructor
44666 * Run a new Comments Filter
44667 * @param {Object} config Configuration options
44668  */
44669 Roo.htmleditor.FilterComment = function(cfg)
44670 {
44671     this.walk(cfg.node);
44672 }
44673
44674 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
44675 {
44676   
44677     replaceComment : function(n)
44678     {
44679         n.parentNode.removeChild(n);
44680     }
44681 });/**
44682  * @class Roo.htmleditor.FilterKeepChildren
44683  * remove tags but keep children
44684  * @constructor
44685  * Run a new Keep Children Filter
44686  * @param {Object} config Configuration options
44687  */
44688
44689 Roo.htmleditor.FilterKeepChildren = function(cfg)
44690 {
44691     Roo.apply(this, cfg);
44692     this.walk(cfg.node);
44693 }
44694
44695 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
44696 {
44697     
44698   
44699     replaceTag : function(node)
44700     {
44701         // walk children...
44702         var ar = Array.from(node.childNodes);
44703         for (var i = 0; i < ar.length; i++) {
44704             node.removeChild(ar[i]);
44705             // what if we need to walk these???
44706             node.parentNode.insertBefore(ar[i], node);
44707             this.walk(ar[i]);
44708         }
44709         node.parentNode.removeChild(node);
44710         return false; // don't walk children
44711         
44712         
44713     }
44714 });/**
44715  * @class Roo.htmleditor.FilterParagraph
44716  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
44717  * like on 'push' to remove the <p> tags and replace them with line breaks.
44718  * @constructor
44719  * Run a new Paragraph Filter
44720  * @param {Object} config Configuration options
44721  */
44722
44723 Roo.htmleditor.FilterParagraph = function(cfg)
44724 {
44725     // no need to apply config.
44726     this.walk(cfg.node);
44727 }
44728
44729 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
44730 {
44731     
44732      
44733     tag : 'P',
44734     
44735      
44736     replaceTag : function(node)
44737     {
44738         
44739         if (node.childNodes.length == 1 &&
44740             node.childNodes[0].nodeType == 3 &&
44741             node.childNodes[0].textContent.trim().length < 1
44742             ) {
44743             // remove and replace with '<BR>';
44744             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
44745             return false; // no need to walk..
44746         }
44747         var ar = Array.from(node.childNodes);
44748         for (var i = 0; i < ar.length; i++) {
44749             node.removeChild(ar[i]);
44750             // what if we need to walk these???
44751             node.parentNode.insertBefore(ar[i], node);
44752         }
44753         // now what about this?
44754         // <p> &nbsp; </p>
44755         
44756         // double BR.
44757         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
44758         node.parentNode.removeChild(node);
44759         
44760         return false;
44761
44762     }
44763     
44764 });/**
44765  * @class Roo.htmleditor.FilterSpan
44766  * filter span's with no attributes out..
44767  * @constructor
44768  * Run a new Span Filter
44769  * @param {Object} config Configuration options
44770  */
44771
44772 Roo.htmleditor.FilterSpan = function(cfg)
44773 {
44774     // no need to apply config.
44775     this.walk(cfg.node);
44776 }
44777
44778 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.Filter,
44779 {
44780      
44781     tag : 'SPAN',
44782      
44783  
44784     replaceTag : function(node)
44785     {
44786         if (node.attributes && node.attributes.length > 0) {
44787             this.walk(node);
44788             return true;
44789         }
44790         for (var i = 0; i < node.childNodes.length; i++) {
44791             node.removeChild(node.childNodes[i]);
44792             // what if we need to walk these???
44793             node.insertBefore(node.childNodes[i], node);
44794             this.walk(node.childNodes[i]);
44795         }
44796         n.parentNode.removeChild(n);
44797         return false; // don't walk children
44798      
44799     }
44800     
44801 });/**
44802  * @class Roo.htmleditor.FilterTableWidth
44803   try and remove table width data - as that frequently messes up other stuff.
44804  * 
44805  *      was cleanTableWidths.
44806  *
44807  * Quite often pasting from word etc.. results in tables with column and widths.
44808  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44809  *
44810  * @constructor
44811  * Run a new Table Filter
44812  * @param {Object} config Configuration options
44813  */
44814
44815 Roo.htmleditor.FilterTableWidth = function(cfg)
44816 {
44817     // no need to apply config.
44818     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
44819     this.walk(cfg.node);
44820 }
44821
44822 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
44823 {
44824      
44825      
44826     
44827     replaceTag: function(node) {
44828         
44829         
44830       
44831         if (node.hasAttribute('width')) {
44832             node.removeAttribute('width');
44833         }
44834         
44835          
44836         if (node.hasAttribute("style")) {
44837             // pretty basic...
44838             
44839             var styles = node.getAttribute("style").split(";");
44840             var nstyle = [];
44841             Roo.each(styles, function(s) {
44842                 if (!s.match(/:/)) {
44843                     return;
44844                 }
44845                 var kv = s.split(":");
44846                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44847                     return;
44848                 }
44849                 // what ever is left... we allow.
44850                 nstyle.push(s);
44851             });
44852             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44853             if (!nstyle.length) {
44854                 node.removeAttribute('style');
44855             }
44856         }
44857         
44858         return true; // continue doing children..
44859     }
44860 });/**
44861  * @class Roo.htmleditor.FilterWord
44862  * try and clean up all the mess that Word generates.
44863  * 
44864  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
44865  
44866  * @constructor
44867  * Run a new Span Filter
44868  * @param {Object} config Configuration options
44869  */
44870
44871 Roo.htmleditor.FilterWord = function(cfg)
44872 {
44873     // no need to apply config.
44874     this.walk(cfg.node);
44875 }
44876
44877 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
44878 {
44879     tag: true,
44880      
44881     
44882     /**
44883      * Clean up MS wordisms...
44884      */
44885     replaceTag : function(node)
44886     {
44887          
44888         // no idea what this does - span with text, replaceds with just text.
44889         if(
44890                 node.nodeName == 'SPAN' &&
44891                 !node.hasAttributes() &&
44892                 node.childNodes.length == 1 &&
44893                 node.firstChild.nodeName == "#text"  
44894         ) {
44895             var textNode = node.firstChild;
44896             node.removeChild(textNode);
44897             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44898                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44899             }
44900             node.parentNode.insertBefore(textNode, node);
44901             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44902                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44903             }
44904             
44905             node.parentNode.removeChild(node);
44906             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
44907         }
44908         
44909    
44910         
44911         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44912             node.parentNode.removeChild(node);
44913             return false; // dont do chidlren
44914         }
44915         //Roo.log(node.tagName);
44916         // remove - but keep children..
44917         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
44918             //Roo.log('-- removed');
44919             while (node.childNodes.length) {
44920                 var cn = node.childNodes[0];
44921                 node.removeChild(cn);
44922                 node.parentNode.insertBefore(cn, node);
44923                 // move node to parent - and clean it..
44924                 this.replaceTag(cn);
44925             }
44926             node.parentNode.removeChild(node);
44927             /// no need to iterate chidlren = it's got none..
44928             //this.iterateChildren(node, this.cleanWord);
44929             return false; // no need to iterate children.
44930         }
44931         // clean styles
44932         if (node.className.length) {
44933             
44934             var cn = node.className.split(/\W+/);
44935             var cna = [];
44936             Roo.each(cn, function(cls) {
44937                 if (cls.match(/Mso[a-zA-Z]+/)) {
44938                     return;
44939                 }
44940                 cna.push(cls);
44941             });
44942             node.className = cna.length ? cna.join(' ') : '';
44943             if (!cna.length) {
44944                 node.removeAttribute("class");
44945             }
44946         }
44947         
44948         if (node.hasAttribute("lang")) {
44949             node.removeAttribute("lang");
44950         }
44951         
44952         if (node.hasAttribute("style")) {
44953             
44954             var styles = node.getAttribute("style").split(";");
44955             var nstyle = [];
44956             Roo.each(styles, function(s) {
44957                 if (!s.match(/:/)) {
44958                     return;
44959                 }
44960                 var kv = s.split(":");
44961                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
44962                     return;
44963                 }
44964                 // what ever is left... we allow.
44965                 nstyle.push(s);
44966             });
44967             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44968             if (!nstyle.length) {
44969                 node.removeAttribute('style');
44970             }
44971         }
44972         return true; // do children
44973         
44974         
44975         
44976     }
44977 });
44978 /**
44979  * @class Roo.htmleditor.Tidy
44980  * Tidy HTML 
44981  * @cfg {Roo.HtmlEditorCore} core the editor.
44982  * @constructor
44983  * Create a new Filter.
44984  * @param {Object} config Configuration options
44985  */
44986
44987
44988 Roo.htmleditor.Tidy = function(cfg) {
44989     Roo.apply(this, cfg);
44990     
44991     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, -1 , false);
44992      
44993 }
44994 Roo.htmleditor.Tidy.prototype = {
44995     
44996     
44997     wrap : function(s) {
44998         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
44999     },
45000
45001     
45002     /* ?? why ?? */
45003     tidy : function(currentElement, depth, nopadtext) {
45004         
45005         depth = depth || 0;
45006         nopadtext = nopadtext || false;
45007         var pad = nopadtext || depth < 1 ? '' : (new Array( depth   )).join( "  " );
45008      
45009         //Roo.log(currentElement);
45010         var j;
45011         var allText = false;
45012         var nodeName = currentElement.nodeName;
45013         //var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
45014         var tagName = currentElement.tagName; /// why encode tagname?
45015         
45016         if  (nodeName == '#text') {
45017             return nopadtext ? currentElement.nodeValue : this.wrap(currentElement.nodeValue.trim()).split("\n").join("\n" + pad);
45018         }
45019         
45020         
45021         var ret = '';
45022         if (nodeName != 'BODY') {
45023              
45024             var i = 0;
45025             // Prints the node tagName, such as <A>, <IMG>, etc
45026             if (tagName) {
45027                 var attr = [];
45028                 for(i = 0; i < currentElement.attributes.length;i++) {
45029                     
45030                     // skip empty values?
45031                     if (!currentElement.attributes.item(i).value.length) {
45032                         continue;
45033                     }
45034                     attr.push(
45035                         currentElement.attributes.item(i).name + '="' +
45036                             Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"'
45037                     );
45038                 }
45039                 
45040                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') ;
45041             } 
45042             else {
45043                 // dont add any attributes to body...
45044                 // eack
45045             }
45046         } else {
45047             tagName = false;
45048         }
45049         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
45050             return ret + '/>';
45051         }
45052         ret += '>';
45053         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
45054             nopadtext = true;
45055         }
45056         
45057         
45058         // Traverse the tree
45059         i = 0;
45060         var ar = Array.from(currentElement.childNodes);
45061         var allText = true;
45062         var innerHTML  = '';
45063         var lastnode = '';
45064         for (var i = 0 ; i < ar.length ; i++) { 
45065              
45066             // Formatting code (indent the tree so it looks nice on the screen)
45067             var nopad = nopadtext;
45068             if (lastnode == 'SPAN') {
45069                 nopad  = true;
45070             }
45071             // text
45072             if  (ar[i].nodeName == '#text') {
45073                 var toadd = Roo.util.Format.htmlEncode(ar[i].nodeValue);
45074                 innerHTML  += nopadtext ? toadd : this.wrap(ar[i].nodeValue.trim()).split("\n").join("\n" + pad + "  "); 
45075                 lastNode = '';
45076                 continue;
45077             }
45078             allText = false;
45079             
45080             innerHTML  += nopad ? '' : "\n"  + pad + "  ";
45081                 
45082             // Recursively traverse the tree structure of the child node
45083             innerHTML   += this.tidy(ar[i], depth+1, nopadtext);
45084             lastnode = ar[i].nodeName;
45085            
45086          }
45087         
45088         ret += innerHTML;
45089         
45090         if (!allText) {
45091                 // The remaining code is mostly for formatting the tree
45092             ret+= nopadtext ? '' : ("\n" + pad);
45093         }
45094         
45095         
45096         if (tagName) {
45097             ret+= "</"+tagName+">";
45098         }
45099         return ret;
45100         
45101     }
45102 }
45103 /**
45104  * @class Roo.htmleditor.KeyEnter
45105  * Handle Enter press..
45106  * @cfg {Roo.HtmlEditorCore} core the editor.
45107  * @constructor
45108  * Create a new Filter.
45109  * @param {Object} config Configuration options
45110  */
45111
45112
45113
45114 Roo.htmleditor.KeyEnter = function(cfg) {
45115     Roo.apply(this, cfg);
45116     // this does not actually call walk as it's really just a abstract class
45117  
45118     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45119 }
45120
45121
45122 Roo.htmleditor.KeyEnter.prototype = {
45123     
45124     core : false,
45125     
45126     keypress : function(e) {
45127         if (e.charCode != 13) {
45128             return true;
45129         }
45130         e.preventDefault();
45131         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45132         var doc = this.core.doc;
45133         
45134         var docFragment = doc.createDocumentFragment();
45135     
45136         //add a new line
45137         var newEle = doc.createTextNode('\n');
45138         docFragment.appendChild(newEle);
45139     
45140         //add the br, or p, or something else
45141         newEle = doc.createElement('br');
45142         docFragment.appendChild(newEle);
45143     
45144         //make the br replace selection
45145         var range = this.core.win.getSelection().getRangeAt(0);
45146         range.deleteContents();
45147         range.insertNode(docFragment);
45148     
45149         //create a new range
45150         range = doc.createRange();
45151         range.setStartAfter(newEle);
45152         range.collapse(true);
45153     
45154         //make the cursor there
45155         var sel = this.core.win.getSelection();
45156         sel.removeAllRanges();
45157         sel.addRange(range);
45158     
45159         return false;
45160          
45161     }
45162 };
45163     //<script type="text/javascript">
45164
45165 /*
45166  * Based  Ext JS Library 1.1.1
45167  * Copyright(c) 2006-2007, Ext JS, LLC.
45168  * LGPL
45169  *
45170  */
45171  
45172 /**
45173  * @class Roo.HtmlEditorCore
45174  * @extends Roo.Component
45175  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
45176  *
45177  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45178  */
45179
45180 Roo.HtmlEditorCore = function(config){
45181     
45182     
45183     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
45184     
45185     
45186     this.addEvents({
45187         /**
45188          * @event initialize
45189          * Fires when the editor is fully initialized (including the iframe)
45190          * @param {Roo.HtmlEditorCore} this
45191          */
45192         initialize: true,
45193         /**
45194          * @event activate
45195          * Fires when the editor is first receives the focus. Any insertion must wait
45196          * until after this event.
45197          * @param {Roo.HtmlEditorCore} this
45198          */
45199         activate: true,
45200          /**
45201          * @event beforesync
45202          * Fires before the textarea is updated with content from the editor iframe. Return false
45203          * to cancel the sync.
45204          * @param {Roo.HtmlEditorCore} this
45205          * @param {String} html
45206          */
45207         beforesync: true,
45208          /**
45209          * @event beforepush
45210          * Fires before the iframe editor is updated with content from the textarea. Return false
45211          * to cancel the push.
45212          * @param {Roo.HtmlEditorCore} this
45213          * @param {String} html
45214          */
45215         beforepush: true,
45216          /**
45217          * @event sync
45218          * Fires when the textarea is updated with content from the editor iframe.
45219          * @param {Roo.HtmlEditorCore} this
45220          * @param {String} html
45221          */
45222         sync: true,
45223          /**
45224          * @event push
45225          * Fires when the iframe editor is updated with content from the textarea.
45226          * @param {Roo.HtmlEditorCore} this
45227          * @param {String} html
45228          */
45229         push: true,
45230         
45231         /**
45232          * @event editorevent
45233          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45234          * @param {Roo.HtmlEditorCore} this
45235          */
45236         editorevent: true
45237         
45238     });
45239     
45240     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
45241     
45242     // defaults : white / black...
45243     this.applyBlacklists();
45244     
45245     
45246     
45247 };
45248
45249
45250 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
45251
45252
45253      /**
45254      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
45255      */
45256     
45257     owner : false,
45258     
45259      /**
45260      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45261      *                        Roo.resizable.
45262      */
45263     resizable : false,
45264      /**
45265      * @cfg {Number} height (in pixels)
45266      */   
45267     height: 300,
45268    /**
45269      * @cfg {Number} width (in pixels)
45270      */   
45271     width: 500,
45272     
45273     /**
45274      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45275      * 
45276      */
45277     stylesheets: false,
45278     
45279     /**
45280      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45281      */
45282     allowComments: false,
45283     // id of frame..
45284     frameId: false,
45285     
45286     // private properties
45287     validationEvent : false,
45288     deferHeight: true,
45289     initialized : false,
45290     activated : false,
45291     sourceEditMode : false,
45292     onFocus : Roo.emptyFn,
45293     iframePad:3,
45294     hideMode:'offsets',
45295     
45296     clearUp: true,
45297     
45298     // blacklist + whitelisted elements..
45299     black: false,
45300     white: false,
45301      
45302     bodyCls : '',
45303
45304     /**
45305      * Protected method that will not generally be called directly. It
45306      * is called when the editor initializes the iframe with HTML contents. Override this method if you
45307      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
45308      */
45309     getDocMarkup : function(){
45310         // body styles..
45311         var st = '';
45312         
45313         // inherit styels from page...?? 
45314         if (this.stylesheets === false) {
45315             
45316             Roo.get(document.head).select('style').each(function(node) {
45317                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45318             });
45319             
45320             Roo.get(document.head).select('link').each(function(node) { 
45321                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45322             });
45323             
45324         } else if (!this.stylesheets.length) {
45325                 // simple..
45326                 st = '<style type="text/css">' +
45327                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45328                    '</style>';
45329         } else {
45330             for (var i in this.stylesheets) {
45331                 if (typeof(this.stylesheets[i]) != 'string') {
45332                     continue;
45333                 }
45334                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
45335             }
45336             
45337         }
45338         
45339         st +=  '<style type="text/css">' +
45340             'IMG { cursor: pointer } ' +
45341         '</style>';
45342
45343         var cls = 'roo-htmleditor-body';
45344         
45345         if(this.bodyCls.length){
45346             cls += ' ' + this.bodyCls;
45347         }
45348         
45349         return '<html><head>' + st  +
45350             //<style type="text/css">' +
45351             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45352             //'</style>' +
45353             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
45354     },
45355
45356     // private
45357     onRender : function(ct, position)
45358     {
45359         var _t = this;
45360         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
45361         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
45362         
45363         
45364         this.el.dom.style.border = '0 none';
45365         this.el.dom.setAttribute('tabIndex', -1);
45366         this.el.addClass('x-hidden hide');
45367         
45368         
45369         
45370         if(Roo.isIE){ // fix IE 1px bogus margin
45371             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
45372         }
45373        
45374         
45375         this.frameId = Roo.id();
45376         
45377          
45378         
45379         var iframe = this.owner.wrap.createChild({
45380             tag: 'iframe',
45381             cls: 'form-control', // bootstrap..
45382             id: this.frameId,
45383             name: this.frameId,
45384             frameBorder : 'no',
45385             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
45386         }, this.el
45387         );
45388         
45389         
45390         this.iframe = iframe.dom;
45391
45392          this.assignDocWin();
45393         
45394         this.doc.designMode = 'on';
45395        
45396         this.doc.open();
45397         this.doc.write(this.getDocMarkup());
45398         this.doc.close();
45399
45400         
45401         var task = { // must defer to wait for browser to be ready
45402             run : function(){
45403                 //console.log("run task?" + this.doc.readyState);
45404                 this.assignDocWin();
45405                 if(this.doc.body || this.doc.readyState == 'complete'){
45406                     try {
45407                         this.doc.designMode="on";
45408                     } catch (e) {
45409                         return;
45410                     }
45411                     Roo.TaskMgr.stop(task);
45412                     this.initEditor.defer(10, this);
45413                 }
45414             },
45415             interval : 10,
45416             duration: 10000,
45417             scope: this
45418         };
45419         Roo.TaskMgr.start(task);
45420
45421     },
45422
45423     // private
45424     onResize : function(w, h)
45425     {
45426          Roo.log('resize: ' +w + ',' + h );
45427         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
45428         if(!this.iframe){
45429             return;
45430         }
45431         if(typeof w == 'number'){
45432             
45433             this.iframe.style.width = w + 'px';
45434         }
45435         if(typeof h == 'number'){
45436             
45437             this.iframe.style.height = h + 'px';
45438             if(this.doc){
45439                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
45440             }
45441         }
45442         
45443     },
45444
45445     /**
45446      * Toggles the editor between standard and source edit mode.
45447      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45448      */
45449     toggleSourceEdit : function(sourceEditMode){
45450         
45451         this.sourceEditMode = sourceEditMode === true;
45452         
45453         if(this.sourceEditMode){
45454  
45455             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
45456             
45457         }else{
45458             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
45459             //this.iframe.className = '';
45460             this.deferFocus();
45461         }
45462         //this.setSize(this.owner.wrap.getSize());
45463         //this.fireEvent('editmodechange', this, this.sourceEditMode);
45464     },
45465
45466     
45467   
45468
45469     /**
45470      * Protected method that will not generally be called directly. If you need/want
45471      * custom HTML cleanup, this is the method you should override.
45472      * @param {String} html The HTML to be cleaned
45473      * return {String} The cleaned HTML
45474      */
45475     cleanHtml : function(html){
45476         html = String(html);
45477         if(html.length > 5){
45478             if(Roo.isSafari){ // strip safari nonsense
45479                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
45480             }
45481         }
45482         if(html == '&nbsp;'){
45483             html = '';
45484         }
45485         return html;
45486     },
45487
45488     /**
45489      * HTML Editor -> Textarea
45490      * Protected method that will not generally be called directly. Syncs the contents
45491      * of the editor iframe with the textarea.
45492      */
45493     syncValue : function(){
45494         if(this.initialized){
45495             var bd = (this.doc.body || this.doc.documentElement);
45496             //this.cleanUpPaste(); -- this is done else where and causes havoc..
45497             var html = bd.innerHTML;
45498             if(Roo.isSafari){
45499                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
45500                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
45501                 if(m && m[1]){
45502                     html = '<div style="'+m[0]+'">' + html + '</div>';
45503                 }
45504             }
45505             html = this.cleanHtml(html);
45506             // fix up the special chars.. normaly like back quotes in word...
45507             // however we do not want to do this with chinese..
45508             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
45509                 
45510                 var cc = match.charCodeAt();
45511
45512                 // Get the character value, handling surrogate pairs
45513                 if (match.length == 2) {
45514                     // It's a surrogate pair, calculate the Unicode code point
45515                     var high = match.charCodeAt(0) - 0xD800;
45516                     var low  = match.charCodeAt(1) - 0xDC00;
45517                     cc = (high * 0x400) + low + 0x10000;
45518                 }  else if (
45519                     (cc >= 0x4E00 && cc < 0xA000 ) ||
45520                     (cc >= 0x3400 && cc < 0x4E00 ) ||
45521                     (cc >= 0xf900 && cc < 0xfb00 )
45522                 ) {
45523                         return match;
45524                 }  
45525          
45526                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
45527                 return "&#" + cc + ";";
45528                 
45529                 
45530             });
45531             
45532             
45533              
45534             if(this.owner.fireEvent('beforesync', this, html) !== false){
45535                 this.el.dom.value = html;
45536                 this.owner.fireEvent('sync', this, html);
45537             }
45538         }
45539     },
45540
45541     /**
45542      * Protected method that will not generally be called directly. Pushes the value of the textarea
45543      * into the iframe editor.
45544      */
45545     pushValue : function(){
45546         if(this.initialized){
45547             var v = this.el.dom.value.trim();
45548             
45549 //            if(v.length < 1){
45550 //                v = '&#160;';
45551 //            }
45552             
45553             if(this.owner.fireEvent('beforepush', this, v) !== false){
45554                 var d = (this.doc.body || this.doc.documentElement);
45555                 d.innerHTML = v;
45556                 this.cleanUpPaste();
45557                 this.el.dom.value = d.innerHTML;
45558                 this.owner.fireEvent('push', this, v);
45559             }
45560         }
45561     },
45562
45563     // private
45564     deferFocus : function(){
45565         this.focus.defer(10, this);
45566     },
45567
45568     // doc'ed in Field
45569     focus : function(){
45570         if(this.win && !this.sourceEditMode){
45571             this.win.focus();
45572         }else{
45573             this.el.focus();
45574         }
45575     },
45576     
45577     assignDocWin: function()
45578     {
45579         var iframe = this.iframe;
45580         
45581          if(Roo.isIE){
45582             this.doc = iframe.contentWindow.document;
45583             this.win = iframe.contentWindow;
45584         } else {
45585 //            if (!Roo.get(this.frameId)) {
45586 //                return;
45587 //            }
45588 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45589 //            this.win = Roo.get(this.frameId).dom.contentWindow;
45590             
45591             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
45592                 return;
45593             }
45594             
45595             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
45596             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
45597         }
45598     },
45599     
45600     // private
45601     initEditor : function(){
45602         //console.log("INIT EDITOR");
45603         this.assignDocWin();
45604         
45605         
45606         
45607         this.doc.designMode="on";
45608         this.doc.open();
45609         this.doc.write(this.getDocMarkup());
45610         this.doc.close();
45611         
45612         var dbody = (this.doc.body || this.doc.documentElement);
45613         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
45614         // this copies styles from the containing element into thsi one..
45615         // not sure why we need all of this..
45616         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
45617         
45618         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
45619         //ss['background-attachment'] = 'fixed'; // w3c
45620         dbody.bgProperties = 'fixed'; // ie
45621         //Roo.DomHelper.applyStyles(dbody, ss);
45622         Roo.EventManager.on(this.doc, {
45623             //'mousedown': this.onEditorEvent,
45624             'mouseup': this.onEditorEvent,
45625             'dblclick': this.onEditorEvent,
45626             'click': this.onEditorEvent,
45627             'keyup': this.onEditorEvent,
45628             buffer:100,
45629             scope: this
45630         });
45631         if(Roo.isGecko){
45632             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
45633         }
45634         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
45635             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
45636         }
45637         this.initialized = true;
45638
45639         
45640         // initialize special key events - enter
45641         new Roo.htmleditor.KeyEnter({core : this});
45642         
45643         
45644         
45645         
45646         this.owner.fireEvent('initialize', this);
45647         this.pushValue();
45648     },
45649
45650     // private
45651     onDestroy : function(){
45652         
45653         
45654         
45655         if(this.rendered){
45656             
45657             //for (var i =0; i < this.toolbars.length;i++) {
45658             //    // fixme - ask toolbars for heights?
45659             //    this.toolbars[i].onDestroy();
45660            // }
45661             
45662             //this.wrap.dom.innerHTML = '';
45663             //this.wrap.remove();
45664         }
45665     },
45666
45667     // private
45668     onFirstFocus : function(){
45669         
45670         this.assignDocWin();
45671         
45672         
45673         this.activated = true;
45674          
45675     
45676         if(Roo.isGecko){ // prevent silly gecko errors
45677             this.win.focus();
45678             var s = this.win.getSelection();
45679             if(!s.focusNode || s.focusNode.nodeType != 3){
45680                 var r = s.getRangeAt(0);
45681                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
45682                 r.collapse(true);
45683                 this.deferFocus();
45684             }
45685             try{
45686                 this.execCmd('useCSS', true);
45687                 this.execCmd('styleWithCSS', false);
45688             }catch(e){}
45689         }
45690         this.owner.fireEvent('activate', this);
45691     },
45692
45693     // private
45694     adjustFont: function(btn){
45695         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
45696         //if(Roo.isSafari){ // safari
45697         //    adjust *= 2;
45698        // }
45699         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
45700         if(Roo.isSafari){ // safari
45701             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
45702             v =  (v < 10) ? 10 : v;
45703             v =  (v > 48) ? 48 : v;
45704             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
45705             
45706         }
45707         
45708         
45709         v = Math.max(1, v+adjust);
45710         
45711         this.execCmd('FontSize', v  );
45712     },
45713
45714     onEditorEvent : function(e)
45715     {
45716         this.owner.fireEvent('editorevent', this, e);
45717       //  this.updateToolbar();
45718         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
45719     },
45720
45721     insertTag : function(tg)
45722     {
45723         // could be a bit smarter... -> wrap the current selected tRoo..
45724         if (tg.toLowerCase() == 'span' ||
45725             tg.toLowerCase() == 'code' ||
45726             tg.toLowerCase() == 'sup' ||
45727             tg.toLowerCase() == 'sub' 
45728             ) {
45729             
45730             range = this.createRange(this.getSelection());
45731             var wrappingNode = this.doc.createElement(tg.toLowerCase());
45732             wrappingNode.appendChild(range.extractContents());
45733             range.insertNode(wrappingNode);
45734
45735             return;
45736             
45737             
45738             
45739         }
45740         this.execCmd("formatblock",   tg);
45741         
45742     },
45743     
45744     insertText : function(txt)
45745     {
45746         
45747         
45748         var range = this.createRange();
45749         range.deleteContents();
45750                //alert(Sender.getAttribute('label'));
45751                
45752         range.insertNode(this.doc.createTextNode(txt));
45753     } ,
45754     
45755      
45756
45757     /**
45758      * Executes a Midas editor command on the editor document and performs necessary focus and
45759      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
45760      * @param {String} cmd The Midas command
45761      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45762      */
45763     relayCmd : function(cmd, value){
45764         this.win.focus();
45765         this.execCmd(cmd, value);
45766         this.owner.fireEvent('editorevent', this);
45767         //this.updateToolbar();
45768         this.owner.deferFocus();
45769     },
45770
45771     /**
45772      * Executes a Midas editor command directly on the editor document.
45773      * For visual commands, you should use {@link #relayCmd} instead.
45774      * <b>This should only be called after the editor is initialized.</b>
45775      * @param {String} cmd The Midas command
45776      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
45777      */
45778     execCmd : function(cmd, value){
45779         this.doc.execCommand(cmd, false, value === undefined ? null : value);
45780         this.syncValue();
45781     },
45782  
45783  
45784    
45785     /**
45786      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
45787      * to insert tRoo.
45788      * @param {String} text | dom node.. 
45789      */
45790     insertAtCursor : function(text)
45791     {
45792         
45793         if(!this.activated){
45794             return;
45795         }
45796         /*
45797         if(Roo.isIE){
45798             this.win.focus();
45799             var r = this.doc.selection.createRange();
45800             if(r){
45801                 r.collapse(true);
45802                 r.pasteHTML(text);
45803                 this.syncValue();
45804                 this.deferFocus();
45805             
45806             }
45807             return;
45808         }
45809         */
45810         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
45811             this.win.focus();
45812             
45813             
45814             // from jquery ui (MIT licenced)
45815             var range, node;
45816             var win = this.win;
45817             
45818             if (win.getSelection && win.getSelection().getRangeAt) {
45819                 range = win.getSelection().getRangeAt(0);
45820                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
45821                 range.insertNode(node);
45822             } else if (win.document.selection && win.document.selection.createRange) {
45823                 // no firefox support
45824                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45825                 win.document.selection.createRange().pasteHTML(txt);
45826             } else {
45827                 // no firefox support
45828                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
45829                 this.execCmd('InsertHTML', txt);
45830             } 
45831             
45832             this.syncValue();
45833             
45834             this.deferFocus();
45835         }
45836     },
45837  // private
45838     mozKeyPress : function(e){
45839         if(e.ctrlKey){
45840             var c = e.getCharCode(), cmd;
45841           
45842             if(c > 0){
45843                 c = String.fromCharCode(c).toLowerCase();
45844                 switch(c){
45845                     case 'b':
45846                         cmd = 'bold';
45847                         break;
45848                     case 'i':
45849                         cmd = 'italic';
45850                         break;
45851                     
45852                     case 'u':
45853                         cmd = 'underline';
45854                         break;
45855                     
45856                     case 'v':
45857                         this.cleanUpPaste.defer(100, this);
45858                         return;
45859                         
45860                 }
45861                 if(cmd){
45862                     this.win.focus();
45863                     this.execCmd(cmd);
45864                     this.deferFocus();
45865                     e.preventDefault();
45866                 }
45867                 
45868             }
45869         }
45870     },
45871
45872     // private
45873     fixKeys : function(){ // load time branching for fastest keydown performance
45874         if(Roo.isIE){
45875             return function(e){
45876                 var k = e.getKey(), r;
45877                 if(k == e.TAB){
45878                     e.stopEvent();
45879                     r = this.doc.selection.createRange();
45880                     if(r){
45881                         r.collapse(true);
45882                         r.pasteHTML('&#160;&#160;&#160;&#160;');
45883                         this.deferFocus();
45884                     }
45885                     return;
45886                 }
45887                 
45888                 if(k == e.ENTER){
45889                     r = this.doc.selection.createRange();
45890                     if(r){
45891                         var target = r.parentElement();
45892                         if(!target || target.tagName.toLowerCase() != 'li'){
45893                             e.stopEvent();
45894                             r.pasteHTML('<br />');
45895                             r.collapse(false);
45896                             r.select();
45897                         }
45898                     }
45899                 }
45900                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45901                     this.cleanUpPaste.defer(100, this);
45902                     return;
45903                 }
45904                 
45905                 
45906             };
45907         }else if(Roo.isOpera){
45908             return function(e){
45909                 var k = e.getKey();
45910                 if(k == e.TAB){
45911                     e.stopEvent();
45912                     this.win.focus();
45913                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
45914                     this.deferFocus();
45915                 }
45916                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45917                     this.cleanUpPaste.defer(100, this);
45918                     return;
45919                 }
45920                 
45921             };
45922         }else if(Roo.isSafari){
45923             return function(e){
45924                 var k = e.getKey();
45925                 
45926                 if(k == e.TAB){
45927                     e.stopEvent();
45928                     this.execCmd('InsertText','\t');
45929                     this.deferFocus();
45930                     return;
45931                 }
45932                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
45933                     this.cleanUpPaste.defer(100, this);
45934                     return;
45935                 }
45936                 
45937              };
45938         }
45939     }(),
45940     
45941     getAllAncestors: function()
45942     {
45943         var p = this.getSelectedNode();
45944         var a = [];
45945         if (!p) {
45946             a.push(p); // push blank onto stack..
45947             p = this.getParentElement();
45948         }
45949         
45950         
45951         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
45952             a.push(p);
45953             p = p.parentNode;
45954         }
45955         a.push(this.doc.body);
45956         return a;
45957     },
45958     lastSel : false,
45959     lastSelNode : false,
45960     
45961     
45962     getSelection : function() 
45963     {
45964         this.assignDocWin();
45965         return Roo.isIE ? this.doc.selection : this.win.getSelection();
45966     },
45967     
45968     getSelectedNode: function() 
45969     {
45970         // this may only work on Gecko!!!
45971         
45972         // should we cache this!!!!
45973         
45974         
45975         
45976          
45977         var range = this.createRange(this.getSelection()).cloneRange();
45978         
45979         if (Roo.isIE) {
45980             var parent = range.parentElement();
45981             while (true) {
45982                 var testRange = range.duplicate();
45983                 testRange.moveToElementText(parent);
45984                 if (testRange.inRange(range)) {
45985                     break;
45986                 }
45987                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
45988                     break;
45989                 }
45990                 parent = parent.parentElement;
45991             }
45992             return parent;
45993         }
45994         
45995         // is ancestor a text element.
45996         var ac =  range.commonAncestorContainer;
45997         if (ac.nodeType == 3) {
45998             ac = ac.parentNode;
45999         }
46000         
46001         var ar = ac.childNodes;
46002          
46003         var nodes = [];
46004         var other_nodes = [];
46005         var has_other_nodes = false;
46006         for (var i=0;i<ar.length;i++) {
46007             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
46008                 continue;
46009             }
46010             // fullly contained node.
46011             
46012             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
46013                 nodes.push(ar[i]);
46014                 continue;
46015             }
46016             
46017             // probably selected..
46018             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
46019                 other_nodes.push(ar[i]);
46020                 continue;
46021             }
46022             // outer..
46023             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
46024                 continue;
46025             }
46026             
46027             
46028             has_other_nodes = true;
46029         }
46030         if (!nodes.length && other_nodes.length) {
46031             nodes= other_nodes;
46032         }
46033         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
46034             return false;
46035         }
46036         
46037         return nodes[0];
46038     },
46039     createRange: function(sel)
46040     {
46041         // this has strange effects when using with 
46042         // top toolbar - not sure if it's a great idea.
46043         //this.editor.contentWindow.focus();
46044         if (typeof sel != "undefined") {
46045             try {
46046                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
46047             } catch(e) {
46048                 return this.doc.createRange();
46049             }
46050         } else {
46051             return this.doc.createRange();
46052         }
46053     },
46054     getParentElement: function()
46055     {
46056         
46057         this.assignDocWin();
46058         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
46059         
46060         var range = this.createRange(sel);
46061          
46062         try {
46063             var p = range.commonAncestorContainer;
46064             while (p.nodeType == 3) { // text node
46065                 p = p.parentNode;
46066             }
46067             return p;
46068         } catch (e) {
46069             return null;
46070         }
46071     
46072     },
46073     /***
46074      *
46075      * Range intersection.. the hard stuff...
46076      *  '-1' = before
46077      *  '0' = hits..
46078      *  '1' = after.
46079      *         [ -- selected range --- ]
46080      *   [fail]                        [fail]
46081      *
46082      *    basically..
46083      *      if end is before start or  hits it. fail.
46084      *      if start is after end or hits it fail.
46085      *
46086      *   if either hits (but other is outside. - then it's not 
46087      *   
46088      *    
46089      **/
46090     
46091     
46092     // @see http://www.thismuchiknow.co.uk/?p=64.
46093     rangeIntersectsNode : function(range, node)
46094     {
46095         var nodeRange = node.ownerDocument.createRange();
46096         try {
46097             nodeRange.selectNode(node);
46098         } catch (e) {
46099             nodeRange.selectNodeContents(node);
46100         }
46101     
46102         var rangeStartRange = range.cloneRange();
46103         rangeStartRange.collapse(true);
46104     
46105         var rangeEndRange = range.cloneRange();
46106         rangeEndRange.collapse(false);
46107     
46108         var nodeStartRange = nodeRange.cloneRange();
46109         nodeStartRange.collapse(true);
46110     
46111         var nodeEndRange = nodeRange.cloneRange();
46112         nodeEndRange.collapse(false);
46113     
46114         return rangeStartRange.compareBoundaryPoints(
46115                  Range.START_TO_START, nodeEndRange) == -1 &&
46116                rangeEndRange.compareBoundaryPoints(
46117                  Range.START_TO_START, nodeStartRange) == 1;
46118         
46119          
46120     },
46121     rangeCompareNode : function(range, node)
46122     {
46123         var nodeRange = node.ownerDocument.createRange();
46124         try {
46125             nodeRange.selectNode(node);
46126         } catch (e) {
46127             nodeRange.selectNodeContents(node);
46128         }
46129         
46130         
46131         range.collapse(true);
46132     
46133         nodeRange.collapse(true);
46134      
46135         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
46136         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
46137          
46138         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
46139         
46140         var nodeIsBefore   =  ss == 1;
46141         var nodeIsAfter    = ee == -1;
46142         
46143         if (nodeIsBefore && nodeIsAfter) {
46144             return 0; // outer
46145         }
46146         if (!nodeIsBefore && nodeIsAfter) {
46147             return 1; //right trailed.
46148         }
46149         
46150         if (nodeIsBefore && !nodeIsAfter) {
46151             return 2;  // left trailed.
46152         }
46153         // fully contined.
46154         return 3;
46155     },
46156
46157     // private? - in a new class?
46158     cleanUpPaste :  function()
46159     {
46160         // cleans up the whole document..
46161         Roo.log('cleanuppaste');
46162         
46163         this.cleanUpChild(this.doc.body);
46164         var clean = this.cleanWordChars(this.doc.body.innerHTML);
46165         if (clean != this.doc.body.innerHTML) {
46166             this.doc.body.innerHTML = clean;
46167         }
46168         
46169     },
46170     
46171     cleanWordChars : function(input) {// change the chars to hex code
46172         var he = Roo.HtmlEditorCore;
46173         
46174         var output = input;
46175         Roo.each(he.swapCodes, function(sw) { 
46176             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
46177             
46178             output = output.replace(swapper, sw[1]);
46179         });
46180         
46181         return output;
46182     },
46183     
46184      
46185     
46186         
46187     
46188     cleanUpChild : function (node)
46189     {
46190         
46191         new Roo.htmleditor.FilterComment({node : node});
46192         new Roo.htmleditor.FilterAttributes({
46193                 node : node,
46194                 attrib_black : this.ablack,
46195                 attrib_clean : this.aclean,
46196                 style_white : this.cwhite,
46197                 style_black : this.cblack
46198         });
46199         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
46200         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
46201          
46202         
46203     },
46204     
46205     /**
46206      * Clean up MS wordisms...
46207      * @deprecated - use filter directly
46208      */
46209     cleanWord : function(node)
46210     {
46211         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
46212         
46213     },
46214    
46215     
46216     /**
46217
46218      * @deprecated - use filters
46219      */
46220     cleanTableWidths : function(node)
46221     {
46222         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
46223         
46224  
46225     },
46226     
46227     
46228     
46229     /* ?? why ?? */
46230     domToHTML : function(currentElement, depth, nopadtext) {
46231         
46232         depth = depth || 0;
46233         nopadtext = nopadtext || false;
46234     
46235         if (!currentElement) {
46236             return this.domToHTML(this.doc.body);
46237         }
46238         
46239         //Roo.log(currentElement);
46240         var j;
46241         var allText = false;
46242         var nodeName = currentElement.nodeName;
46243         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
46244         
46245         if  (nodeName == '#text') {
46246             
46247             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
46248         }
46249         
46250         
46251         var ret = '';
46252         if (nodeName != 'BODY') {
46253              
46254             var i = 0;
46255             // Prints the node tagName, such as <A>, <IMG>, etc
46256             if (tagName) {
46257                 var attr = [];
46258                 for(i = 0; i < currentElement.attributes.length;i++) {
46259                     // quoting?
46260                     var aname = currentElement.attributes.item(i).name;
46261                     if (!currentElement.attributes.item(i).value.length) {
46262                         continue;
46263                     }
46264                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
46265                 }
46266                 
46267                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
46268             } 
46269             else {
46270                 
46271                 // eack
46272             }
46273         } else {
46274             tagName = false;
46275         }
46276         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
46277             return ret;
46278         }
46279         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
46280             nopadtext = true;
46281         }
46282         
46283         
46284         // Traverse the tree
46285         i = 0;
46286         var currentElementChild = currentElement.childNodes.item(i);
46287         var allText = true;
46288         var innerHTML  = '';
46289         lastnode = '';
46290         while (currentElementChild) {
46291             // Formatting code (indent the tree so it looks nice on the screen)
46292             var nopad = nopadtext;
46293             if (lastnode == 'SPAN') {
46294                 nopad  = true;
46295             }
46296             // text
46297             if  (currentElementChild.nodeName == '#text') {
46298                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
46299                 toadd = nopadtext ? toadd : toadd.trim();
46300                 if (!nopad && toadd.length > 80) {
46301                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
46302                 }
46303                 innerHTML  += toadd;
46304                 
46305                 i++;
46306                 currentElementChild = currentElement.childNodes.item(i);
46307                 lastNode = '';
46308                 continue;
46309             }
46310             allText = false;
46311             
46312             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
46313                 
46314             // Recursively traverse the tree structure of the child node
46315             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
46316             lastnode = currentElementChild.nodeName;
46317             i++;
46318             currentElementChild=currentElement.childNodes.item(i);
46319         }
46320         
46321         ret += innerHTML;
46322         
46323         if (!allText) {
46324                 // The remaining code is mostly for formatting the tree
46325             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
46326         }
46327         
46328         
46329         if (tagName) {
46330             ret+= "</"+tagName+">";
46331         }
46332         return ret;
46333         
46334     },
46335         
46336     applyBlacklists : function()
46337     {
46338         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
46339         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
46340         
46341         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
46342         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
46343         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
46344         
46345         this.white = [];
46346         this.black = [];
46347         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
46348             if (b.indexOf(tag) > -1) {
46349                 return;
46350             }
46351             this.white.push(tag);
46352             
46353         }, this);
46354         
46355         Roo.each(w, function(tag) {
46356             if (b.indexOf(tag) > -1) {
46357                 return;
46358             }
46359             if (this.white.indexOf(tag) > -1) {
46360                 return;
46361             }
46362             this.white.push(tag);
46363             
46364         }, this);
46365         
46366         
46367         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
46368             if (w.indexOf(tag) > -1) {
46369                 return;
46370             }
46371             this.black.push(tag);
46372             
46373         }, this);
46374         
46375         Roo.each(b, function(tag) {
46376             if (w.indexOf(tag) > -1) {
46377                 return;
46378             }
46379             if (this.black.indexOf(tag) > -1) {
46380                 return;
46381             }
46382             this.black.push(tag);
46383             
46384         }, this);
46385         
46386         
46387         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
46388         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
46389         
46390         this.cwhite = [];
46391         this.cblack = [];
46392         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
46393             if (b.indexOf(tag) > -1) {
46394                 return;
46395             }
46396             this.cwhite.push(tag);
46397             
46398         }, this);
46399         
46400         Roo.each(w, function(tag) {
46401             if (b.indexOf(tag) > -1) {
46402                 return;
46403             }
46404             if (this.cwhite.indexOf(tag) > -1) {
46405                 return;
46406             }
46407             this.cwhite.push(tag);
46408             
46409         }, this);
46410         
46411         
46412         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
46413             if (w.indexOf(tag) > -1) {
46414                 return;
46415             }
46416             this.cblack.push(tag);
46417             
46418         }, this);
46419         
46420         Roo.each(b, function(tag) {
46421             if (w.indexOf(tag) > -1) {
46422                 return;
46423             }
46424             if (this.cblack.indexOf(tag) > -1) {
46425                 return;
46426             }
46427             this.cblack.push(tag);
46428             
46429         }, this);
46430     },
46431     
46432     setStylesheets : function(stylesheets)
46433     {
46434         if(typeof(stylesheets) == 'string'){
46435             Roo.get(this.iframe.contentDocument.head).createChild({
46436                 tag : 'link',
46437                 rel : 'stylesheet',
46438                 type : 'text/css',
46439                 href : stylesheets
46440             });
46441             
46442             return;
46443         }
46444         var _this = this;
46445      
46446         Roo.each(stylesheets, function(s) {
46447             if(!s.length){
46448                 return;
46449             }
46450             
46451             Roo.get(_this.iframe.contentDocument.head).createChild({
46452                 tag : 'link',
46453                 rel : 'stylesheet',
46454                 type : 'text/css',
46455                 href : s
46456             });
46457         });
46458
46459         
46460     },
46461     
46462     removeStylesheets : function()
46463     {
46464         var _this = this;
46465         
46466         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46467             s.remove();
46468         });
46469     },
46470     
46471     setStyle : function(style)
46472     {
46473         Roo.get(this.iframe.contentDocument.head).createChild({
46474             tag : 'style',
46475             type : 'text/css',
46476             html : style
46477         });
46478
46479         return;
46480     }
46481     
46482     // hide stuff that is not compatible
46483     /**
46484      * @event blur
46485      * @hide
46486      */
46487     /**
46488      * @event change
46489      * @hide
46490      */
46491     /**
46492      * @event focus
46493      * @hide
46494      */
46495     /**
46496      * @event specialkey
46497      * @hide
46498      */
46499     /**
46500      * @cfg {String} fieldClass @hide
46501      */
46502     /**
46503      * @cfg {String} focusClass @hide
46504      */
46505     /**
46506      * @cfg {String} autoCreate @hide
46507      */
46508     /**
46509      * @cfg {String} inputType @hide
46510      */
46511     /**
46512      * @cfg {String} invalidClass @hide
46513      */
46514     /**
46515      * @cfg {String} invalidText @hide
46516      */
46517     /**
46518      * @cfg {String} msgFx @hide
46519      */
46520     /**
46521      * @cfg {String} validateOnBlur @hide
46522      */
46523 });
46524
46525 Roo.HtmlEditorCore.white = [
46526         'area', 'br', 'img', 'input', 'hr', 'wbr',
46527         
46528        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
46529        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
46530        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
46531        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
46532        'table',   'ul',         'xmp', 
46533        
46534        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
46535       'thead',   'tr', 
46536      
46537       'dir', 'menu', 'ol', 'ul', 'dl',
46538        
46539       'embed',  'object'
46540 ];
46541
46542
46543 Roo.HtmlEditorCore.black = [
46544     //    'embed',  'object', // enable - backend responsiblity to clean thiese
46545         'applet', // 
46546         'base',   'basefont', 'bgsound', 'blink',  'body', 
46547         'frame',  'frameset', 'head',    'html',   'ilayer', 
46548         'iframe', 'layer',  'link',     'meta',    'object',   
46549         'script', 'style' ,'title',  'xml' // clean later..
46550 ];
46551 Roo.HtmlEditorCore.clean = [
46552     'script', 'style', 'title', 'xml'
46553 ];
46554 Roo.HtmlEditorCore.tag_remove = [
46555     'font'
46556 ];
46557 // attributes..
46558
46559 Roo.HtmlEditorCore.ablack = [
46560     'on'
46561 ];
46562     
46563 Roo.HtmlEditorCore.aclean = [ 
46564     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
46565 ];
46566
46567 // protocols..
46568 Roo.HtmlEditorCore.pwhite= [
46569         'http',  'https',  'mailto'
46570 ];
46571
46572 // white listed style attributes.
46573 Roo.HtmlEditorCore.cwhite= [
46574       //  'text-align', /// default is to allow most things..
46575       
46576          
46577 //        'font-size'//??
46578 ];
46579
46580 // black listed style attributes.
46581 Roo.HtmlEditorCore.cblack= [
46582       //  'font-size' -- this can be set by the project 
46583 ];
46584
46585
46586 Roo.HtmlEditorCore.swapCodes   =[ 
46587     [    8211, "&#8211;" ], 
46588     [    8212, "&#8212;" ], 
46589     [    8216,  "'" ],  
46590     [    8217, "'" ],  
46591     [    8220, '"' ],  
46592     [    8221, '"' ],  
46593     [    8226, "*" ],  
46594     [    8230, "..." ]
46595 ]; 
46596
46597     //<script type="text/javascript">
46598
46599 /*
46600  * Ext JS Library 1.1.1
46601  * Copyright(c) 2006-2007, Ext JS, LLC.
46602  * Licence LGPL
46603  * 
46604  */
46605  
46606  
46607 Roo.form.HtmlEditor = function(config){
46608     
46609     
46610     
46611     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
46612     
46613     if (!this.toolbars) {
46614         this.toolbars = [];
46615     }
46616     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
46617     
46618     
46619 };
46620
46621 /**
46622  * @class Roo.form.HtmlEditor
46623  * @extends Roo.form.Field
46624  * Provides a lightweight HTML Editor component.
46625  *
46626  * This has been tested on Fireforx / Chrome.. IE may not be so great..
46627  * 
46628  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
46629  * supported by this editor.</b><br/><br/>
46630  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
46631  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46632  */
46633 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
46634     /**
46635      * @cfg {Boolean} clearUp
46636      */
46637     clearUp : true,
46638       /**
46639      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
46640      */
46641     toolbars : false,
46642    
46643      /**
46644      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46645      *                        Roo.resizable.
46646      */
46647     resizable : false,
46648      /**
46649      * @cfg {Number} height (in pixels)
46650      */   
46651     height: 300,
46652    /**
46653      * @cfg {Number} width (in pixels)
46654      */   
46655     width: 500,
46656     
46657     /**
46658      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
46659      * 
46660      */
46661     stylesheets: false,
46662     
46663     
46664      /**
46665      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
46666      * 
46667      */
46668     cblack: false,
46669     /**
46670      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
46671      * 
46672      */
46673     cwhite: false,
46674     
46675      /**
46676      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
46677      * 
46678      */
46679     black: false,
46680     /**
46681      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
46682      * 
46683      */
46684     white: false,
46685     /**
46686      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46687      */
46688     allowComments: false,
46689     /**
46690      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
46691      */
46692     
46693     
46694      bodyCls : '',
46695     
46696     // id of frame..
46697     frameId: false,
46698     
46699     // private properties
46700     validationEvent : false,
46701     deferHeight: true,
46702     initialized : false,
46703     activated : false,
46704     
46705     onFocus : Roo.emptyFn,
46706     iframePad:3,
46707     hideMode:'offsets',
46708     
46709     actionMode : 'container', // defaults to hiding it...
46710     
46711     defaultAutoCreate : { // modified by initCompnoent..
46712         tag: "textarea",
46713         style:"width:500px;height:300px;",
46714         autocomplete: "new-password"
46715     },
46716
46717     // private
46718     initComponent : function(){
46719         this.addEvents({
46720             /**
46721              * @event initialize
46722              * Fires when the editor is fully initialized (including the iframe)
46723              * @param {HtmlEditor} this
46724              */
46725             initialize: true,
46726             /**
46727              * @event activate
46728              * Fires when the editor is first receives the focus. Any insertion must wait
46729              * until after this event.
46730              * @param {HtmlEditor} this
46731              */
46732             activate: true,
46733              /**
46734              * @event beforesync
46735              * Fires before the textarea is updated with content from the editor iframe. Return false
46736              * to cancel the sync.
46737              * @param {HtmlEditor} this
46738              * @param {String} html
46739              */
46740             beforesync: true,
46741              /**
46742              * @event beforepush
46743              * Fires before the iframe editor is updated with content from the textarea. Return false
46744              * to cancel the push.
46745              * @param {HtmlEditor} this
46746              * @param {String} html
46747              */
46748             beforepush: true,
46749              /**
46750              * @event sync
46751              * Fires when the textarea is updated with content from the editor iframe.
46752              * @param {HtmlEditor} this
46753              * @param {String} html
46754              */
46755             sync: true,
46756              /**
46757              * @event push
46758              * Fires when the iframe editor is updated with content from the textarea.
46759              * @param {HtmlEditor} this
46760              * @param {String} html
46761              */
46762             push: true,
46763              /**
46764              * @event editmodechange
46765              * Fires when the editor switches edit modes
46766              * @param {HtmlEditor} this
46767              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
46768              */
46769             editmodechange: true,
46770             /**
46771              * @event editorevent
46772              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46773              * @param {HtmlEditor} this
46774              */
46775             editorevent: true,
46776             /**
46777              * @event firstfocus
46778              * Fires when on first focus - needed by toolbars..
46779              * @param {HtmlEditor} this
46780              */
46781             firstfocus: true,
46782             /**
46783              * @event autosave
46784              * Auto save the htmlEditor value as a file into Events
46785              * @param {HtmlEditor} this
46786              */
46787             autosave: true,
46788             /**
46789              * @event savedpreview
46790              * preview the saved version of htmlEditor
46791              * @param {HtmlEditor} this
46792              */
46793             savedpreview: true,
46794             
46795             /**
46796             * @event stylesheetsclick
46797             * Fires when press the Sytlesheets button
46798             * @param {Roo.HtmlEditorCore} this
46799             */
46800             stylesheetsclick: true
46801         });
46802         this.defaultAutoCreate =  {
46803             tag: "textarea",
46804             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
46805             autocomplete: "new-password"
46806         };
46807     },
46808
46809     /**
46810      * Protected method that will not generally be called directly. It
46811      * is called when the editor creates its toolbar. Override this method if you need to
46812      * add custom toolbar buttons.
46813      * @param {HtmlEditor} editor
46814      */
46815     createToolbar : function(editor){
46816         Roo.log("create toolbars");
46817         if (!editor.toolbars || !editor.toolbars.length) {
46818             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
46819         }
46820         
46821         for (var i =0 ; i < editor.toolbars.length;i++) {
46822             editor.toolbars[i] = Roo.factory(
46823                     typeof(editor.toolbars[i]) == 'string' ?
46824                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
46825                 Roo.form.HtmlEditor);
46826             editor.toolbars[i].init(editor);
46827         }
46828          
46829         
46830     },
46831
46832      
46833     // private
46834     onRender : function(ct, position)
46835     {
46836         var _t = this;
46837         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
46838         
46839         this.wrap = this.el.wrap({
46840             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
46841         });
46842         
46843         this.editorcore.onRender(ct, position);
46844          
46845         if (this.resizable) {
46846             this.resizeEl = new Roo.Resizable(this.wrap, {
46847                 pinned : true,
46848                 wrap: true,
46849                 dynamic : true,
46850                 minHeight : this.height,
46851                 height: this.height,
46852                 handles : this.resizable,
46853                 width: this.width,
46854                 listeners : {
46855                     resize : function(r, w, h) {
46856                         _t.onResize(w,h); // -something
46857                     }
46858                 }
46859             });
46860             
46861         }
46862         this.createToolbar(this);
46863        
46864         
46865         if(!this.width){
46866             this.setSize(this.wrap.getSize());
46867         }
46868         if (this.resizeEl) {
46869             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
46870             // should trigger onReize..
46871         }
46872         
46873         this.keyNav = new Roo.KeyNav(this.el, {
46874             
46875             "tab" : function(e){
46876                 e.preventDefault();
46877                 
46878                 var value = this.getValue();
46879                 
46880                 var start = this.el.dom.selectionStart;
46881                 var end = this.el.dom.selectionEnd;
46882                 
46883                 if(!e.shiftKey){
46884                     
46885                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
46886                     this.el.dom.setSelectionRange(end + 1, end + 1);
46887                     return;
46888                 }
46889                 
46890                 var f = value.substring(0, start).split("\t");
46891                 
46892                 if(f.pop().length != 0){
46893                     return;
46894                 }
46895                 
46896                 this.setValue(f.join("\t") + value.substring(end));
46897                 this.el.dom.setSelectionRange(start - 1, start - 1);
46898                 
46899             },
46900             
46901             "home" : function(e){
46902                 e.preventDefault();
46903                 
46904                 var curr = this.el.dom.selectionStart;
46905                 var lines = this.getValue().split("\n");
46906                 
46907                 if(!lines.length){
46908                     return;
46909                 }
46910                 
46911                 if(e.ctrlKey){
46912                     this.el.dom.setSelectionRange(0, 0);
46913                     return;
46914                 }
46915                 
46916                 var pos = 0;
46917                 
46918                 for (var i = 0; i < lines.length;i++) {
46919                     pos += lines[i].length;
46920                     
46921                     if(i != 0){
46922                         pos += 1;
46923                     }
46924                     
46925                     if(pos < curr){
46926                         continue;
46927                     }
46928                     
46929                     pos -= lines[i].length;
46930                     
46931                     break;
46932                 }
46933                 
46934                 if(!e.shiftKey){
46935                     this.el.dom.setSelectionRange(pos, pos);
46936                     return;
46937                 }
46938                 
46939                 this.el.dom.selectionStart = pos;
46940                 this.el.dom.selectionEnd = curr;
46941             },
46942             
46943             "end" : function(e){
46944                 e.preventDefault();
46945                 
46946                 var curr = this.el.dom.selectionStart;
46947                 var lines = this.getValue().split("\n");
46948                 
46949                 if(!lines.length){
46950                     return;
46951                 }
46952                 
46953                 if(e.ctrlKey){
46954                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
46955                     return;
46956                 }
46957                 
46958                 var pos = 0;
46959                 
46960                 for (var i = 0; i < lines.length;i++) {
46961                     
46962                     pos += lines[i].length;
46963                     
46964                     if(i != 0){
46965                         pos += 1;
46966                     }
46967                     
46968                     if(pos < curr){
46969                         continue;
46970                     }
46971                     
46972                     break;
46973                 }
46974                 
46975                 if(!e.shiftKey){
46976                     this.el.dom.setSelectionRange(pos, pos);
46977                     return;
46978                 }
46979                 
46980                 this.el.dom.selectionStart = curr;
46981                 this.el.dom.selectionEnd = pos;
46982             },
46983
46984             scope : this,
46985
46986             doRelay : function(foo, bar, hname){
46987                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
46988             },
46989
46990             forceKeyDown: true
46991         });
46992         
46993 //        if(this.autosave && this.w){
46994 //            this.autoSaveFn = setInterval(this.autosave, 1000);
46995 //        }
46996     },
46997
46998     // private
46999     onResize : function(w, h)
47000     {
47001         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
47002         var ew = false;
47003         var eh = false;
47004         
47005         if(this.el ){
47006             if(typeof w == 'number'){
47007                 var aw = w - this.wrap.getFrameWidth('lr');
47008                 this.el.setWidth(this.adjustWidth('textarea', aw));
47009                 ew = aw;
47010             }
47011             if(typeof h == 'number'){
47012                 var tbh = 0;
47013                 for (var i =0; i < this.toolbars.length;i++) {
47014                     // fixme - ask toolbars for heights?
47015                     tbh += this.toolbars[i].tb.el.getHeight();
47016                     if (this.toolbars[i].footer) {
47017                         tbh += this.toolbars[i].footer.el.getHeight();
47018                     }
47019                 }
47020                 
47021                 
47022                 
47023                 
47024                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
47025                 ah -= 5; // knock a few pixes off for look..
47026 //                Roo.log(ah);
47027                 this.el.setHeight(this.adjustWidth('textarea', ah));
47028                 var eh = ah;
47029             }
47030         }
47031         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
47032         this.editorcore.onResize(ew,eh);
47033         
47034     },
47035
47036     /**
47037      * Toggles the editor between standard and source edit mode.
47038      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
47039      */
47040     toggleSourceEdit : function(sourceEditMode)
47041     {
47042         this.editorcore.toggleSourceEdit(sourceEditMode);
47043         
47044         if(this.editorcore.sourceEditMode){
47045             Roo.log('editor - showing textarea');
47046             
47047 //            Roo.log('in');
47048 //            Roo.log(this.syncValue());
47049             this.editorcore.syncValue();
47050             this.el.removeClass('x-hidden');
47051             this.el.dom.removeAttribute('tabIndex');
47052             this.el.focus();
47053             
47054             for (var i = 0; i < this.toolbars.length; i++) {
47055                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
47056                     this.toolbars[i].tb.hide();
47057                     this.toolbars[i].footer.hide();
47058                 }
47059             }
47060             
47061         }else{
47062             Roo.log('editor - hiding textarea');
47063 //            Roo.log('out')
47064 //            Roo.log(this.pushValue()); 
47065             this.editorcore.pushValue();
47066             
47067             this.el.addClass('x-hidden');
47068             this.el.dom.setAttribute('tabIndex', -1);
47069             
47070             for (var i = 0; i < this.toolbars.length; i++) {
47071                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
47072                     this.toolbars[i].tb.show();
47073                     this.toolbars[i].footer.show();
47074                 }
47075             }
47076             
47077             //this.deferFocus();
47078         }
47079         
47080         this.setSize(this.wrap.getSize());
47081         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
47082         
47083         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
47084     },
47085  
47086     // private (for BoxComponent)
47087     adjustSize : Roo.BoxComponent.prototype.adjustSize,
47088
47089     // private (for BoxComponent)
47090     getResizeEl : function(){
47091         return this.wrap;
47092     },
47093
47094     // private (for BoxComponent)
47095     getPositionEl : function(){
47096         return this.wrap;
47097     },
47098
47099     // private
47100     initEvents : function(){
47101         this.originalValue = this.getValue();
47102     },
47103
47104     /**
47105      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47106      * @method
47107      */
47108     markInvalid : Roo.emptyFn,
47109     /**
47110      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47111      * @method
47112      */
47113     clearInvalid : Roo.emptyFn,
47114
47115     setValue : function(v){
47116         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
47117         this.editorcore.pushValue();
47118     },
47119
47120      
47121     // private
47122     deferFocus : function(){
47123         this.focus.defer(10, this);
47124     },
47125
47126     // doc'ed in Field
47127     focus : function(){
47128         this.editorcore.focus();
47129         
47130     },
47131       
47132
47133     // private
47134     onDestroy : function(){
47135         
47136         
47137         
47138         if(this.rendered){
47139             
47140             for (var i =0; i < this.toolbars.length;i++) {
47141                 // fixme - ask toolbars for heights?
47142                 this.toolbars[i].onDestroy();
47143             }
47144             
47145             this.wrap.dom.innerHTML = '';
47146             this.wrap.remove();
47147         }
47148     },
47149
47150     // private
47151     onFirstFocus : function(){
47152         //Roo.log("onFirstFocus");
47153         this.editorcore.onFirstFocus();
47154          for (var i =0; i < this.toolbars.length;i++) {
47155             this.toolbars[i].onFirstFocus();
47156         }
47157         
47158     },
47159     
47160     // private
47161     syncValue : function()
47162     {
47163         this.editorcore.syncValue();
47164     },
47165     
47166     pushValue : function()
47167     {
47168         this.editorcore.pushValue();
47169     },
47170     
47171     setStylesheets : function(stylesheets)
47172     {
47173         this.editorcore.setStylesheets(stylesheets);
47174     },
47175     
47176     removeStylesheets : function()
47177     {
47178         this.editorcore.removeStylesheets();
47179     }
47180      
47181     
47182     // hide stuff that is not compatible
47183     /**
47184      * @event blur
47185      * @hide
47186      */
47187     /**
47188      * @event change
47189      * @hide
47190      */
47191     /**
47192      * @event focus
47193      * @hide
47194      */
47195     /**
47196      * @event specialkey
47197      * @hide
47198      */
47199     /**
47200      * @cfg {String} fieldClass @hide
47201      */
47202     /**
47203      * @cfg {String} focusClass @hide
47204      */
47205     /**
47206      * @cfg {String} autoCreate @hide
47207      */
47208     /**
47209      * @cfg {String} inputType @hide
47210      */
47211     /**
47212      * @cfg {String} invalidClass @hide
47213      */
47214     /**
47215      * @cfg {String} invalidText @hide
47216      */
47217     /**
47218      * @cfg {String} msgFx @hide
47219      */
47220     /**
47221      * @cfg {String} validateOnBlur @hide
47222      */
47223 });
47224  
47225     // <script type="text/javascript">
47226 /*
47227  * Based on
47228  * Ext JS Library 1.1.1
47229  * Copyright(c) 2006-2007, Ext JS, LLC.
47230  *  
47231  
47232  */
47233
47234 /**
47235  * @class Roo.form.HtmlEditorToolbar1
47236  * Basic Toolbar
47237  * 
47238  * Usage:
47239  *
47240  new Roo.form.HtmlEditor({
47241     ....
47242     toolbars : [
47243         new Roo.form.HtmlEditorToolbar1({
47244             disable : { fonts: 1 , format: 1, ..., ... , ...],
47245             btns : [ .... ]
47246         })
47247     }
47248      
47249  * 
47250  * @cfg {Object} disable List of elements to disable..
47251  * @cfg {Array} btns List of additional buttons.
47252  * 
47253  * 
47254  * NEEDS Extra CSS? 
47255  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
47256  */
47257  
47258 Roo.form.HtmlEditor.ToolbarStandard = function(config)
47259 {
47260     
47261     Roo.apply(this, config);
47262     
47263     // default disabled, based on 'good practice'..
47264     this.disable = this.disable || {};
47265     Roo.applyIf(this.disable, {
47266         fontSize : true,
47267         colors : true,
47268         specialElements : true
47269     });
47270     
47271     
47272     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47273     // dont call parent... till later.
47274 }
47275
47276 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
47277     
47278     tb: false,
47279     
47280     rendered: false,
47281     
47282     editor : false,
47283     editorcore : false,
47284     /**
47285      * @cfg {Object} disable  List of toolbar elements to disable
47286          
47287      */
47288     disable : false,
47289     
47290     
47291      /**
47292      * @cfg {String} createLinkText The default text for the create link prompt
47293      */
47294     createLinkText : 'Please enter the URL for the link:',
47295     /**
47296      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
47297      */
47298     defaultLinkValue : 'http:/'+'/',
47299    
47300     
47301       /**
47302      * @cfg {Array} fontFamilies An array of available font families
47303      */
47304     fontFamilies : [
47305         'Arial',
47306         'Courier New',
47307         'Tahoma',
47308         'Times New Roman',
47309         'Verdana'
47310     ],
47311     
47312     specialChars : [
47313            "&#169;",
47314           "&#174;",     
47315           "&#8482;",    
47316           "&#163;" ,    
47317          // "&#8212;",    
47318           "&#8230;",    
47319           "&#247;" ,    
47320         //  "&#225;" ,     ?? a acute?
47321            "&#8364;"    , //Euro
47322        //   "&#8220;"    ,
47323         //  "&#8221;"    ,
47324         //  "&#8226;"    ,
47325           "&#176;"  //   , // degrees
47326
47327          // "&#233;"     , // e ecute
47328          // "&#250;"     , // u ecute?
47329     ],
47330     
47331     specialElements : [
47332         {
47333             text: "Insert Table",
47334             xtype: 'MenuItem',
47335             xns : Roo.Menu,
47336             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
47337                 
47338         },
47339         {    
47340             text: "Insert Image",
47341             xtype: 'MenuItem',
47342             xns : Roo.Menu,
47343             ihtml : '<img src="about:blank"/>'
47344             
47345         }
47346         
47347          
47348     ],
47349     
47350     
47351     inputElements : [ 
47352             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
47353             "input:submit", "input:button", "select", "textarea", "label" ],
47354     formats : [
47355         ["p"] ,  
47356         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
47357         ["pre"],[ "code"], 
47358         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
47359         ['div'],['span'],
47360         ['sup'],['sub']
47361     ],
47362     
47363     cleanStyles : [
47364         "font-size"
47365     ],
47366      /**
47367      * @cfg {String} defaultFont default font to use.
47368      */
47369     defaultFont: 'tahoma',
47370    
47371     fontSelect : false,
47372     
47373     
47374     formatCombo : false,
47375     
47376     init : function(editor)
47377     {
47378         this.editor = editor;
47379         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47380         var editorcore = this.editorcore;
47381         
47382         var _t = this;
47383         
47384         var fid = editorcore.frameId;
47385         var etb = this;
47386         function btn(id, toggle, handler){
47387             var xid = fid + '-'+ id ;
47388             return {
47389                 id : xid,
47390                 cmd : id,
47391                 cls : 'x-btn-icon x-edit-'+id,
47392                 enableToggle:toggle !== false,
47393                 scope: _t, // was editor...
47394                 handler:handler||_t.relayBtnCmd,
47395                 clickEvent:'mousedown',
47396                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47397                 tabIndex:-1
47398             };
47399         }
47400         
47401         
47402         
47403         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47404         this.tb = tb;
47405          // stop form submits
47406         tb.el.on('click', function(e){
47407             e.preventDefault(); // what does this do?
47408         });
47409
47410         if(!this.disable.font) { // && !Roo.isSafari){
47411             /* why no safari for fonts 
47412             editor.fontSelect = tb.el.createChild({
47413                 tag:'select',
47414                 tabIndex: -1,
47415                 cls:'x-font-select',
47416                 html: this.createFontOptions()
47417             });
47418             
47419             editor.fontSelect.on('change', function(){
47420                 var font = editor.fontSelect.dom.value;
47421                 editor.relayCmd('fontname', font);
47422                 editor.deferFocus();
47423             }, editor);
47424             
47425             tb.add(
47426                 editor.fontSelect.dom,
47427                 '-'
47428             );
47429             */
47430             
47431         };
47432         if(!this.disable.formats){
47433             this.formatCombo = new Roo.form.ComboBox({
47434                 store: new Roo.data.SimpleStore({
47435                     id : 'tag',
47436                     fields: ['tag'],
47437                     data : this.formats // from states.js
47438                 }),
47439                 blockFocus : true,
47440                 name : '',
47441                 //autoCreate : {tag: "div",  size: "20"},
47442                 displayField:'tag',
47443                 typeAhead: false,
47444                 mode: 'local',
47445                 editable : false,
47446                 triggerAction: 'all',
47447                 emptyText:'Add tag',
47448                 selectOnFocus:true,
47449                 width:135,
47450                 listeners : {
47451                     'select': function(c, r, i) {
47452                         editorcore.insertTag(r.get('tag'));
47453                         editor.focus();
47454                     }
47455                 }
47456
47457             });
47458             tb.addField(this.formatCombo);
47459             
47460         }
47461         
47462         if(!this.disable.format){
47463             tb.add(
47464                 btn('bold'),
47465                 btn('italic'),
47466                 btn('underline'),
47467                 btn('strikethrough')
47468             );
47469         };
47470         if(!this.disable.fontSize){
47471             tb.add(
47472                 '-',
47473                 
47474                 
47475                 btn('increasefontsize', false, editorcore.adjustFont),
47476                 btn('decreasefontsize', false, editorcore.adjustFont)
47477             );
47478         };
47479         
47480         
47481         if(!this.disable.colors){
47482             tb.add(
47483                 '-', {
47484                     id:editorcore.frameId +'-forecolor',
47485                     cls:'x-btn-icon x-edit-forecolor',
47486                     clickEvent:'mousedown',
47487                     tooltip: this.buttonTips['forecolor'] || undefined,
47488                     tabIndex:-1,
47489                     menu : new Roo.menu.ColorMenu({
47490                         allowReselect: true,
47491                         focus: Roo.emptyFn,
47492                         value:'000000',
47493                         plain:true,
47494                         selectHandler: function(cp, color){
47495                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
47496                             editor.deferFocus();
47497                         },
47498                         scope: editorcore,
47499                         clickEvent:'mousedown'
47500                     })
47501                 }, {
47502                     id:editorcore.frameId +'backcolor',
47503                     cls:'x-btn-icon x-edit-backcolor',
47504                     clickEvent:'mousedown',
47505                     tooltip: this.buttonTips['backcolor'] || undefined,
47506                     tabIndex:-1,
47507                     menu : new Roo.menu.ColorMenu({
47508                         focus: Roo.emptyFn,
47509                         value:'FFFFFF',
47510                         plain:true,
47511                         allowReselect: true,
47512                         selectHandler: function(cp, color){
47513                             if(Roo.isGecko){
47514                                 editorcore.execCmd('useCSS', false);
47515                                 editorcore.execCmd('hilitecolor', color);
47516                                 editorcore.execCmd('useCSS', true);
47517                                 editor.deferFocus();
47518                             }else{
47519                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
47520                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
47521                                 editor.deferFocus();
47522                             }
47523                         },
47524                         scope:editorcore,
47525                         clickEvent:'mousedown'
47526                     })
47527                 }
47528             );
47529         };
47530         // now add all the items...
47531         
47532
47533         if(!this.disable.alignments){
47534             tb.add(
47535                 '-',
47536                 btn('justifyleft'),
47537                 btn('justifycenter'),
47538                 btn('justifyright')
47539             );
47540         };
47541
47542         //if(!Roo.isSafari){
47543             if(!this.disable.links){
47544                 tb.add(
47545                     '-',
47546                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
47547                 );
47548             };
47549
47550             if(!this.disable.lists){
47551                 tb.add(
47552                     '-',
47553                     btn('insertorderedlist'),
47554                     btn('insertunorderedlist')
47555                 );
47556             }
47557             if(!this.disable.sourceEdit){
47558                 tb.add(
47559                     '-',
47560                     btn('sourceedit', true, function(btn){
47561                         this.toggleSourceEdit(btn.pressed);
47562                     })
47563                 );
47564             }
47565         //}
47566         
47567         var smenu = { };
47568         // special menu.. - needs to be tidied up..
47569         if (!this.disable.special) {
47570             smenu = {
47571                 text: "&#169;",
47572                 cls: 'x-edit-none',
47573                 
47574                 menu : {
47575                     items : []
47576                 }
47577             };
47578             for (var i =0; i < this.specialChars.length; i++) {
47579                 smenu.menu.items.push({
47580                     
47581                     html: this.specialChars[i],
47582                     handler: function(a,b) {
47583                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
47584                         //editor.insertAtCursor(a.html);
47585                         
47586                     },
47587                     tabIndex:-1
47588                 });
47589             }
47590             
47591             
47592             tb.add(smenu);
47593             
47594             
47595         }
47596         
47597         var cmenu = { };
47598         if (!this.disable.cleanStyles) {
47599             cmenu = {
47600                 cls: 'x-btn-icon x-btn-clear',
47601                 
47602                 menu : {
47603                     items : []
47604                 }
47605             };
47606             for (var i =0; i < this.cleanStyles.length; i++) {
47607                 cmenu.menu.items.push({
47608                     actiontype : this.cleanStyles[i],
47609                     html: 'Remove ' + this.cleanStyles[i],
47610                     handler: function(a,b) {
47611 //                        Roo.log(a);
47612 //                        Roo.log(b);
47613                         var c = Roo.get(editorcore.doc.body);
47614                         c.select('[style]').each(function(s) {
47615                             s.dom.style.removeProperty(a.actiontype);
47616                         });
47617                         editorcore.syncValue();
47618                     },
47619                     tabIndex:-1
47620                 });
47621             }
47622             cmenu.menu.items.push({
47623                 actiontype : 'tablewidths',
47624                 html: 'Remove Table Widths',
47625                 handler: function(a,b) {
47626                     editorcore.cleanTableWidths();
47627                     editorcore.syncValue();
47628                 },
47629                 tabIndex:-1
47630             });
47631             cmenu.menu.items.push({
47632                 actiontype : 'word',
47633                 html: 'Remove MS Word Formating',
47634                 handler: function(a,b) {
47635                     editorcore.cleanWord();
47636                     editorcore.syncValue();
47637                 },
47638                 tabIndex:-1
47639             });
47640             
47641             cmenu.menu.items.push({
47642                 actiontype : 'all',
47643                 html: 'Remove All Styles',
47644                 handler: function(a,b) {
47645                     
47646                     var c = Roo.get(editorcore.doc.body);
47647                     c.select('[style]').each(function(s) {
47648                         s.dom.removeAttribute('style');
47649                     });
47650                     editorcore.syncValue();
47651                 },
47652                 tabIndex:-1
47653             });
47654             
47655             cmenu.menu.items.push({
47656                 actiontype : 'all',
47657                 html: 'Remove All CSS Classes',
47658                 handler: function(a,b) {
47659                     
47660                     var c = Roo.get(editorcore.doc.body);
47661                     c.select('[class]').each(function(s) {
47662                         s.dom.removeAttribute('class');
47663                     });
47664                     editorcore.cleanWord();
47665                     editorcore.syncValue();
47666                 },
47667                 tabIndex:-1
47668             });
47669             
47670              cmenu.menu.items.push({
47671                 actiontype : 'tidy',
47672                 html: 'Tidy HTML Source',
47673                 handler: function(a,b) {
47674                     editorcore.doc.body.innerHTML = editorcore.domToHTML();
47675                     editorcore.syncValue();
47676                 },
47677                 tabIndex:-1
47678             });
47679             
47680             
47681             tb.add(cmenu);
47682         }
47683          
47684         if (!this.disable.specialElements) {
47685             var semenu = {
47686                 text: "Other;",
47687                 cls: 'x-edit-none',
47688                 menu : {
47689                     items : []
47690                 }
47691             };
47692             for (var i =0; i < this.specialElements.length; i++) {
47693                 semenu.menu.items.push(
47694                     Roo.apply({ 
47695                         handler: function(a,b) {
47696                             editor.insertAtCursor(this.ihtml);
47697                         }
47698                     }, this.specialElements[i])
47699                 );
47700                     
47701             }
47702             
47703             tb.add(semenu);
47704             
47705             
47706         }
47707          
47708         
47709         if (this.btns) {
47710             for(var i =0; i< this.btns.length;i++) {
47711                 var b = Roo.factory(this.btns[i],Roo.form);
47712                 b.cls =  'x-edit-none';
47713                 
47714                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
47715                     b.cls += ' x-init-enable';
47716                 }
47717                 
47718                 b.scope = editorcore;
47719                 tb.add(b);
47720             }
47721         
47722         }
47723         
47724         
47725         
47726         // disable everything...
47727         
47728         this.tb.items.each(function(item){
47729             
47730            if(
47731                 item.id != editorcore.frameId+ '-sourceedit' && 
47732                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
47733             ){
47734                 
47735                 item.disable();
47736             }
47737         });
47738         this.rendered = true;
47739         
47740         // the all the btns;
47741         editor.on('editorevent', this.updateToolbar, this);
47742         // other toolbars need to implement this..
47743         //editor.on('editmodechange', this.updateToolbar, this);
47744     },
47745     
47746     
47747     relayBtnCmd : function(btn) {
47748         this.editorcore.relayCmd(btn.cmd);
47749     },
47750     // private used internally
47751     createLink : function(){
47752         Roo.log("create link?");
47753         var url = prompt(this.createLinkText, this.defaultLinkValue);
47754         if(url && url != 'http:/'+'/'){
47755             this.editorcore.relayCmd('createlink', url);
47756         }
47757     },
47758
47759     
47760     /**
47761      * Protected method that will not generally be called directly. It triggers
47762      * a toolbar update by reading the markup state of the current selection in the editor.
47763      */
47764     updateToolbar: function(){
47765
47766         if(!this.editorcore.activated){
47767             this.editor.onFirstFocus();
47768             return;
47769         }
47770
47771         var btns = this.tb.items.map, 
47772             doc = this.editorcore.doc,
47773             frameId = this.editorcore.frameId;
47774
47775         if(!this.disable.font && !Roo.isSafari){
47776             /*
47777             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
47778             if(name != this.fontSelect.dom.value){
47779                 this.fontSelect.dom.value = name;
47780             }
47781             */
47782         }
47783         if(!this.disable.format){
47784             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
47785             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
47786             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
47787             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
47788         }
47789         if(!this.disable.alignments){
47790             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
47791             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
47792             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
47793         }
47794         if(!Roo.isSafari && !this.disable.lists){
47795             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
47796             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
47797         }
47798         
47799         var ans = this.editorcore.getAllAncestors();
47800         if (this.formatCombo) {
47801             
47802             
47803             var store = this.formatCombo.store;
47804             this.formatCombo.setValue("");
47805             for (var i =0; i < ans.length;i++) {
47806                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
47807                     // select it..
47808                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
47809                     break;
47810                 }
47811             }
47812         }
47813         
47814         
47815         
47816         // hides menus... - so this cant be on a menu...
47817         Roo.menu.MenuMgr.hideAll();
47818
47819         //this.editorsyncValue();
47820     },
47821    
47822     
47823     createFontOptions : function(){
47824         var buf = [], fs = this.fontFamilies, ff, lc;
47825         
47826         
47827         
47828         for(var i = 0, len = fs.length; i< len; i++){
47829             ff = fs[i];
47830             lc = ff.toLowerCase();
47831             buf.push(
47832                 '<option value="',lc,'" style="font-family:',ff,';"',
47833                     (this.defaultFont == lc ? ' selected="true">' : '>'),
47834                     ff,
47835                 '</option>'
47836             );
47837         }
47838         return buf.join('');
47839     },
47840     
47841     toggleSourceEdit : function(sourceEditMode){
47842         
47843         Roo.log("toolbar toogle");
47844         if(sourceEditMode === undefined){
47845             sourceEditMode = !this.sourceEditMode;
47846         }
47847         this.sourceEditMode = sourceEditMode === true;
47848         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
47849         // just toggle the button?
47850         if(btn.pressed !== this.sourceEditMode){
47851             btn.toggle(this.sourceEditMode);
47852             return;
47853         }
47854         
47855         if(sourceEditMode){
47856             Roo.log("disabling buttons");
47857             this.tb.items.each(function(item){
47858                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
47859                     item.disable();
47860                 }
47861             });
47862           
47863         }else{
47864             Roo.log("enabling buttons");
47865             if(this.editorcore.initialized){
47866                 this.tb.items.each(function(item){
47867                     item.enable();
47868                 });
47869             }
47870             
47871         }
47872         Roo.log("calling toggole on editor");
47873         // tell the editor that it's been pressed..
47874         this.editor.toggleSourceEdit(sourceEditMode);
47875        
47876     },
47877      /**
47878      * Object collection of toolbar tooltips for the buttons in the editor. The key
47879      * is the command id associated with that button and the value is a valid QuickTips object.
47880      * For example:
47881 <pre><code>
47882 {
47883     bold : {
47884         title: 'Bold (Ctrl+B)',
47885         text: 'Make the selected text bold.',
47886         cls: 'x-html-editor-tip'
47887     },
47888     italic : {
47889         title: 'Italic (Ctrl+I)',
47890         text: 'Make the selected text italic.',
47891         cls: 'x-html-editor-tip'
47892     },
47893     ...
47894 </code></pre>
47895     * @type Object
47896      */
47897     buttonTips : {
47898         bold : {
47899             title: 'Bold (Ctrl+B)',
47900             text: 'Make the selected text bold.',
47901             cls: 'x-html-editor-tip'
47902         },
47903         italic : {
47904             title: 'Italic (Ctrl+I)',
47905             text: 'Make the selected text italic.',
47906             cls: 'x-html-editor-tip'
47907         },
47908         underline : {
47909             title: 'Underline (Ctrl+U)',
47910             text: 'Underline the selected text.',
47911             cls: 'x-html-editor-tip'
47912         },
47913         strikethrough : {
47914             title: 'Strikethrough',
47915             text: 'Strikethrough the selected text.',
47916             cls: 'x-html-editor-tip'
47917         },
47918         increasefontsize : {
47919             title: 'Grow Text',
47920             text: 'Increase the font size.',
47921             cls: 'x-html-editor-tip'
47922         },
47923         decreasefontsize : {
47924             title: 'Shrink Text',
47925             text: 'Decrease the font size.',
47926             cls: 'x-html-editor-tip'
47927         },
47928         backcolor : {
47929             title: 'Text Highlight Color',
47930             text: 'Change the background color of the selected text.',
47931             cls: 'x-html-editor-tip'
47932         },
47933         forecolor : {
47934             title: 'Font Color',
47935             text: 'Change the color of the selected text.',
47936             cls: 'x-html-editor-tip'
47937         },
47938         justifyleft : {
47939             title: 'Align Text Left',
47940             text: 'Align text to the left.',
47941             cls: 'x-html-editor-tip'
47942         },
47943         justifycenter : {
47944             title: 'Center Text',
47945             text: 'Center text in the editor.',
47946             cls: 'x-html-editor-tip'
47947         },
47948         justifyright : {
47949             title: 'Align Text Right',
47950             text: 'Align text to the right.',
47951             cls: 'x-html-editor-tip'
47952         },
47953         insertunorderedlist : {
47954             title: 'Bullet List',
47955             text: 'Start a bulleted list.',
47956             cls: 'x-html-editor-tip'
47957         },
47958         insertorderedlist : {
47959             title: 'Numbered List',
47960             text: 'Start a numbered list.',
47961             cls: 'x-html-editor-tip'
47962         },
47963         createlink : {
47964             title: 'Hyperlink',
47965             text: 'Make the selected text a hyperlink.',
47966             cls: 'x-html-editor-tip'
47967         },
47968         sourceedit : {
47969             title: 'Source Edit',
47970             text: 'Switch to source editing mode.',
47971             cls: 'x-html-editor-tip'
47972         }
47973     },
47974     // private
47975     onDestroy : function(){
47976         if(this.rendered){
47977             
47978             this.tb.items.each(function(item){
47979                 if(item.menu){
47980                     item.menu.removeAll();
47981                     if(item.menu.el){
47982                         item.menu.el.destroy();
47983                     }
47984                 }
47985                 item.destroy();
47986             });
47987              
47988         }
47989     },
47990     onFirstFocus: function() {
47991         this.tb.items.each(function(item){
47992            item.enable();
47993         });
47994     }
47995 });
47996
47997
47998
47999
48000 // <script type="text/javascript">
48001 /*
48002  * Based on
48003  * Ext JS Library 1.1.1
48004  * Copyright(c) 2006-2007, Ext JS, LLC.
48005  *  
48006  
48007  */
48008
48009  
48010 /**
48011  * @class Roo.form.HtmlEditor.ToolbarContext
48012  * Context Toolbar
48013  * 
48014  * Usage:
48015  *
48016  new Roo.form.HtmlEditor({
48017     ....
48018     toolbars : [
48019         { xtype: 'ToolbarStandard', styles : {} }
48020         { xtype: 'ToolbarContext', disable : {} }
48021     ]
48022 })
48023
48024      
48025  * 
48026  * @config : {Object} disable List of elements to disable.. (not done yet.)
48027  * @config : {Object} styles  Map of styles available.
48028  * 
48029  */
48030
48031 Roo.form.HtmlEditor.ToolbarContext = function(config)
48032 {
48033     
48034     Roo.apply(this, config);
48035     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48036     // dont call parent... till later.
48037     this.styles = this.styles || {};
48038 }
48039
48040  
48041
48042 Roo.form.HtmlEditor.ToolbarContext.types = {
48043     'IMG' : {
48044         width : {
48045             title: "Width",
48046             width: 40
48047         },
48048         height:  {
48049             title: "Height",
48050             width: 40
48051         },
48052         align: {
48053             title: "Align",
48054             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
48055             width : 80
48056             
48057         },
48058         border: {
48059             title: "Border",
48060             width: 40
48061         },
48062         alt: {
48063             title: "Alt",
48064             width: 120
48065         },
48066         src : {
48067             title: "Src",
48068             width: 220
48069         }
48070         
48071     },
48072     'A' : {
48073         name : {
48074             title: "Name",
48075             width: 50
48076         },
48077         target:  {
48078             title: "Target",
48079             width: 120
48080         },
48081         href:  {
48082             title: "Href",
48083             width: 220
48084         } // border?
48085         
48086     },
48087     'TABLE' : {
48088         rows : {
48089             title: "Rows",
48090             width: 20
48091         },
48092         cols : {
48093             title: "Cols",
48094             width: 20
48095         },
48096         width : {
48097             title: "Width",
48098             width: 40
48099         },
48100         height : {
48101             title: "Height",
48102             width: 40
48103         },
48104         border : {
48105             title: "Border",
48106             width: 20
48107         }
48108     },
48109     'TD' : {
48110         width : {
48111             title: "Width",
48112             width: 40
48113         },
48114         height : {
48115             title: "Height",
48116             width: 40
48117         },   
48118         align: {
48119             title: "Align",
48120             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
48121             width: 80
48122         },
48123         valign: {
48124             title: "Valign",
48125             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
48126             width: 80
48127         },
48128         colspan: {
48129             title: "Colspan",
48130             width: 20
48131             
48132         },
48133          'font-family'  : {
48134             title : "Font",
48135             style : 'fontFamily',
48136             displayField: 'display',
48137             optname : 'font-family',
48138             width: 140
48139         }
48140     },
48141     'INPUT' : {
48142         name : {
48143             title: "name",
48144             width: 120
48145         },
48146         value : {
48147             title: "Value",
48148             width: 120
48149         },
48150         width : {
48151             title: "Width",
48152             width: 40
48153         }
48154     },
48155     'LABEL' : {
48156         'for' : {
48157             title: "For",
48158             width: 120
48159         }
48160     },
48161     'TEXTAREA' : {
48162           name : {
48163             title: "name",
48164             width: 120
48165         },
48166         rows : {
48167             title: "Rows",
48168             width: 20
48169         },
48170         cols : {
48171             title: "Cols",
48172             width: 20
48173         }
48174     },
48175     'SELECT' : {
48176         name : {
48177             title: "name",
48178             width: 120
48179         },
48180         selectoptions : {
48181             title: "Options",
48182             width: 200
48183         }
48184     },
48185     
48186     // should we really allow this??
48187     // should this just be 
48188     'BODY' : {
48189         title : {
48190             title: "Title",
48191             width: 200,
48192             disabled : true
48193         }
48194     },
48195     'SPAN' : {
48196         'font-family'  : {
48197             title : "Font",
48198             style : 'fontFamily',
48199             displayField: 'display',
48200             optname : 'font-family',
48201             width: 140
48202         }
48203     },
48204     'DIV' : {
48205         'font-family'  : {
48206             title : "Font",
48207             style : 'fontFamily',
48208             displayField: 'display',
48209             optname : 'font-family',
48210             width: 140
48211         }
48212     },
48213      'P' : {
48214         'font-family'  : {
48215             title : "Font",
48216             style : 'fontFamily',
48217             displayField: 'display',
48218             optname : 'font-family',
48219             width: 140
48220         }
48221     },
48222     
48223     '*' : {
48224         // empty..
48225     }
48226
48227 };
48228
48229 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
48230 Roo.form.HtmlEditor.ToolbarContext.stores = false;
48231
48232 Roo.form.HtmlEditor.ToolbarContext.options = {
48233         'font-family'  : [ 
48234                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
48235                 [ 'Courier New', 'Courier New'],
48236                 [ 'Tahoma', 'Tahoma'],
48237                 [ 'Times New Roman,serif', 'Times'],
48238                 [ 'Verdana','Verdana' ]
48239         ]
48240 };
48241
48242 // fixme - these need to be configurable..
48243  
48244
48245 //Roo.form.HtmlEditor.ToolbarContext.types
48246
48247
48248 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
48249     
48250     tb: false,
48251     
48252     rendered: false,
48253     
48254     editor : false,
48255     editorcore : false,
48256     /**
48257      * @cfg {Object} disable  List of toolbar elements to disable
48258          
48259      */
48260     disable : false,
48261     /**
48262      * @cfg {Object} styles List of styles 
48263      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
48264      *
48265      * These must be defined in the page, so they get rendered correctly..
48266      * .headline { }
48267      * TD.underline { }
48268      * 
48269      */
48270     styles : false,
48271     
48272     options: false,
48273     
48274     toolbars : false,
48275     
48276     init : function(editor)
48277     {
48278         this.editor = editor;
48279         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48280         var editorcore = this.editorcore;
48281         
48282         var fid = editorcore.frameId;
48283         var etb = this;
48284         function btn(id, toggle, handler){
48285             var xid = fid + '-'+ id ;
48286             return {
48287                 id : xid,
48288                 cmd : id,
48289                 cls : 'x-btn-icon x-edit-'+id,
48290                 enableToggle:toggle !== false,
48291                 scope: editorcore, // was editor...
48292                 handler:handler||editorcore.relayBtnCmd,
48293                 clickEvent:'mousedown',
48294                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48295                 tabIndex:-1
48296             };
48297         }
48298         // create a new element.
48299         var wdiv = editor.wrap.createChild({
48300                 tag: 'div'
48301             }, editor.wrap.dom.firstChild.nextSibling, true);
48302         
48303         // can we do this more than once??
48304         
48305          // stop form submits
48306       
48307  
48308         // disable everything...
48309         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48310         this.toolbars = {};
48311            
48312         for (var i in  ty) {
48313           
48314             this.toolbars[i] = this.buildToolbar(ty[i],i);
48315         }
48316         this.tb = this.toolbars.BODY;
48317         this.tb.el.show();
48318         this.buildFooter();
48319         this.footer.show();
48320         editor.on('hide', function( ) { this.footer.hide() }, this);
48321         editor.on('show', function( ) { this.footer.show() }, this);
48322         
48323          
48324         this.rendered = true;
48325         
48326         // the all the btns;
48327         editor.on('editorevent', this.updateToolbar, this);
48328         // other toolbars need to implement this..
48329         //editor.on('editmodechange', this.updateToolbar, this);
48330     },
48331     
48332     
48333     
48334     /**
48335      * Protected method that will not generally be called directly. It triggers
48336      * a toolbar update by reading the markup state of the current selection in the editor.
48337      *
48338      * Note you can force an update by calling on('editorevent', scope, false)
48339      */
48340     updateToolbar: function(editor,ev,sel){
48341
48342         //Roo.log(ev);
48343         // capture mouse up - this is handy for selecting images..
48344         // perhaps should go somewhere else...
48345         if(!this.editorcore.activated){
48346              this.editor.onFirstFocus();
48347             return;
48348         }
48349         
48350         
48351         
48352         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
48353         // selectNode - might want to handle IE?
48354         if (ev &&
48355             (ev.type == 'mouseup' || ev.type == 'click' ) &&
48356             ev.target && ev.target.tagName == 'IMG') {
48357             // they have click on an image...
48358             // let's see if we can change the selection...
48359             sel = ev.target;
48360          
48361               var nodeRange = sel.ownerDocument.createRange();
48362             try {
48363                 nodeRange.selectNode(sel);
48364             } catch (e) {
48365                 nodeRange.selectNodeContents(sel);
48366             }
48367             //nodeRange.collapse(true);
48368             var s = this.editorcore.win.getSelection();
48369             s.removeAllRanges();
48370             s.addRange(nodeRange);
48371         }  
48372         
48373       
48374         var updateFooter = sel ? false : true;
48375         
48376         
48377         var ans = this.editorcore.getAllAncestors();
48378         
48379         // pick
48380         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48381         
48382         if (!sel) { 
48383             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
48384             sel = sel ? sel : this.editorcore.doc.body;
48385             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
48386             
48387         }
48388         // pick a menu that exists..
48389         var tn = sel.tagName.toUpperCase();
48390         //sel = typeof(ty[tn]) != 'undefined' ? sel : this.editor.doc.body;
48391         
48392         tn = sel.tagName.toUpperCase();
48393         
48394         var lastSel = this.tb.selectedNode;
48395         
48396         this.tb.selectedNode = sel;
48397         
48398         // if current menu does not match..
48399         
48400         if ((this.tb.name != tn) || (lastSel != this.tb.selectedNode) || ev === false) {
48401                 
48402             this.tb.el.hide();
48403             ///console.log("show: " + tn);
48404             this.tb =  typeof(ty[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
48405             this.tb.el.show();
48406             // update name
48407             this.tb.items.first().el.innerHTML = tn + ':&nbsp;';
48408             
48409             
48410             // update attributes
48411             if (this.tb.fields) {
48412                 this.tb.fields.each(function(e) {
48413                     if (e.stylename) {
48414                         e.setValue(sel.style[e.stylename]);
48415                         return;
48416                     } 
48417                    e.setValue(sel.getAttribute(e.attrname));
48418                 });
48419             }
48420             
48421             var hasStyles = false;
48422             for(var i in this.styles) {
48423                 hasStyles = true;
48424                 break;
48425             }
48426             
48427             // update styles
48428             if (hasStyles) { 
48429                 var st = this.tb.fields.item(0);
48430                 
48431                 st.store.removeAll();
48432                
48433                 
48434                 var cn = sel.className.split(/\s+/);
48435                 
48436                 var avs = [];
48437                 if (this.styles['*']) {
48438                     
48439                     Roo.each(this.styles['*'], function(v) {
48440                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48441                     });
48442                 }
48443                 if (this.styles[tn]) { 
48444                     Roo.each(this.styles[tn], function(v) {
48445                         avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48446                     });
48447                 }
48448                 
48449                 st.store.loadData(avs);
48450                 st.collapse();
48451                 st.setValue(cn);
48452             }
48453             // flag our selected Node.
48454             this.tb.selectedNode = sel;
48455            
48456            
48457             Roo.menu.MenuMgr.hideAll();
48458
48459         }
48460         
48461         if (!updateFooter) {
48462             //this.footDisp.dom.innerHTML = ''; 
48463             return;
48464         }
48465         // update the footer
48466         //
48467         var html = '';
48468         
48469         this.footerEls = ans.reverse();
48470         Roo.each(this.footerEls, function(a,i) {
48471             if (!a) { return; }
48472             html += html.length ? ' &gt; '  :  '';
48473             
48474             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
48475             
48476         });
48477        
48478         // 
48479         var sz = this.footDisp.up('td').getSize();
48480         this.footDisp.dom.style.width = (sz.width -10) + 'px';
48481         this.footDisp.dom.style.marginLeft = '5px';
48482         
48483         this.footDisp.dom.style.overflow = 'hidden';
48484         
48485         this.footDisp.dom.innerHTML = html;
48486             
48487         //this.editorsyncValue();
48488     },
48489      
48490     
48491    
48492        
48493     // private
48494     onDestroy : function(){
48495         if(this.rendered){
48496             
48497             this.tb.items.each(function(item){
48498                 if(item.menu){
48499                     item.menu.removeAll();
48500                     if(item.menu.el){
48501                         item.menu.el.destroy();
48502                     }
48503                 }
48504                 item.destroy();
48505             });
48506              
48507         }
48508     },
48509     onFirstFocus: function() {
48510         // need to do this for all the toolbars..
48511         this.tb.items.each(function(item){
48512            item.enable();
48513         });
48514     },
48515     buildToolbar: function(tlist, nm)
48516     {
48517         var editor = this.editor;
48518         var editorcore = this.editorcore;
48519          // create a new element.
48520         var wdiv = editor.wrap.createChild({
48521                 tag: 'div'
48522             }, editor.wrap.dom.firstChild.nextSibling, true);
48523         
48524        
48525         var tb = new Roo.Toolbar(wdiv);
48526         // add the name..
48527         
48528         tb.add(nm+ ":&nbsp;");
48529         
48530         var styles = [];
48531         for(var i in this.styles) {
48532             styles.push(i);
48533         }
48534         
48535         // styles...
48536         if (styles && styles.length) {
48537             
48538             // this needs a multi-select checkbox...
48539             tb.addField( new Roo.form.ComboBox({
48540                 store: new Roo.data.SimpleStore({
48541                     id : 'val',
48542                     fields: ['val', 'selected'],
48543                     data : [] 
48544                 }),
48545                 name : '-roo-edit-className',
48546                 attrname : 'className',
48547                 displayField: 'val',
48548                 typeAhead: false,
48549                 mode: 'local',
48550                 editable : false,
48551                 triggerAction: 'all',
48552                 emptyText:'Select Style',
48553                 selectOnFocus:true,
48554                 width: 130,
48555                 listeners : {
48556                     'select': function(c, r, i) {
48557                         // initial support only for on class per el..
48558                         tb.selectedNode.className =  r ? r.get('val') : '';
48559                         editorcore.syncValue();
48560                     }
48561                 }
48562     
48563             }));
48564         }
48565         
48566         var tbc = Roo.form.HtmlEditor.ToolbarContext;
48567         var tbops = tbc.options;
48568         
48569         for (var i in tlist) {
48570             
48571             var item = tlist[i];
48572             tb.add(item.title + ":&nbsp;");
48573             
48574             
48575             //optname == used so you can configure the options available..
48576             var opts = item.opts ? item.opts : false;
48577             if (item.optname) {
48578                 opts = tbops[item.optname];
48579            
48580             }
48581             
48582             if (opts) {
48583                 // opts == pulldown..
48584                 tb.addField( new Roo.form.ComboBox({
48585                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
48586                         id : 'val',
48587                         fields: ['val', 'display'],
48588                         data : opts  
48589                     }),
48590                     name : '-roo-edit-' + i,
48591                     attrname : i,
48592                     stylename : item.style ? item.style : false,
48593                     displayField: item.displayField ? item.displayField : 'val',
48594                     valueField :  'val',
48595                     typeAhead: false,
48596                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
48597                     editable : false,
48598                     triggerAction: 'all',
48599                     emptyText:'Select',
48600                     selectOnFocus:true,
48601                     width: item.width ? item.width  : 130,
48602                     listeners : {
48603                         'select': function(c, r, i) {
48604                             if (c.stylename) {
48605                                 tb.selectedNode.style[c.stylename] =  r.get('val');
48606                                 return;
48607                             }
48608                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
48609                         }
48610                     }
48611
48612                 }));
48613                 continue;
48614                     
48615                  
48616                 
48617                 tb.addField( new Roo.form.TextField({
48618                     name: i,
48619                     width: 100,
48620                     //allowBlank:false,
48621                     value: ''
48622                 }));
48623                 continue;
48624             }
48625             tb.addField( new Roo.form.TextField({
48626                 name: '-roo-edit-' + i,
48627                 attrname : i,
48628                 
48629                 width: item.width,
48630                 //allowBlank:true,
48631                 value: '',
48632                 listeners: {
48633                     'change' : function(f, nv, ov) {
48634                         tb.selectedNode.setAttribute(f.attrname, nv);
48635                         editorcore.syncValue();
48636                     }
48637                 }
48638             }));
48639              
48640         }
48641         
48642         var _this = this;
48643         
48644         if(nm == 'BODY'){
48645             tb.addSeparator();
48646         
48647             tb.addButton( {
48648                 text: 'Stylesheets',
48649
48650                 listeners : {
48651                     click : function ()
48652                     {
48653                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
48654                     }
48655                 }
48656             });
48657         }
48658         
48659         tb.addFill();
48660         tb.addButton( {
48661             text: 'Remove Tag',
48662     
48663             listeners : {
48664                 click : function ()
48665                 {
48666                     // remove
48667                     // undo does not work.
48668                      
48669                     var sn = tb.selectedNode;
48670                     
48671                     var pn = sn.parentNode;
48672                     
48673                     var stn =  sn.childNodes[0];
48674                     var en = sn.childNodes[sn.childNodes.length - 1 ];
48675                     while (sn.childNodes.length) {
48676                         var node = sn.childNodes[0];
48677                         sn.removeChild(node);
48678                         //Roo.log(node);
48679                         pn.insertBefore(node, sn);
48680                         
48681                     }
48682                     pn.removeChild(sn);
48683                     var range = editorcore.createRange();
48684         
48685                     range.setStart(stn,0);
48686                     range.setEnd(en,0); //????
48687                     //range.selectNode(sel);
48688                     
48689                     
48690                     var selection = editorcore.getSelection();
48691                     selection.removeAllRanges();
48692                     selection.addRange(range);
48693                     
48694                     
48695                     
48696                     //_this.updateToolbar(null, null, pn);
48697                     _this.updateToolbar(null, null, null);
48698                     _this.footDisp.dom.innerHTML = ''; 
48699                 }
48700             }
48701             
48702                     
48703                 
48704             
48705         });
48706         
48707         
48708         tb.el.on('click', function(e){
48709             e.preventDefault(); // what does this do?
48710         });
48711         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
48712         tb.el.hide();
48713         tb.name = nm;
48714         // dont need to disable them... as they will get hidden
48715         return tb;
48716          
48717         
48718     },
48719     buildFooter : function()
48720     {
48721         
48722         var fel = this.editor.wrap.createChild();
48723         this.footer = new Roo.Toolbar(fel);
48724         // toolbar has scrolly on left / right?
48725         var footDisp= new Roo.Toolbar.Fill();
48726         var _t = this;
48727         this.footer.add(
48728             {
48729                 text : '&lt;',
48730                 xtype: 'Button',
48731                 handler : function() {
48732                     _t.footDisp.scrollTo('left',0,true)
48733                 }
48734             }
48735         );
48736         this.footer.add( footDisp );
48737         this.footer.add( 
48738             {
48739                 text : '&gt;',
48740                 xtype: 'Button',
48741                 handler : function() {
48742                     // no animation..
48743                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
48744                 }
48745             }
48746         );
48747         var fel = Roo.get(footDisp.el);
48748         fel.addClass('x-editor-context');
48749         this.footDispWrap = fel; 
48750         this.footDispWrap.overflow  = 'hidden';
48751         
48752         this.footDisp = fel.createChild();
48753         this.footDispWrap.on('click', this.onContextClick, this)
48754         
48755         
48756     },
48757     onContextClick : function (ev,dom)
48758     {
48759         ev.preventDefault();
48760         var  cn = dom.className;
48761         //Roo.log(cn);
48762         if (!cn.match(/x-ed-loc-/)) {
48763             return;
48764         }
48765         var n = cn.split('-').pop();
48766         var ans = this.footerEls;
48767         var sel = ans[n];
48768         
48769          // pick
48770         var range = this.editorcore.createRange();
48771         
48772         range.selectNodeContents(sel);
48773         //range.selectNode(sel);
48774         
48775         
48776         var selection = this.editorcore.getSelection();
48777         selection.removeAllRanges();
48778         selection.addRange(range);
48779         
48780         
48781         
48782         this.updateToolbar(null, null, sel);
48783         
48784         
48785     }
48786     
48787     
48788     
48789     
48790     
48791 });
48792
48793
48794
48795
48796
48797 /*
48798  * Based on:
48799  * Ext JS Library 1.1.1
48800  * Copyright(c) 2006-2007, Ext JS, LLC.
48801  *
48802  * Originally Released Under LGPL - original licence link has changed is not relivant.
48803  *
48804  * Fork - LGPL
48805  * <script type="text/javascript">
48806  */
48807  
48808 /**
48809  * @class Roo.form.BasicForm
48810  * @extends Roo.util.Observable
48811  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
48812  * @constructor
48813  * @param {String/HTMLElement/Roo.Element} el The form element or its id
48814  * @param {Object} config Configuration options
48815  */
48816 Roo.form.BasicForm = function(el, config){
48817     this.allItems = [];
48818     this.childForms = [];
48819     Roo.apply(this, config);
48820     /*
48821      * The Roo.form.Field items in this form.
48822      * @type MixedCollection
48823      */
48824      
48825      
48826     this.items = new Roo.util.MixedCollection(false, function(o){
48827         return o.id || (o.id = Roo.id());
48828     });
48829     this.addEvents({
48830         /**
48831          * @event beforeaction
48832          * Fires before any action is performed. Return false to cancel the action.
48833          * @param {Form} this
48834          * @param {Action} action The action to be performed
48835          */
48836         beforeaction: true,
48837         /**
48838          * @event actionfailed
48839          * Fires when an action fails.
48840          * @param {Form} this
48841          * @param {Action} action The action that failed
48842          */
48843         actionfailed : true,
48844         /**
48845          * @event actioncomplete
48846          * Fires when an action is completed.
48847          * @param {Form} this
48848          * @param {Action} action The action that completed
48849          */
48850         actioncomplete : true
48851     });
48852     if(el){
48853         this.initEl(el);
48854     }
48855     Roo.form.BasicForm.superclass.constructor.call(this);
48856     
48857     Roo.form.BasicForm.popover.apply();
48858 };
48859
48860 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
48861     /**
48862      * @cfg {String} method
48863      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
48864      */
48865     /**
48866      * @cfg {DataReader} reader
48867      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
48868      * This is optional as there is built-in support for processing JSON.
48869      */
48870     /**
48871      * @cfg {DataReader} errorReader
48872      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
48873      * This is completely optional as there is built-in support for processing JSON.
48874      */
48875     /**
48876      * @cfg {String} url
48877      * The URL to use for form actions if one isn't supplied in the action options.
48878      */
48879     /**
48880      * @cfg {Boolean} fileUpload
48881      * Set to true if this form is a file upload.
48882      */
48883      
48884     /**
48885      * @cfg {Object} baseParams
48886      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
48887      */
48888      /**
48889      
48890     /**
48891      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
48892      */
48893     timeout: 30,
48894
48895     // private
48896     activeAction : null,
48897
48898     /**
48899      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
48900      * or setValues() data instead of when the form was first created.
48901      */
48902     trackResetOnLoad : false,
48903     
48904     
48905     /**
48906      * childForms - used for multi-tab forms
48907      * @type {Array}
48908      */
48909     childForms : false,
48910     
48911     /**
48912      * allItems - full list of fields.
48913      * @type {Array}
48914      */
48915     allItems : false,
48916     
48917     /**
48918      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
48919      * element by passing it or its id or mask the form itself by passing in true.
48920      * @type Mixed
48921      */
48922     waitMsgTarget : false,
48923     
48924     /**
48925      * @type Boolean
48926      */
48927     disableMask : false,
48928     
48929     /**
48930      * @cfg {Boolean} errorMask (true|false) default false
48931      */
48932     errorMask : false,
48933     
48934     /**
48935      * @cfg {Number} maskOffset Default 100
48936      */
48937     maskOffset : 100,
48938
48939     // private
48940     initEl : function(el){
48941         this.el = Roo.get(el);
48942         this.id = this.el.id || Roo.id();
48943         this.el.on('submit', this.onSubmit, this);
48944         this.el.addClass('x-form');
48945     },
48946
48947     // private
48948     onSubmit : function(e){
48949         e.stopEvent();
48950     },
48951
48952     /**
48953      * Returns true if client-side validation on the form is successful.
48954      * @return Boolean
48955      */
48956     isValid : function(){
48957         var valid = true;
48958         var target = false;
48959         this.items.each(function(f){
48960             if(f.validate()){
48961                 return;
48962             }
48963             
48964             valid = false;
48965                 
48966             if(!target && f.el.isVisible(true)){
48967                 target = f;
48968             }
48969         });
48970         
48971         if(this.errorMask && !valid){
48972             Roo.form.BasicForm.popover.mask(this, target);
48973         }
48974         
48975         return valid;
48976     },
48977     /**
48978      * Returns array of invalid form fields.
48979      * @return Array
48980      */
48981     
48982     invalidFields : function()
48983     {
48984         var ret = [];
48985         this.items.each(function(f){
48986             if(f.validate()){
48987                 return;
48988             }
48989             ret.push(f);
48990             
48991         });
48992         
48993         return ret;
48994     },
48995     
48996     
48997     /**
48998      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
48999      * @return Boolean
49000      */
49001     isDirty : function(){
49002         var dirty = false;
49003         this.items.each(function(f){
49004            if(f.isDirty()){
49005                dirty = true;
49006                return false;
49007            }
49008         });
49009         return dirty;
49010     },
49011     
49012     /**
49013      * Returns true if any fields in this form have changed since their original load. (New version)
49014      * @return Boolean
49015      */
49016     
49017     hasChanged : function()
49018     {
49019         var dirty = false;
49020         this.items.each(function(f){
49021            if(f.hasChanged()){
49022                dirty = true;
49023                return false;
49024            }
49025         });
49026         return dirty;
49027         
49028     },
49029     /**
49030      * Resets all hasChanged to 'false' -
49031      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
49032      * So hasChanged storage is only to be used for this purpose
49033      * @return Boolean
49034      */
49035     resetHasChanged : function()
49036     {
49037         this.items.each(function(f){
49038            f.resetHasChanged();
49039         });
49040         
49041     },
49042     
49043     
49044     /**
49045      * Performs a predefined action (submit or load) or custom actions you define on this form.
49046      * @param {String} actionName The name of the action type
49047      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
49048      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
49049      * accept other config options):
49050      * <pre>
49051 Property          Type             Description
49052 ----------------  ---------------  ----------------------------------------------------------------------------------
49053 url               String           The url for the action (defaults to the form's url)
49054 method            String           The form method to use (defaults to the form's method, or POST if not defined)
49055 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
49056 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
49057                                    validate the form on the client (defaults to false)
49058      * </pre>
49059      * @return {BasicForm} this
49060      */
49061     doAction : function(action, options){
49062         if(typeof action == 'string'){
49063             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
49064         }
49065         if(this.fireEvent('beforeaction', this, action) !== false){
49066             this.beforeAction(action);
49067             action.run.defer(100, action);
49068         }
49069         return this;
49070     },
49071
49072     /**
49073      * Shortcut to do a submit action.
49074      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
49075      * @return {BasicForm} this
49076      */
49077     submit : function(options){
49078         this.doAction('submit', options);
49079         return this;
49080     },
49081
49082     /**
49083      * Shortcut to do a load action.
49084      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
49085      * @return {BasicForm} this
49086      */
49087     load : function(options){
49088         this.doAction('load', options);
49089         return this;
49090     },
49091
49092     /**
49093      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
49094      * @param {Record} record The record to edit
49095      * @return {BasicForm} this
49096      */
49097     updateRecord : function(record){
49098         record.beginEdit();
49099         var fs = record.fields;
49100         fs.each(function(f){
49101             var field = this.findField(f.name);
49102             if(field){
49103                 record.set(f.name, field.getValue());
49104             }
49105         }, this);
49106         record.endEdit();
49107         return this;
49108     },
49109
49110     /**
49111      * Loads an Roo.data.Record into this form.
49112      * @param {Record} record The record to load
49113      * @return {BasicForm} this
49114      */
49115     loadRecord : function(record){
49116         this.setValues(record.data);
49117         return this;
49118     },
49119
49120     // private
49121     beforeAction : function(action){
49122         var o = action.options;
49123         
49124         if(!this.disableMask) {
49125             if(this.waitMsgTarget === true){
49126                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
49127             }else if(this.waitMsgTarget){
49128                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
49129                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
49130             }else {
49131                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
49132             }
49133         }
49134         
49135          
49136     },
49137
49138     // private
49139     afterAction : function(action, success){
49140         this.activeAction = null;
49141         var o = action.options;
49142         
49143         if(!this.disableMask) {
49144             if(this.waitMsgTarget === true){
49145                 this.el.unmask();
49146             }else if(this.waitMsgTarget){
49147                 this.waitMsgTarget.unmask();
49148             }else{
49149                 Roo.MessageBox.updateProgress(1);
49150                 Roo.MessageBox.hide();
49151             }
49152         }
49153         
49154         if(success){
49155             if(o.reset){
49156                 this.reset();
49157             }
49158             Roo.callback(o.success, o.scope, [this, action]);
49159             this.fireEvent('actioncomplete', this, action);
49160             
49161         }else{
49162             
49163             // failure condition..
49164             // we have a scenario where updates need confirming.
49165             // eg. if a locking scenario exists..
49166             // we look for { errors : { needs_confirm : true }} in the response.
49167             if (
49168                 (typeof(action.result) != 'undefined')  &&
49169                 (typeof(action.result.errors) != 'undefined')  &&
49170                 (typeof(action.result.errors.needs_confirm) != 'undefined')
49171            ){
49172                 var _t = this;
49173                 Roo.MessageBox.confirm(
49174                     "Change requires confirmation",
49175                     action.result.errorMsg,
49176                     function(r) {
49177                         if (r != 'yes') {
49178                             return;
49179                         }
49180                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
49181                     }
49182                     
49183                 );
49184                 
49185                 
49186                 
49187                 return;
49188             }
49189             
49190             Roo.callback(o.failure, o.scope, [this, action]);
49191             // show an error message if no failed handler is set..
49192             if (!this.hasListener('actionfailed')) {
49193                 Roo.MessageBox.alert("Error",
49194                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
49195                         action.result.errorMsg :
49196                         "Saving Failed, please check your entries or try again"
49197                 );
49198             }
49199             
49200             this.fireEvent('actionfailed', this, action);
49201         }
49202         
49203     },
49204
49205     /**
49206      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
49207      * @param {String} id The value to search for
49208      * @return Field
49209      */
49210     findField : function(id){
49211         var field = this.items.get(id);
49212         if(!field){
49213             this.items.each(function(f){
49214                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
49215                     field = f;
49216                     return false;
49217                 }
49218             });
49219         }
49220         return field || null;
49221     },
49222
49223     /**
49224      * Add a secondary form to this one, 
49225      * Used to provide tabbed forms. One form is primary, with hidden values 
49226      * which mirror the elements from the other forms.
49227      * 
49228      * @param {Roo.form.Form} form to add.
49229      * 
49230      */
49231     addForm : function(form)
49232     {
49233        
49234         if (this.childForms.indexOf(form) > -1) {
49235             // already added..
49236             return;
49237         }
49238         this.childForms.push(form);
49239         var n = '';
49240         Roo.each(form.allItems, function (fe) {
49241             
49242             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
49243             if (this.findField(n)) { // already added..
49244                 return;
49245             }
49246             var add = new Roo.form.Hidden({
49247                 name : n
49248             });
49249             add.render(this.el);
49250             
49251             this.add( add );
49252         }, this);
49253         
49254     },
49255     /**
49256      * Mark fields in this form invalid in bulk.
49257      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
49258      * @return {BasicForm} this
49259      */
49260     markInvalid : function(errors){
49261         if(errors instanceof Array){
49262             for(var i = 0, len = errors.length; i < len; i++){
49263                 var fieldError = errors[i];
49264                 var f = this.findField(fieldError.id);
49265                 if(f){
49266                     f.markInvalid(fieldError.msg);
49267                 }
49268             }
49269         }else{
49270             var field, id;
49271             for(id in errors){
49272                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
49273                     field.markInvalid(errors[id]);
49274                 }
49275             }
49276         }
49277         Roo.each(this.childForms || [], function (f) {
49278             f.markInvalid(errors);
49279         });
49280         
49281         return this;
49282     },
49283
49284     /**
49285      * Set values for fields in this form in bulk.
49286      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
49287      * @return {BasicForm} this
49288      */
49289     setValues : function(values){
49290         if(values instanceof Array){ // array of objects
49291             for(var i = 0, len = values.length; i < len; i++){
49292                 var v = values[i];
49293                 var f = this.findField(v.id);
49294                 if(f){
49295                     f.setValue(v.value);
49296                     if(this.trackResetOnLoad){
49297                         f.originalValue = f.getValue();
49298                     }
49299                 }
49300             }
49301         }else{ // object hash
49302             var field, id;
49303             for(id in values){
49304                 if(typeof values[id] != 'function' && (field = this.findField(id))){
49305                     
49306                     if (field.setFromData && 
49307                         field.valueField && 
49308                         field.displayField &&
49309                         // combos' with local stores can 
49310                         // be queried via setValue()
49311                         // to set their value..
49312                         (field.store && !field.store.isLocal)
49313                         ) {
49314                         // it's a combo
49315                         var sd = { };
49316                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
49317                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
49318                         field.setFromData(sd);
49319                         
49320                     } else {
49321                         field.setValue(values[id]);
49322                     }
49323                     
49324                     
49325                     if(this.trackResetOnLoad){
49326                         field.originalValue = field.getValue();
49327                     }
49328                 }
49329             }
49330         }
49331         this.resetHasChanged();
49332         
49333         
49334         Roo.each(this.childForms || [], function (f) {
49335             f.setValues(values);
49336             f.resetHasChanged();
49337         });
49338                 
49339         return this;
49340     },
49341  
49342     /**
49343      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
49344      * they are returned as an array.
49345      * @param {Boolean} asString
49346      * @return {Object}
49347      */
49348     getValues : function(asString){
49349         if (this.childForms) {
49350             // copy values from the child forms
49351             Roo.each(this.childForms, function (f) {
49352                 this.setValues(f.getValues());
49353             }, this);
49354         }
49355         
49356         // use formdata
49357         if (typeof(FormData) != 'undefined' && asString !== true) {
49358             // this relies on a 'recent' version of chrome apparently...
49359             try {
49360                 var fd = (new FormData(this.el.dom)).entries();
49361                 var ret = {};
49362                 var ent = fd.next();
49363                 while (!ent.done) {
49364                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
49365                     ent = fd.next();
49366                 };
49367                 return ret;
49368             } catch(e) {
49369                 
49370             }
49371             
49372         }
49373         
49374         
49375         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
49376         if(asString === true){
49377             return fs;
49378         }
49379         return Roo.urlDecode(fs);
49380     },
49381     
49382     /**
49383      * Returns the fields in this form as an object with key/value pairs. 
49384      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
49385      * @return {Object}
49386      */
49387     getFieldValues : function(with_hidden)
49388     {
49389         if (this.childForms) {
49390             // copy values from the child forms
49391             // should this call getFieldValues - probably not as we do not currently copy
49392             // hidden fields when we generate..
49393             Roo.each(this.childForms, function (f) {
49394                 this.setValues(f.getValues());
49395             }, this);
49396         }
49397         
49398         var ret = {};
49399         this.items.each(function(f){
49400             if (!f.getName()) {
49401                 return;
49402             }
49403             var v = f.getValue();
49404             if (f.inputType =='radio') {
49405                 if (typeof(ret[f.getName()]) == 'undefined') {
49406                     ret[f.getName()] = ''; // empty..
49407                 }
49408                 
49409                 if (!f.el.dom.checked) {
49410                     return;
49411                     
49412                 }
49413                 v = f.el.dom.value;
49414                 
49415             }
49416             
49417             // not sure if this supported any more..
49418             if ((typeof(v) == 'object') && f.getRawValue) {
49419                 v = f.getRawValue() ; // dates..
49420             }
49421             // combo boxes where name != hiddenName...
49422             if (f.name != f.getName()) {
49423                 ret[f.name] = f.getRawValue();
49424             }
49425             ret[f.getName()] = v;
49426         });
49427         
49428         return ret;
49429     },
49430
49431     /**
49432      * Clears all invalid messages in this form.
49433      * @return {BasicForm} this
49434      */
49435     clearInvalid : function(){
49436         this.items.each(function(f){
49437            f.clearInvalid();
49438         });
49439         
49440         Roo.each(this.childForms || [], function (f) {
49441             f.clearInvalid();
49442         });
49443         
49444         
49445         return this;
49446     },
49447
49448     /**
49449      * Resets this form.
49450      * @return {BasicForm} this
49451      */
49452     reset : function(){
49453         this.items.each(function(f){
49454             f.reset();
49455         });
49456         
49457         Roo.each(this.childForms || [], function (f) {
49458             f.reset();
49459         });
49460         this.resetHasChanged();
49461         
49462         return this;
49463     },
49464
49465     /**
49466      * Add Roo.form components to this form.
49467      * @param {Field} field1
49468      * @param {Field} field2 (optional)
49469      * @param {Field} etc (optional)
49470      * @return {BasicForm} this
49471      */
49472     add : function(){
49473         this.items.addAll(Array.prototype.slice.call(arguments, 0));
49474         return this;
49475     },
49476
49477
49478     /**
49479      * Removes a field from the items collection (does NOT remove its markup).
49480      * @param {Field} field
49481      * @return {BasicForm} this
49482      */
49483     remove : function(field){
49484         this.items.remove(field);
49485         return this;
49486     },
49487
49488     /**
49489      * Looks at the fields in this form, checks them for an id attribute,
49490      * and calls applyTo on the existing dom element with that id.
49491      * @return {BasicForm} this
49492      */
49493     render : function(){
49494         this.items.each(function(f){
49495             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
49496                 f.applyTo(f.id);
49497             }
49498         });
49499         return this;
49500     },
49501
49502     /**
49503      * Calls {@link Ext#apply} for all fields in this form with the passed object.
49504      * @param {Object} values
49505      * @return {BasicForm} this
49506      */
49507     applyToFields : function(o){
49508         this.items.each(function(f){
49509            Roo.apply(f, o);
49510         });
49511         return this;
49512     },
49513
49514     /**
49515      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
49516      * @param {Object} values
49517      * @return {BasicForm} this
49518      */
49519     applyIfToFields : function(o){
49520         this.items.each(function(f){
49521            Roo.applyIf(f, o);
49522         });
49523         return this;
49524     }
49525 });
49526
49527 // back compat
49528 Roo.BasicForm = Roo.form.BasicForm;
49529
49530 Roo.apply(Roo.form.BasicForm, {
49531     
49532     popover : {
49533         
49534         padding : 5,
49535         
49536         isApplied : false,
49537         
49538         isMasked : false,
49539         
49540         form : false,
49541         
49542         target : false,
49543         
49544         intervalID : false,
49545         
49546         maskEl : false,
49547         
49548         apply : function()
49549         {
49550             if(this.isApplied){
49551                 return;
49552             }
49553             
49554             this.maskEl = {
49555                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
49556                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
49557                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
49558                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
49559             };
49560             
49561             this.maskEl.top.enableDisplayMode("block");
49562             this.maskEl.left.enableDisplayMode("block");
49563             this.maskEl.bottom.enableDisplayMode("block");
49564             this.maskEl.right.enableDisplayMode("block");
49565             
49566             Roo.get(document.body).on('click', function(){
49567                 this.unmask();
49568             }, this);
49569             
49570             Roo.get(document.body).on('touchstart', function(){
49571                 this.unmask();
49572             }, this);
49573             
49574             this.isApplied = true
49575         },
49576         
49577         mask : function(form, target)
49578         {
49579             this.form = form;
49580             
49581             this.target = target;
49582             
49583             if(!this.form.errorMask || !target.el){
49584                 return;
49585             }
49586             
49587             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
49588             
49589             var ot = this.target.el.calcOffsetsTo(scrollable);
49590             
49591             var scrollTo = ot[1] - this.form.maskOffset;
49592             
49593             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
49594             
49595             scrollable.scrollTo('top', scrollTo);
49596             
49597             var el = this.target.wrap || this.target.el;
49598             
49599             var box = el.getBox();
49600             
49601             this.maskEl.top.setStyle('position', 'absolute');
49602             this.maskEl.top.setStyle('z-index', 10000);
49603             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
49604             this.maskEl.top.setLeft(0);
49605             this.maskEl.top.setTop(0);
49606             this.maskEl.top.show();
49607             
49608             this.maskEl.left.setStyle('position', 'absolute');
49609             this.maskEl.left.setStyle('z-index', 10000);
49610             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
49611             this.maskEl.left.setLeft(0);
49612             this.maskEl.left.setTop(box.y - this.padding);
49613             this.maskEl.left.show();
49614
49615             this.maskEl.bottom.setStyle('position', 'absolute');
49616             this.maskEl.bottom.setStyle('z-index', 10000);
49617             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
49618             this.maskEl.bottom.setLeft(0);
49619             this.maskEl.bottom.setTop(box.bottom + this.padding);
49620             this.maskEl.bottom.show();
49621
49622             this.maskEl.right.setStyle('position', 'absolute');
49623             this.maskEl.right.setStyle('z-index', 10000);
49624             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
49625             this.maskEl.right.setLeft(box.right + this.padding);
49626             this.maskEl.right.setTop(box.y - this.padding);
49627             this.maskEl.right.show();
49628
49629             this.intervalID = window.setInterval(function() {
49630                 Roo.form.BasicForm.popover.unmask();
49631             }, 10000);
49632
49633             window.onwheel = function(){ return false;};
49634             
49635             (function(){ this.isMasked = true; }).defer(500, this);
49636             
49637         },
49638         
49639         unmask : function()
49640         {
49641             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
49642                 return;
49643             }
49644             
49645             this.maskEl.top.setStyle('position', 'absolute');
49646             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
49647             this.maskEl.top.hide();
49648
49649             this.maskEl.left.setStyle('position', 'absolute');
49650             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
49651             this.maskEl.left.hide();
49652
49653             this.maskEl.bottom.setStyle('position', 'absolute');
49654             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
49655             this.maskEl.bottom.hide();
49656
49657             this.maskEl.right.setStyle('position', 'absolute');
49658             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
49659             this.maskEl.right.hide();
49660             
49661             window.onwheel = function(){ return true;};
49662             
49663             if(this.intervalID){
49664                 window.clearInterval(this.intervalID);
49665                 this.intervalID = false;
49666             }
49667             
49668             this.isMasked = false;
49669             
49670         }
49671         
49672     }
49673     
49674 });/*
49675  * Based on:
49676  * Ext JS Library 1.1.1
49677  * Copyright(c) 2006-2007, Ext JS, LLC.
49678  *
49679  * Originally Released Under LGPL - original licence link has changed is not relivant.
49680  *
49681  * Fork - LGPL
49682  * <script type="text/javascript">
49683  */
49684
49685 /**
49686  * @class Roo.form.Form
49687  * @extends Roo.form.BasicForm
49688  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
49689  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
49690  * @constructor
49691  * @param {Object} config Configuration options
49692  */
49693 Roo.form.Form = function(config){
49694     var xitems =  [];
49695     if (config.items) {
49696         xitems = config.items;
49697         delete config.items;
49698     }
49699    
49700     
49701     Roo.form.Form.superclass.constructor.call(this, null, config);
49702     this.url = this.url || this.action;
49703     if(!this.root){
49704         this.root = new Roo.form.Layout(Roo.applyIf({
49705             id: Roo.id()
49706         }, config));
49707     }
49708     this.active = this.root;
49709     /**
49710      * Array of all the buttons that have been added to this form via {@link addButton}
49711      * @type Array
49712      */
49713     this.buttons = [];
49714     this.allItems = [];
49715     this.addEvents({
49716         /**
49717          * @event clientvalidation
49718          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
49719          * @param {Form} this
49720          * @param {Boolean} valid true if the form has passed client-side validation
49721          */
49722         clientvalidation: true,
49723         /**
49724          * @event rendered
49725          * Fires when the form is rendered
49726          * @param {Roo.form.Form} form
49727          */
49728         rendered : true
49729     });
49730     
49731     if (this.progressUrl) {
49732             // push a hidden field onto the list of fields..
49733             this.addxtype( {
49734                     xns: Roo.form, 
49735                     xtype : 'Hidden', 
49736                     name : 'UPLOAD_IDENTIFIER' 
49737             });
49738         }
49739         
49740     
49741     Roo.each(xitems, this.addxtype, this);
49742     
49743 };
49744
49745 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
49746      /**
49747      * @cfg {Roo.Button} buttons[] buttons at bottom of form
49748      */
49749     
49750     /**
49751      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
49752      */
49753     /**
49754      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
49755      */
49756     /**
49757      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
49758      */
49759     buttonAlign:'center',
49760
49761     /**
49762      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
49763      */
49764     minButtonWidth:75,
49765
49766     /**
49767      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
49768      * This property cascades to child containers if not set.
49769      */
49770     labelAlign:'left',
49771
49772     /**
49773      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
49774      * fires a looping event with that state. This is required to bind buttons to the valid
49775      * state using the config value formBind:true on the button.
49776      */
49777     monitorValid : false,
49778
49779     /**
49780      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
49781      */
49782     monitorPoll : 200,
49783     
49784     /**
49785      * @cfg {String} progressUrl - Url to return progress data 
49786      */
49787     
49788     progressUrl : false,
49789     /**
49790      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
49791      * sending a formdata with extra parameters - eg uploaded elements.
49792      */
49793     
49794     formData : false,
49795     
49796     /**
49797      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
49798      * fields are added and the column is closed. If no fields are passed the column remains open
49799      * until end() is called.
49800      * @param {Object} config The config to pass to the column
49801      * @param {Field} field1 (optional)
49802      * @param {Field} field2 (optional)
49803      * @param {Field} etc (optional)
49804      * @return Column The column container object
49805      */
49806     column : function(c){
49807         var col = new Roo.form.Column(c);
49808         this.start(col);
49809         if(arguments.length > 1){ // duplicate code required because of Opera
49810             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49811             this.end();
49812         }
49813         return col;
49814     },
49815
49816     /**
49817      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
49818      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
49819      * until end() is called.
49820      * @param {Object} config The config to pass to the fieldset
49821      * @param {Field} field1 (optional)
49822      * @param {Field} field2 (optional)
49823      * @param {Field} etc (optional)
49824      * @return FieldSet The fieldset container object
49825      */
49826     fieldset : function(c){
49827         var fs = new Roo.form.FieldSet(c);
49828         this.start(fs);
49829         if(arguments.length > 1){ // duplicate code required because of Opera
49830             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49831             this.end();
49832         }
49833         return fs;
49834     },
49835
49836     /**
49837      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
49838      * fields are added and the container is closed. If no fields are passed the container remains open
49839      * until end() is called.
49840      * @param {Object} config The config to pass to the Layout
49841      * @param {Field} field1 (optional)
49842      * @param {Field} field2 (optional)
49843      * @param {Field} etc (optional)
49844      * @return Layout The container object
49845      */
49846     container : function(c){
49847         var l = new Roo.form.Layout(c);
49848         this.start(l);
49849         if(arguments.length > 1){ // duplicate code required because of Opera
49850             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
49851             this.end();
49852         }
49853         return l;
49854     },
49855
49856     /**
49857      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
49858      * @param {Object} container A Roo.form.Layout or subclass of Layout
49859      * @return {Form} this
49860      */
49861     start : function(c){
49862         // cascade label info
49863         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
49864         this.active.stack.push(c);
49865         c.ownerCt = this.active;
49866         this.active = c;
49867         return this;
49868     },
49869
49870     /**
49871      * Closes the current open container
49872      * @return {Form} this
49873      */
49874     end : function(){
49875         if(this.active == this.root){
49876             return this;
49877         }
49878         this.active = this.active.ownerCt;
49879         return this;
49880     },
49881
49882     /**
49883      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
49884      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
49885      * as the label of the field.
49886      * @param {Field} field1
49887      * @param {Field} field2 (optional)
49888      * @param {Field} etc. (optional)
49889      * @return {Form} this
49890      */
49891     add : function(){
49892         this.active.stack.push.apply(this.active.stack, arguments);
49893         this.allItems.push.apply(this.allItems,arguments);
49894         var r = [];
49895         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
49896             if(a[i].isFormField){
49897                 r.push(a[i]);
49898             }
49899         }
49900         if(r.length > 0){
49901             Roo.form.Form.superclass.add.apply(this, r);
49902         }
49903         return this;
49904     },
49905     
49906
49907     
49908     
49909     
49910      /**
49911      * Find any element that has been added to a form, using it's ID or name
49912      * This can include framesets, columns etc. along with regular fields..
49913      * @param {String} id - id or name to find.
49914      
49915      * @return {Element} e - or false if nothing found.
49916      */
49917     findbyId : function(id)
49918     {
49919         var ret = false;
49920         if (!id) {
49921             return ret;
49922         }
49923         Roo.each(this.allItems, function(f){
49924             if (f.id == id || f.name == id ){
49925                 ret = f;
49926                 return false;
49927             }
49928         });
49929         return ret;
49930     },
49931
49932     
49933     
49934     /**
49935      * Render this form into the passed container. This should only be called once!
49936      * @param {String/HTMLElement/Element} container The element this component should be rendered into
49937      * @return {Form} this
49938      */
49939     render : function(ct)
49940     {
49941         
49942         
49943         
49944         ct = Roo.get(ct);
49945         var o = this.autoCreate || {
49946             tag: 'form',
49947             method : this.method || 'POST',
49948             id : this.id || Roo.id()
49949         };
49950         this.initEl(ct.createChild(o));
49951
49952         this.root.render(this.el);
49953         
49954        
49955              
49956         this.items.each(function(f){
49957             f.render('x-form-el-'+f.id);
49958         });
49959
49960         if(this.buttons.length > 0){
49961             // tables are required to maintain order and for correct IE layout
49962             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
49963                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
49964                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
49965             }}, null, true);
49966             var tr = tb.getElementsByTagName('tr')[0];
49967             for(var i = 0, len = this.buttons.length; i < len; i++) {
49968                 var b = this.buttons[i];
49969                 var td = document.createElement('td');
49970                 td.className = 'x-form-btn-td';
49971                 b.render(tr.appendChild(td));
49972             }
49973         }
49974         if(this.monitorValid){ // initialize after render
49975             this.startMonitoring();
49976         }
49977         this.fireEvent('rendered', this);
49978         return this;
49979     },
49980
49981     /**
49982      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
49983      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
49984      * object or a valid Roo.DomHelper element config
49985      * @param {Function} handler The function called when the button is clicked
49986      * @param {Object} scope (optional) The scope of the handler function
49987      * @return {Roo.Button}
49988      */
49989     addButton : function(config, handler, scope){
49990         var bc = {
49991             handler: handler,
49992             scope: scope,
49993             minWidth: this.minButtonWidth,
49994             hideParent:true
49995         };
49996         if(typeof config == "string"){
49997             bc.text = config;
49998         }else{
49999             Roo.apply(bc, config);
50000         }
50001         var btn = new Roo.Button(null, bc);
50002         this.buttons.push(btn);
50003         return btn;
50004     },
50005
50006      /**
50007      * Adds a series of form elements (using the xtype property as the factory method.
50008      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
50009      * @param {Object} config 
50010      */
50011     
50012     addxtype : function()
50013     {
50014         var ar = Array.prototype.slice.call(arguments, 0);
50015         var ret = false;
50016         for(var i = 0; i < ar.length; i++) {
50017             if (!ar[i]) {
50018                 continue; // skip -- if this happends something invalid got sent, we 
50019                 // should ignore it, as basically that interface element will not show up
50020                 // and that should be pretty obvious!!
50021             }
50022             
50023             if (Roo.form[ar[i].xtype]) {
50024                 ar[i].form = this;
50025                 var fe = Roo.factory(ar[i], Roo.form);
50026                 if (!ret) {
50027                     ret = fe;
50028                 }
50029                 fe.form = this;
50030                 if (fe.store) {
50031                     fe.store.form = this;
50032                 }
50033                 if (fe.isLayout) {  
50034                          
50035                     this.start(fe);
50036                     this.allItems.push(fe);
50037                     if (fe.items && fe.addxtype) {
50038                         fe.addxtype.apply(fe, fe.items);
50039                         delete fe.items;
50040                     }
50041                      this.end();
50042                     continue;
50043                 }
50044                 
50045                 
50046                  
50047                 this.add(fe);
50048               //  console.log('adding ' + ar[i].xtype);
50049             }
50050             if (ar[i].xtype == 'Button') {  
50051                 //console.log('adding button');
50052                 //console.log(ar[i]);
50053                 this.addButton(ar[i]);
50054                 this.allItems.push(fe);
50055                 continue;
50056             }
50057             
50058             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
50059                 alert('end is not supported on xtype any more, use items');
50060             //    this.end();
50061             //    //console.log('adding end');
50062             }
50063             
50064         }
50065         return ret;
50066     },
50067     
50068     /**
50069      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
50070      * option "monitorValid"
50071      */
50072     startMonitoring : function(){
50073         if(!this.bound){
50074             this.bound = true;
50075             Roo.TaskMgr.start({
50076                 run : this.bindHandler,
50077                 interval : this.monitorPoll || 200,
50078                 scope: this
50079             });
50080         }
50081     },
50082
50083     /**
50084      * Stops monitoring of the valid state of this form
50085      */
50086     stopMonitoring : function(){
50087         this.bound = false;
50088     },
50089
50090     // private
50091     bindHandler : function(){
50092         if(!this.bound){
50093             return false; // stops binding
50094         }
50095         var valid = true;
50096         this.items.each(function(f){
50097             if(!f.isValid(true)){
50098                 valid = false;
50099                 return false;
50100             }
50101         });
50102         for(var i = 0, len = this.buttons.length; i < len; i++){
50103             var btn = this.buttons[i];
50104             if(btn.formBind === true && btn.disabled === valid){
50105                 btn.setDisabled(!valid);
50106             }
50107         }
50108         this.fireEvent('clientvalidation', this, valid);
50109     }
50110     
50111     
50112     
50113     
50114     
50115     
50116     
50117     
50118 });
50119
50120
50121 // back compat
50122 Roo.Form = Roo.form.Form;
50123 /*
50124  * Based on:
50125  * Ext JS Library 1.1.1
50126  * Copyright(c) 2006-2007, Ext JS, LLC.
50127  *
50128  * Originally Released Under LGPL - original licence link has changed is not relivant.
50129  *
50130  * Fork - LGPL
50131  * <script type="text/javascript">
50132  */
50133
50134 // as we use this in bootstrap.
50135 Roo.namespace('Roo.form');
50136  /**
50137  * @class Roo.form.Action
50138  * Internal Class used to handle form actions
50139  * @constructor
50140  * @param {Roo.form.BasicForm} el The form element or its id
50141  * @param {Object} config Configuration options
50142  */
50143
50144  
50145  
50146 // define the action interface
50147 Roo.form.Action = function(form, options){
50148     this.form = form;
50149     this.options = options || {};
50150 };
50151 /**
50152  * Client Validation Failed
50153  * @const 
50154  */
50155 Roo.form.Action.CLIENT_INVALID = 'client';
50156 /**
50157  * Server Validation Failed
50158  * @const 
50159  */
50160 Roo.form.Action.SERVER_INVALID = 'server';
50161  /**
50162  * Connect to Server Failed
50163  * @const 
50164  */
50165 Roo.form.Action.CONNECT_FAILURE = 'connect';
50166 /**
50167  * Reading Data from Server Failed
50168  * @const 
50169  */
50170 Roo.form.Action.LOAD_FAILURE = 'load';
50171
50172 Roo.form.Action.prototype = {
50173     type : 'default',
50174     failureType : undefined,
50175     response : undefined,
50176     result : undefined,
50177
50178     // interface method
50179     run : function(options){
50180
50181     },
50182
50183     // interface method
50184     success : function(response){
50185
50186     },
50187
50188     // interface method
50189     handleResponse : function(response){
50190
50191     },
50192
50193     // default connection failure
50194     failure : function(response){
50195         
50196         this.response = response;
50197         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50198         this.form.afterAction(this, false);
50199     },
50200
50201     processResponse : function(response){
50202         this.response = response;
50203         if(!response.responseText){
50204             return true;
50205         }
50206         this.result = this.handleResponse(response);
50207         return this.result;
50208     },
50209
50210     // utility functions used internally
50211     getUrl : function(appendParams){
50212         var url = this.options.url || this.form.url || this.form.el.dom.action;
50213         if(appendParams){
50214             var p = this.getParams();
50215             if(p){
50216                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
50217             }
50218         }
50219         return url;
50220     },
50221
50222     getMethod : function(){
50223         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
50224     },
50225
50226     getParams : function(){
50227         var bp = this.form.baseParams;
50228         var p = this.options.params;
50229         if(p){
50230             if(typeof p == "object"){
50231                 p = Roo.urlEncode(Roo.applyIf(p, bp));
50232             }else if(typeof p == 'string' && bp){
50233                 p += '&' + Roo.urlEncode(bp);
50234             }
50235         }else if(bp){
50236             p = Roo.urlEncode(bp);
50237         }
50238         return p;
50239     },
50240
50241     createCallback : function(){
50242         return {
50243             success: this.success,
50244             failure: this.failure,
50245             scope: this,
50246             timeout: (this.form.timeout*1000),
50247             upload: this.form.fileUpload ? this.success : undefined
50248         };
50249     }
50250 };
50251
50252 Roo.form.Action.Submit = function(form, options){
50253     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
50254 };
50255
50256 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
50257     type : 'submit',
50258
50259     haveProgress : false,
50260     uploadComplete : false,
50261     
50262     // uploadProgress indicator.
50263     uploadProgress : function()
50264     {
50265         if (!this.form.progressUrl) {
50266             return;
50267         }
50268         
50269         if (!this.haveProgress) {
50270             Roo.MessageBox.progress("Uploading", "Uploading");
50271         }
50272         if (this.uploadComplete) {
50273            Roo.MessageBox.hide();
50274            return;
50275         }
50276         
50277         this.haveProgress = true;
50278    
50279         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
50280         
50281         var c = new Roo.data.Connection();
50282         c.request({
50283             url : this.form.progressUrl,
50284             params: {
50285                 id : uid
50286             },
50287             method: 'GET',
50288             success : function(req){
50289                //console.log(data);
50290                 var rdata = false;
50291                 var edata;
50292                 try  {
50293                    rdata = Roo.decode(req.responseText)
50294                 } catch (e) {
50295                     Roo.log("Invalid data from server..");
50296                     Roo.log(edata);
50297                     return;
50298                 }
50299                 if (!rdata || !rdata.success) {
50300                     Roo.log(rdata);
50301                     Roo.MessageBox.alert(Roo.encode(rdata));
50302                     return;
50303                 }
50304                 var data = rdata.data;
50305                 
50306                 if (this.uploadComplete) {
50307                    Roo.MessageBox.hide();
50308                    return;
50309                 }
50310                    
50311                 if (data){
50312                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
50313                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
50314                     );
50315                 }
50316                 this.uploadProgress.defer(2000,this);
50317             },
50318        
50319             failure: function(data) {
50320                 Roo.log('progress url failed ');
50321                 Roo.log(data);
50322             },
50323             scope : this
50324         });
50325            
50326     },
50327     
50328     
50329     run : function()
50330     {
50331         // run get Values on the form, so it syncs any secondary forms.
50332         this.form.getValues();
50333         
50334         var o = this.options;
50335         var method = this.getMethod();
50336         var isPost = method == 'POST';
50337         if(o.clientValidation === false || this.form.isValid()){
50338             
50339             if (this.form.progressUrl) {
50340                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
50341                     (new Date() * 1) + '' + Math.random());
50342                     
50343             } 
50344             
50345             
50346             Roo.Ajax.request(Roo.apply(this.createCallback(), {
50347                 form:this.form.el.dom,
50348                 url:this.getUrl(!isPost),
50349                 method: method,
50350                 params:isPost ? this.getParams() : null,
50351                 isUpload: this.form.fileUpload,
50352                 formData : this.form.formData
50353             }));
50354             
50355             this.uploadProgress();
50356
50357         }else if (o.clientValidation !== false){ // client validation failed
50358             this.failureType = Roo.form.Action.CLIENT_INVALID;
50359             this.form.afterAction(this, false);
50360         }
50361     },
50362
50363     success : function(response)
50364     {
50365         this.uploadComplete= true;
50366         if (this.haveProgress) {
50367             Roo.MessageBox.hide();
50368         }
50369         
50370         
50371         var result = this.processResponse(response);
50372         if(result === true || result.success){
50373             this.form.afterAction(this, true);
50374             return;
50375         }
50376         if(result.errors){
50377             this.form.markInvalid(result.errors);
50378             this.failureType = Roo.form.Action.SERVER_INVALID;
50379         }
50380         this.form.afterAction(this, false);
50381     },
50382     failure : function(response)
50383     {
50384         this.uploadComplete= true;
50385         if (this.haveProgress) {
50386             Roo.MessageBox.hide();
50387         }
50388         
50389         this.response = response;
50390         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50391         this.form.afterAction(this, false);
50392     },
50393     
50394     handleResponse : function(response){
50395         if(this.form.errorReader){
50396             var rs = this.form.errorReader.read(response);
50397             var errors = [];
50398             if(rs.records){
50399                 for(var i = 0, len = rs.records.length; i < len; i++) {
50400                     var r = rs.records[i];
50401                     errors[i] = r.data;
50402                 }
50403             }
50404             if(errors.length < 1){
50405                 errors = null;
50406             }
50407             return {
50408                 success : rs.success,
50409                 errors : errors
50410             };
50411         }
50412         var ret = false;
50413         try {
50414             ret = Roo.decode(response.responseText);
50415         } catch (e) {
50416             ret = {
50417                 success: false,
50418                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
50419                 errors : []
50420             };
50421         }
50422         return ret;
50423         
50424     }
50425 });
50426
50427
50428 Roo.form.Action.Load = function(form, options){
50429     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
50430     this.reader = this.form.reader;
50431 };
50432
50433 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
50434     type : 'load',
50435
50436     run : function(){
50437         
50438         Roo.Ajax.request(Roo.apply(
50439                 this.createCallback(), {
50440                     method:this.getMethod(),
50441                     url:this.getUrl(false),
50442                     params:this.getParams()
50443         }));
50444     },
50445
50446     success : function(response){
50447         
50448         var result = this.processResponse(response);
50449         if(result === true || !result.success || !result.data){
50450             this.failureType = Roo.form.Action.LOAD_FAILURE;
50451             this.form.afterAction(this, false);
50452             return;
50453         }
50454         this.form.clearInvalid();
50455         this.form.setValues(result.data);
50456         this.form.afterAction(this, true);
50457     },
50458
50459     handleResponse : function(response){
50460         if(this.form.reader){
50461             var rs = this.form.reader.read(response);
50462             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
50463             return {
50464                 success : rs.success,
50465                 data : data
50466             };
50467         }
50468         return Roo.decode(response.responseText);
50469     }
50470 });
50471
50472 Roo.form.Action.ACTION_TYPES = {
50473     'load' : Roo.form.Action.Load,
50474     'submit' : Roo.form.Action.Submit
50475 };/*
50476  * Based on:
50477  * Ext JS Library 1.1.1
50478  * Copyright(c) 2006-2007, Ext JS, LLC.
50479  *
50480  * Originally Released Under LGPL - original licence link has changed is not relivant.
50481  *
50482  * Fork - LGPL
50483  * <script type="text/javascript">
50484  */
50485  
50486 /**
50487  * @class Roo.form.Layout
50488  * @extends Roo.Component
50489  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50490  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
50491  * @constructor
50492  * @param {Object} config Configuration options
50493  */
50494 Roo.form.Layout = function(config){
50495     var xitems = [];
50496     if (config.items) {
50497         xitems = config.items;
50498         delete config.items;
50499     }
50500     Roo.form.Layout.superclass.constructor.call(this, config);
50501     this.stack = [];
50502     Roo.each(xitems, this.addxtype, this);
50503      
50504 };
50505
50506 Roo.extend(Roo.form.Layout, Roo.Component, {
50507     /**
50508      * @cfg {String/Object} autoCreate
50509      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
50510      */
50511     /**
50512      * @cfg {String/Object/Function} style
50513      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
50514      * a function which returns such a specification.
50515      */
50516     /**
50517      * @cfg {String} labelAlign
50518      * Valid values are "left," "top" and "right" (defaults to "left")
50519      */
50520     /**
50521      * @cfg {Number} labelWidth
50522      * Fixed width in pixels of all field labels (defaults to undefined)
50523      */
50524     /**
50525      * @cfg {Boolean} clear
50526      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
50527      */
50528     clear : true,
50529     /**
50530      * @cfg {String} labelSeparator
50531      * The separator to use after field labels (defaults to ':')
50532      */
50533     labelSeparator : ':',
50534     /**
50535      * @cfg {Boolean} hideLabels
50536      * True to suppress the display of field labels in this layout (defaults to false)
50537      */
50538     hideLabels : false,
50539
50540     // private
50541     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
50542     
50543     isLayout : true,
50544     
50545     // private
50546     onRender : function(ct, position){
50547         if(this.el){ // from markup
50548             this.el = Roo.get(this.el);
50549         }else {  // generate
50550             var cfg = this.getAutoCreate();
50551             this.el = ct.createChild(cfg, position);
50552         }
50553         if(this.style){
50554             this.el.applyStyles(this.style);
50555         }
50556         if(this.labelAlign){
50557             this.el.addClass('x-form-label-'+this.labelAlign);
50558         }
50559         if(this.hideLabels){
50560             this.labelStyle = "display:none";
50561             this.elementStyle = "padding-left:0;";
50562         }else{
50563             if(typeof this.labelWidth == 'number'){
50564                 this.labelStyle = "width:"+this.labelWidth+"px;";
50565                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
50566             }
50567             if(this.labelAlign == 'top'){
50568                 this.labelStyle = "width:auto;";
50569                 this.elementStyle = "padding-left:0;";
50570             }
50571         }
50572         var stack = this.stack;
50573         var slen = stack.length;
50574         if(slen > 0){
50575             if(!this.fieldTpl){
50576                 var t = new Roo.Template(
50577                     '<div class="x-form-item {5}">',
50578                         '<label for="{0}" style="{2}">{1}{4}</label>',
50579                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50580                         '</div>',
50581                     '</div><div class="x-form-clear-left"></div>'
50582                 );
50583                 t.disableFormats = true;
50584                 t.compile();
50585                 Roo.form.Layout.prototype.fieldTpl = t;
50586             }
50587             for(var i = 0; i < slen; i++) {
50588                 if(stack[i].isFormField){
50589                     this.renderField(stack[i]);
50590                 }else{
50591                     this.renderComponent(stack[i]);
50592                 }
50593             }
50594         }
50595         if(this.clear){
50596             this.el.createChild({cls:'x-form-clear'});
50597         }
50598     },
50599
50600     // private
50601     renderField : function(f){
50602         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
50603                f.id, //0
50604                f.fieldLabel, //1
50605                f.labelStyle||this.labelStyle||'', //2
50606                this.elementStyle||'', //3
50607                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
50608                f.itemCls||this.itemCls||''  //5
50609        ], true).getPrevSibling());
50610     },
50611
50612     // private
50613     renderComponent : function(c){
50614         c.render(c.isLayout ? this.el : this.el.createChild());    
50615     },
50616     /**
50617      * Adds a object form elements (using the xtype property as the factory method.)
50618      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
50619      * @param {Object} config 
50620      */
50621     addxtype : function(o)
50622     {
50623         // create the lement.
50624         o.form = this.form;
50625         var fe = Roo.factory(o, Roo.form);
50626         this.form.allItems.push(fe);
50627         this.stack.push(fe);
50628         
50629         if (fe.isFormField) {
50630             this.form.items.add(fe);
50631         }
50632          
50633         return fe;
50634     }
50635 });
50636
50637 /**
50638  * @class Roo.form.Column
50639  * @extends Roo.form.Layout
50640  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
50641  * @constructor
50642  * @param {Object} config Configuration options
50643  */
50644 Roo.form.Column = function(config){
50645     Roo.form.Column.superclass.constructor.call(this, config);
50646 };
50647
50648 Roo.extend(Roo.form.Column, Roo.form.Layout, {
50649     /**
50650      * @cfg {Number/String} width
50651      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50652      */
50653     /**
50654      * @cfg {String/Object} autoCreate
50655      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
50656      */
50657
50658     // private
50659     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
50660
50661     // private
50662     onRender : function(ct, position){
50663         Roo.form.Column.superclass.onRender.call(this, ct, position);
50664         if(this.width){
50665             this.el.setWidth(this.width);
50666         }
50667     }
50668 });
50669
50670
50671 /**
50672  * @class Roo.form.Row
50673  * @extends Roo.form.Layout
50674  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50675  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
50676  * @constructor
50677  * @param {Object} config Configuration options
50678  */
50679
50680  
50681 Roo.form.Row = function(config){
50682     Roo.form.Row.superclass.constructor.call(this, config);
50683 };
50684  
50685 Roo.extend(Roo.form.Row, Roo.form.Layout, {
50686       /**
50687      * @cfg {Number/String} width
50688      * The fixed width of the column in pixels or CSS value (defaults to "auto")
50689      */
50690     /**
50691      * @cfg {Number/String} height
50692      * The fixed height of the column in pixels or CSS value (defaults to "auto")
50693      */
50694     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
50695     
50696     padWidth : 20,
50697     // private
50698     onRender : function(ct, position){
50699         //console.log('row render');
50700         if(!this.rowTpl){
50701             var t = new Roo.Template(
50702                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
50703                     '<label for="{0}" style="{2}">{1}{4}</label>',
50704                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
50705                     '</div>',
50706                 '</div>'
50707             );
50708             t.disableFormats = true;
50709             t.compile();
50710             Roo.form.Layout.prototype.rowTpl = t;
50711         }
50712         this.fieldTpl = this.rowTpl;
50713         
50714         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
50715         var labelWidth = 100;
50716         
50717         if ((this.labelAlign != 'top')) {
50718             if (typeof this.labelWidth == 'number') {
50719                 labelWidth = this.labelWidth
50720             }
50721             this.padWidth =  20 + labelWidth;
50722             
50723         }
50724         
50725         Roo.form.Column.superclass.onRender.call(this, ct, position);
50726         if(this.width){
50727             this.el.setWidth(this.width);
50728         }
50729         if(this.height){
50730             this.el.setHeight(this.height);
50731         }
50732     },
50733     
50734     // private
50735     renderField : function(f){
50736         f.fieldEl = this.fieldTpl.append(this.el, [
50737                f.id, f.fieldLabel,
50738                f.labelStyle||this.labelStyle||'',
50739                this.elementStyle||'',
50740                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
50741                f.itemCls||this.itemCls||'',
50742                f.width ? f.width + this.padWidth : 160 + this.padWidth
50743        ],true);
50744     }
50745 });
50746  
50747
50748 /**
50749  * @class Roo.form.FieldSet
50750  * @extends Roo.form.Layout
50751  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50752  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
50753  * @constructor
50754  * @param {Object} config Configuration options
50755  */
50756 Roo.form.FieldSet = function(config){
50757     Roo.form.FieldSet.superclass.constructor.call(this, config);
50758 };
50759
50760 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
50761     /**
50762      * @cfg {String} legend
50763      * The text to display as the legend for the FieldSet (defaults to '')
50764      */
50765     /**
50766      * @cfg {String/Object} autoCreate
50767      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
50768      */
50769
50770     // private
50771     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
50772
50773     // private
50774     onRender : function(ct, position){
50775         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
50776         if(this.legend){
50777             this.setLegend(this.legend);
50778         }
50779     },
50780
50781     // private
50782     setLegend : function(text){
50783         if(this.rendered){
50784             this.el.child('legend').update(text);
50785         }
50786     }
50787 });/*
50788  * Based on:
50789  * Ext JS Library 1.1.1
50790  * Copyright(c) 2006-2007, Ext JS, LLC.
50791  *
50792  * Originally Released Under LGPL - original licence link has changed is not relivant.
50793  *
50794  * Fork - LGPL
50795  * <script type="text/javascript">
50796  */
50797 /**
50798  * @class Roo.form.VTypes
50799  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
50800  * @static
50801  */
50802 Roo.form.VTypes = function(){
50803     // closure these in so they are only created once.
50804     var alpha = /^[a-zA-Z_]+$/;
50805     var alphanum = /^[a-zA-Z0-9_]+$/;
50806     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
50807     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
50808
50809     // All these messages and functions are configurable
50810     return {
50811         /**
50812          * The function used to validate email addresses
50813          * @param {String} value The email address
50814          */
50815         'email' : function(v){
50816             return email.test(v);
50817         },
50818         /**
50819          * The error text to display when the email validation function returns false
50820          * @type String
50821          */
50822         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
50823         /**
50824          * The keystroke filter mask to be applied on email input
50825          * @type RegExp
50826          */
50827         'emailMask' : /[a-z0-9_\.\-@]/i,
50828
50829         /**
50830          * The function used to validate URLs
50831          * @param {String} value The URL
50832          */
50833         'url' : function(v){
50834             return url.test(v);
50835         },
50836         /**
50837          * The error text to display when the url validation function returns false
50838          * @type String
50839          */
50840         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
50841         
50842         /**
50843          * The function used to validate alpha values
50844          * @param {String} value The value
50845          */
50846         'alpha' : function(v){
50847             return alpha.test(v);
50848         },
50849         /**
50850          * The error text to display when the alpha validation function returns false
50851          * @type String
50852          */
50853         'alphaText' : 'This field should only contain letters and _',
50854         /**
50855          * The keystroke filter mask to be applied on alpha input
50856          * @type RegExp
50857          */
50858         'alphaMask' : /[a-z_]/i,
50859
50860         /**
50861          * The function used to validate alphanumeric values
50862          * @param {String} value The value
50863          */
50864         'alphanum' : function(v){
50865             return alphanum.test(v);
50866         },
50867         /**
50868          * The error text to display when the alphanumeric validation function returns false
50869          * @type String
50870          */
50871         'alphanumText' : 'This field should only contain letters, numbers and _',
50872         /**
50873          * The keystroke filter mask to be applied on alphanumeric input
50874          * @type RegExp
50875          */
50876         'alphanumMask' : /[a-z0-9_]/i
50877     };
50878 }();//<script type="text/javascript">
50879
50880 /**
50881  * @class Roo.form.FCKeditor
50882  * @extends Roo.form.TextArea
50883  * Wrapper around the FCKEditor http://www.fckeditor.net
50884  * @constructor
50885  * Creates a new FCKeditor
50886  * @param {Object} config Configuration options
50887  */
50888 Roo.form.FCKeditor = function(config){
50889     Roo.form.FCKeditor.superclass.constructor.call(this, config);
50890     this.addEvents({
50891          /**
50892          * @event editorinit
50893          * Fired when the editor is initialized - you can add extra handlers here..
50894          * @param {FCKeditor} this
50895          * @param {Object} the FCK object.
50896          */
50897         editorinit : true
50898     });
50899     
50900     
50901 };
50902 Roo.form.FCKeditor.editors = { };
50903 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
50904 {
50905     //defaultAutoCreate : {
50906     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
50907     //},
50908     // private
50909     /**
50910      * @cfg {Object} fck options - see fck manual for details.
50911      */
50912     fckconfig : false,
50913     
50914     /**
50915      * @cfg {Object} fck toolbar set (Basic or Default)
50916      */
50917     toolbarSet : 'Basic',
50918     /**
50919      * @cfg {Object} fck BasePath
50920      */ 
50921     basePath : '/fckeditor/',
50922     
50923     
50924     frame : false,
50925     
50926     value : '',
50927     
50928    
50929     onRender : function(ct, position)
50930     {
50931         if(!this.el){
50932             this.defaultAutoCreate = {
50933                 tag: "textarea",
50934                 style:"width:300px;height:60px;",
50935                 autocomplete: "new-password"
50936             };
50937         }
50938         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
50939         /*
50940         if(this.grow){
50941             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
50942             if(this.preventScrollbars){
50943                 this.el.setStyle("overflow", "hidden");
50944             }
50945             this.el.setHeight(this.growMin);
50946         }
50947         */
50948         //console.log('onrender' + this.getId() );
50949         Roo.form.FCKeditor.editors[this.getId()] = this;
50950          
50951
50952         this.replaceTextarea() ;
50953         
50954     },
50955     
50956     getEditor : function() {
50957         return this.fckEditor;
50958     },
50959     /**
50960      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
50961      * @param {Mixed} value The value to set
50962      */
50963     
50964     
50965     setValue : function(value)
50966     {
50967         //console.log('setValue: ' + value);
50968         
50969         if(typeof(value) == 'undefined') { // not sure why this is happending...
50970             return;
50971         }
50972         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
50973         
50974         //if(!this.el || !this.getEditor()) {
50975         //    this.value = value;
50976             //this.setValue.defer(100,this,[value]);    
50977         //    return;
50978         //} 
50979         
50980         if(!this.getEditor()) {
50981             return;
50982         }
50983         
50984         this.getEditor().SetData(value);
50985         
50986         //
50987
50988     },
50989
50990     /**
50991      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
50992      * @return {Mixed} value The field value
50993      */
50994     getValue : function()
50995     {
50996         
50997         if (this.frame && this.frame.dom.style.display == 'none') {
50998             return Roo.form.FCKeditor.superclass.getValue.call(this);
50999         }
51000         
51001         if(!this.el || !this.getEditor()) {
51002            
51003            // this.getValue.defer(100,this); 
51004             return this.value;
51005         }
51006        
51007         
51008         var value=this.getEditor().GetData();
51009         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
51010         return Roo.form.FCKeditor.superclass.getValue.call(this);
51011         
51012
51013     },
51014
51015     /**
51016      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
51017      * @return {Mixed} value The field value
51018      */
51019     getRawValue : function()
51020     {
51021         if (this.frame && this.frame.dom.style.display == 'none') {
51022             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
51023         }
51024         
51025         if(!this.el || !this.getEditor()) {
51026             //this.getRawValue.defer(100,this); 
51027             return this.value;
51028             return;
51029         }
51030         
51031         
51032         
51033         var value=this.getEditor().GetData();
51034         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
51035         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
51036          
51037     },
51038     
51039     setSize : function(w,h) {
51040         
51041         
51042         
51043         //if (this.frame && this.frame.dom.style.display == 'none') {
51044         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
51045         //    return;
51046         //}
51047         //if(!this.el || !this.getEditor()) {
51048         //    this.setSize.defer(100,this, [w,h]); 
51049         //    return;
51050         //}
51051         
51052         
51053         
51054         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
51055         
51056         this.frame.dom.setAttribute('width', w);
51057         this.frame.dom.setAttribute('height', h);
51058         this.frame.setSize(w,h);
51059         
51060     },
51061     
51062     toggleSourceEdit : function(value) {
51063         
51064       
51065          
51066         this.el.dom.style.display = value ? '' : 'none';
51067         this.frame.dom.style.display = value ?  'none' : '';
51068         
51069     },
51070     
51071     
51072     focus: function(tag)
51073     {
51074         if (this.frame.dom.style.display == 'none') {
51075             return Roo.form.FCKeditor.superclass.focus.call(this);
51076         }
51077         if(!this.el || !this.getEditor()) {
51078             this.focus.defer(100,this, [tag]); 
51079             return;
51080         }
51081         
51082         
51083         
51084         
51085         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
51086         this.getEditor().Focus();
51087         if (tgs.length) {
51088             if (!this.getEditor().Selection.GetSelection()) {
51089                 this.focus.defer(100,this, [tag]); 
51090                 return;
51091             }
51092             
51093             
51094             var r = this.getEditor().EditorDocument.createRange();
51095             r.setStart(tgs[0],0);
51096             r.setEnd(tgs[0],0);
51097             this.getEditor().Selection.GetSelection().removeAllRanges();
51098             this.getEditor().Selection.GetSelection().addRange(r);
51099             this.getEditor().Focus();
51100         }
51101         
51102     },
51103     
51104     
51105     
51106     replaceTextarea : function()
51107     {
51108         if ( document.getElementById( this.getId() + '___Frame' ) ) {
51109             return ;
51110         }
51111         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
51112         //{
51113             // We must check the elements firstly using the Id and then the name.
51114         var oTextarea = document.getElementById( this.getId() );
51115         
51116         var colElementsByName = document.getElementsByName( this.getId() ) ;
51117          
51118         oTextarea.style.display = 'none' ;
51119
51120         if ( oTextarea.tabIndex ) {            
51121             this.TabIndex = oTextarea.tabIndex ;
51122         }
51123         
51124         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
51125         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
51126         this.frame = Roo.get(this.getId() + '___Frame')
51127     },
51128     
51129     _getConfigHtml : function()
51130     {
51131         var sConfig = '' ;
51132
51133         for ( var o in this.fckconfig ) {
51134             sConfig += sConfig.length > 0  ? '&amp;' : '';
51135             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
51136         }
51137
51138         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
51139     },
51140     
51141     
51142     _getIFrameHtml : function()
51143     {
51144         var sFile = 'fckeditor.html' ;
51145         /* no idea what this is about..
51146         try
51147         {
51148             if ( (/fcksource=true/i).test( window.top.location.search ) )
51149                 sFile = 'fckeditor.original.html' ;
51150         }
51151         catch (e) { 
51152         */
51153
51154         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
51155         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
51156         
51157         
51158         var html = '<iframe id="' + this.getId() +
51159             '___Frame" src="' + sLink +
51160             '" width="' + this.width +
51161             '" height="' + this.height + '"' +
51162             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
51163             ' frameborder="0" scrolling="no"></iframe>' ;
51164
51165         return html ;
51166     },
51167     
51168     _insertHtmlBefore : function( html, element )
51169     {
51170         if ( element.insertAdjacentHTML )       {
51171             // IE
51172             element.insertAdjacentHTML( 'beforeBegin', html ) ;
51173         } else { // Gecko
51174             var oRange = document.createRange() ;
51175             oRange.setStartBefore( element ) ;
51176             var oFragment = oRange.createContextualFragment( html );
51177             element.parentNode.insertBefore( oFragment, element ) ;
51178         }
51179     }
51180     
51181     
51182   
51183     
51184     
51185     
51186     
51187
51188 });
51189
51190 //Roo.reg('fckeditor', Roo.form.FCKeditor);
51191
51192 function FCKeditor_OnComplete(editorInstance){
51193     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
51194     f.fckEditor = editorInstance;
51195     //console.log("loaded");
51196     f.fireEvent('editorinit', f, editorInstance);
51197
51198   
51199
51200  
51201
51202
51203
51204
51205
51206
51207
51208
51209
51210
51211
51212
51213
51214
51215
51216 //<script type="text/javascript">
51217 /**
51218  * @class Roo.form.GridField
51219  * @extends Roo.form.Field
51220  * Embed a grid (or editable grid into a form)
51221  * STATUS ALPHA
51222  * 
51223  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
51224  * it needs 
51225  * xgrid.store = Roo.data.Store
51226  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
51227  * xgrid.store.reader = Roo.data.JsonReader 
51228  * 
51229  * 
51230  * @constructor
51231  * Creates a new GridField
51232  * @param {Object} config Configuration options
51233  */
51234 Roo.form.GridField = function(config){
51235     Roo.form.GridField.superclass.constructor.call(this, config);
51236      
51237 };
51238
51239 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
51240     /**
51241      * @cfg {Number} width  - used to restrict width of grid..
51242      */
51243     width : 100,
51244     /**
51245      * @cfg {Number} height - used to restrict height of grid..
51246      */
51247     height : 50,
51248      /**
51249      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
51250          * 
51251          *}
51252      */
51253     xgrid : false, 
51254     /**
51255      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51256      * {tag: "input", type: "checkbox", autocomplete: "off"})
51257      */
51258    // defaultAutoCreate : { tag: 'div' },
51259     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
51260     /**
51261      * @cfg {String} addTitle Text to include for adding a title.
51262      */
51263     addTitle : false,
51264     //
51265     onResize : function(){
51266         Roo.form.Field.superclass.onResize.apply(this, arguments);
51267     },
51268
51269     initEvents : function(){
51270         // Roo.form.Checkbox.superclass.initEvents.call(this);
51271         // has no events...
51272        
51273     },
51274
51275
51276     getResizeEl : function(){
51277         return this.wrap;
51278     },
51279
51280     getPositionEl : function(){
51281         return this.wrap;
51282     },
51283
51284     // private
51285     onRender : function(ct, position){
51286         
51287         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
51288         var style = this.style;
51289         delete this.style;
51290         
51291         Roo.form.GridField.superclass.onRender.call(this, ct, position);
51292         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
51293         this.viewEl = this.wrap.createChild({ tag: 'div' });
51294         if (style) {
51295             this.viewEl.applyStyles(style);
51296         }
51297         if (this.width) {
51298             this.viewEl.setWidth(this.width);
51299         }
51300         if (this.height) {
51301             this.viewEl.setHeight(this.height);
51302         }
51303         //if(this.inputValue !== undefined){
51304         //this.setValue(this.value);
51305         
51306         
51307         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
51308         
51309         
51310         this.grid.render();
51311         this.grid.getDataSource().on('remove', this.refreshValue, this);
51312         this.grid.getDataSource().on('update', this.refreshValue, this);
51313         this.grid.on('afteredit', this.refreshValue, this);
51314  
51315     },
51316      
51317     
51318     /**
51319      * Sets the value of the item. 
51320      * @param {String} either an object  or a string..
51321      */
51322     setValue : function(v){
51323         //this.value = v;
51324         v = v || []; // empty set..
51325         // this does not seem smart - it really only affects memoryproxy grids..
51326         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
51327             var ds = this.grid.getDataSource();
51328             // assumes a json reader..
51329             var data = {}
51330             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
51331             ds.loadData( data);
51332         }
51333         // clear selection so it does not get stale.
51334         if (this.grid.sm) { 
51335             this.grid.sm.clearSelections();
51336         }
51337         
51338         Roo.form.GridField.superclass.setValue.call(this, v);
51339         this.refreshValue();
51340         // should load data in the grid really....
51341     },
51342     
51343     // private
51344     refreshValue: function() {
51345          var val = [];
51346         this.grid.getDataSource().each(function(r) {
51347             val.push(r.data);
51348         });
51349         this.el.dom.value = Roo.encode(val);
51350     }
51351     
51352      
51353     
51354     
51355 });/*
51356  * Based on:
51357  * Ext JS Library 1.1.1
51358  * Copyright(c) 2006-2007, Ext JS, LLC.
51359  *
51360  * Originally Released Under LGPL - original licence link has changed is not relivant.
51361  *
51362  * Fork - LGPL
51363  * <script type="text/javascript">
51364  */
51365 /**
51366  * @class Roo.form.DisplayField
51367  * @extends Roo.form.Field
51368  * A generic Field to display non-editable data.
51369  * @cfg {Boolean} closable (true|false) default false
51370  * @constructor
51371  * Creates a new Display Field item.
51372  * @param {Object} config Configuration options
51373  */
51374 Roo.form.DisplayField = function(config){
51375     Roo.form.DisplayField.superclass.constructor.call(this, config);
51376     
51377     this.addEvents({
51378         /**
51379          * @event close
51380          * Fires after the click the close btn
51381              * @param {Roo.form.DisplayField} this
51382              */
51383         close : true
51384     });
51385 };
51386
51387 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
51388     inputType:      'hidden',
51389     allowBlank:     true,
51390     readOnly:         true,
51391     
51392  
51393     /**
51394      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51395      */
51396     focusClass : undefined,
51397     /**
51398      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51399      */
51400     fieldClass: 'x-form-field',
51401     
51402      /**
51403      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
51404      */
51405     valueRenderer: undefined,
51406     
51407     width: 100,
51408     /**
51409      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51410      * {tag: "input", type: "checkbox", autocomplete: "off"})
51411      */
51412      
51413  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
51414  
51415     closable : false,
51416     
51417     onResize : function(){
51418         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
51419         
51420     },
51421
51422     initEvents : function(){
51423         // Roo.form.Checkbox.superclass.initEvents.call(this);
51424         // has no events...
51425         
51426         if(this.closable){
51427             this.closeEl.on('click', this.onClose, this);
51428         }
51429        
51430     },
51431
51432
51433     getResizeEl : function(){
51434         return this.wrap;
51435     },
51436
51437     getPositionEl : function(){
51438         return this.wrap;
51439     },
51440
51441     // private
51442     onRender : function(ct, position){
51443         
51444         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
51445         //if(this.inputValue !== undefined){
51446         this.wrap = this.el.wrap();
51447         
51448         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
51449         
51450         if(this.closable){
51451             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
51452         }
51453         
51454         if (this.bodyStyle) {
51455             this.viewEl.applyStyles(this.bodyStyle);
51456         }
51457         //this.viewEl.setStyle('padding', '2px');
51458         
51459         this.setValue(this.value);
51460         
51461     },
51462 /*
51463     // private
51464     initValue : Roo.emptyFn,
51465
51466   */
51467
51468         // private
51469     onClick : function(){
51470         
51471     },
51472
51473     /**
51474      * Sets the checked state of the checkbox.
51475      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
51476      */
51477     setValue : function(v){
51478         this.value = v;
51479         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
51480         // this might be called before we have a dom element..
51481         if (!this.viewEl) {
51482             return;
51483         }
51484         this.viewEl.dom.innerHTML = html;
51485         Roo.form.DisplayField.superclass.setValue.call(this, v);
51486
51487     },
51488     
51489     onClose : function(e)
51490     {
51491         e.preventDefault();
51492         
51493         this.fireEvent('close', this);
51494     }
51495 });/*
51496  * 
51497  * Licence- LGPL
51498  * 
51499  */
51500
51501 /**
51502  * @class Roo.form.DayPicker
51503  * @extends Roo.form.Field
51504  * A Day picker show [M] [T] [W] ....
51505  * @constructor
51506  * Creates a new Day Picker
51507  * @param {Object} config Configuration options
51508  */
51509 Roo.form.DayPicker= function(config){
51510     Roo.form.DayPicker.superclass.constructor.call(this, config);
51511      
51512 };
51513
51514 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
51515     /**
51516      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51517      */
51518     focusClass : undefined,
51519     /**
51520      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51521      */
51522     fieldClass: "x-form-field",
51523    
51524     /**
51525      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51526      * {tag: "input", type: "checkbox", autocomplete: "off"})
51527      */
51528     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
51529     
51530    
51531     actionMode : 'viewEl', 
51532     //
51533     // private
51534  
51535     inputType : 'hidden',
51536     
51537      
51538     inputElement: false, // real input element?
51539     basedOn: false, // ????
51540     
51541     isFormField: true, // not sure where this is needed!!!!
51542
51543     onResize : function(){
51544         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
51545         if(!this.boxLabel){
51546             this.el.alignTo(this.wrap, 'c-c');
51547         }
51548     },
51549
51550     initEvents : function(){
51551         Roo.form.Checkbox.superclass.initEvents.call(this);
51552         this.el.on("click", this.onClick,  this);
51553         this.el.on("change", this.onClick,  this);
51554     },
51555
51556
51557     getResizeEl : function(){
51558         return this.wrap;
51559     },
51560
51561     getPositionEl : function(){
51562         return this.wrap;
51563     },
51564
51565     
51566     // private
51567     onRender : function(ct, position){
51568         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
51569        
51570         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
51571         
51572         var r1 = '<table><tr>';
51573         var r2 = '<tr class="x-form-daypick-icons">';
51574         for (var i=0; i < 7; i++) {
51575             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
51576             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
51577         }
51578         
51579         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
51580         viewEl.select('img').on('click', this.onClick, this);
51581         this.viewEl = viewEl;   
51582         
51583         
51584         // this will not work on Chrome!!!
51585         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
51586         this.el.on('propertychange', this.setFromHidden,  this);  //ie
51587         
51588         
51589           
51590
51591     },
51592
51593     // private
51594     initValue : Roo.emptyFn,
51595
51596     /**
51597      * Returns the checked state of the checkbox.
51598      * @return {Boolean} True if checked, else false
51599      */
51600     getValue : function(){
51601         return this.el.dom.value;
51602         
51603     },
51604
51605         // private
51606     onClick : function(e){ 
51607         //this.setChecked(!this.checked);
51608         Roo.get(e.target).toggleClass('x-menu-item-checked');
51609         this.refreshValue();
51610         //if(this.el.dom.checked != this.checked){
51611         //    this.setValue(this.el.dom.checked);
51612        // }
51613     },
51614     
51615     // private
51616     refreshValue : function()
51617     {
51618         var val = '';
51619         this.viewEl.select('img',true).each(function(e,i,n)  {
51620             val += e.is(".x-menu-item-checked") ? String(n) : '';
51621         });
51622         this.setValue(val, true);
51623     },
51624
51625     /**
51626      * Sets the checked state of the checkbox.
51627      * On is always based on a string comparison between inputValue and the param.
51628      * @param {Boolean/String} value - the value to set 
51629      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
51630      */
51631     setValue : function(v,suppressEvent){
51632         if (!this.el.dom) {
51633             return;
51634         }
51635         var old = this.el.dom.value ;
51636         this.el.dom.value = v;
51637         if (suppressEvent) {
51638             return ;
51639         }
51640          
51641         // update display..
51642         this.viewEl.select('img',true).each(function(e,i,n)  {
51643             
51644             var on = e.is(".x-menu-item-checked");
51645             var newv = v.indexOf(String(n)) > -1;
51646             if (on != newv) {
51647                 e.toggleClass('x-menu-item-checked');
51648             }
51649             
51650         });
51651         
51652         
51653         this.fireEvent('change', this, v, old);
51654         
51655         
51656     },
51657    
51658     // handle setting of hidden value by some other method!!?!?
51659     setFromHidden: function()
51660     {
51661         if(!this.el){
51662             return;
51663         }
51664         //console.log("SET FROM HIDDEN");
51665         //alert('setFrom hidden');
51666         this.setValue(this.el.dom.value);
51667     },
51668     
51669     onDestroy : function()
51670     {
51671         if(this.viewEl){
51672             Roo.get(this.viewEl).remove();
51673         }
51674          
51675         Roo.form.DayPicker.superclass.onDestroy.call(this);
51676     }
51677
51678 });/*
51679  * RooJS Library 1.1.1
51680  * Copyright(c) 2008-2011  Alan Knowles
51681  *
51682  * License - LGPL
51683  */
51684  
51685
51686 /**
51687  * @class Roo.form.ComboCheck
51688  * @extends Roo.form.ComboBox
51689  * A combobox for multiple select items.
51690  *
51691  * FIXME - could do with a reset button..
51692  * 
51693  * @constructor
51694  * Create a new ComboCheck
51695  * @param {Object} config Configuration options
51696  */
51697 Roo.form.ComboCheck = function(config){
51698     Roo.form.ComboCheck.superclass.constructor.call(this, config);
51699     // should verify some data...
51700     // like
51701     // hiddenName = required..
51702     // displayField = required
51703     // valudField == required
51704     var req= [ 'hiddenName', 'displayField', 'valueField' ];
51705     var _t = this;
51706     Roo.each(req, function(e) {
51707         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
51708             throw "Roo.form.ComboCheck : missing value for: " + e;
51709         }
51710     });
51711     
51712     
51713 };
51714
51715 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
51716      
51717      
51718     editable : false,
51719      
51720     selectedClass: 'x-menu-item-checked', 
51721     
51722     // private
51723     onRender : function(ct, position){
51724         var _t = this;
51725         
51726         
51727         
51728         if(!this.tpl){
51729             var cls = 'x-combo-list';
51730
51731             
51732             this.tpl =  new Roo.Template({
51733                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
51734                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
51735                    '<span>{' + this.displayField + '}</span>' +
51736                     '</div>' 
51737                 
51738             });
51739         }
51740  
51741         
51742         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
51743         this.view.singleSelect = false;
51744         this.view.multiSelect = true;
51745         this.view.toggleSelect = true;
51746         this.pageTb.add(new Roo.Toolbar.Fill(), {
51747             
51748             text: 'Done',
51749             handler: function()
51750             {
51751                 _t.collapse();
51752             }
51753         });
51754     },
51755     
51756     onViewOver : function(e, t){
51757         // do nothing...
51758         return;
51759         
51760     },
51761     
51762     onViewClick : function(doFocus,index){
51763         return;
51764         
51765     },
51766     select: function () {
51767         //Roo.log("SELECT CALLED");
51768     },
51769      
51770     selectByValue : function(xv, scrollIntoView){
51771         var ar = this.getValueArray();
51772         var sels = [];
51773         
51774         Roo.each(ar, function(v) {
51775             if(v === undefined || v === null){
51776                 return;
51777             }
51778             var r = this.findRecord(this.valueField, v);
51779             if(r){
51780                 sels.push(this.store.indexOf(r))
51781                 
51782             }
51783         },this);
51784         this.view.select(sels);
51785         return false;
51786     },
51787     
51788     
51789     
51790     onSelect : function(record, index){
51791        // Roo.log("onselect Called");
51792        // this is only called by the clear button now..
51793         this.view.clearSelections();
51794         this.setValue('[]');
51795         if (this.value != this.valueBefore) {
51796             this.fireEvent('change', this, this.value, this.valueBefore);
51797             this.valueBefore = this.value;
51798         }
51799     },
51800     getValueArray : function()
51801     {
51802         var ar = [] ;
51803         
51804         try {
51805             //Roo.log(this.value);
51806             if (typeof(this.value) == 'undefined') {
51807                 return [];
51808             }
51809             var ar = Roo.decode(this.value);
51810             return  ar instanceof Array ? ar : []; //?? valid?
51811             
51812         } catch(e) {
51813             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
51814             return [];
51815         }
51816          
51817     },
51818     expand : function ()
51819     {
51820         
51821         Roo.form.ComboCheck.superclass.expand.call(this);
51822         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
51823         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
51824         
51825
51826     },
51827     
51828     collapse : function(){
51829         Roo.form.ComboCheck.superclass.collapse.call(this);
51830         var sl = this.view.getSelectedIndexes();
51831         var st = this.store;
51832         var nv = [];
51833         var tv = [];
51834         var r;
51835         Roo.each(sl, function(i) {
51836             r = st.getAt(i);
51837             nv.push(r.get(this.valueField));
51838         },this);
51839         this.setValue(Roo.encode(nv));
51840         if (this.value != this.valueBefore) {
51841
51842             this.fireEvent('change', this, this.value, this.valueBefore);
51843             this.valueBefore = this.value;
51844         }
51845         
51846     },
51847     
51848     setValue : function(v){
51849         // Roo.log(v);
51850         this.value = v;
51851         
51852         var vals = this.getValueArray();
51853         var tv = [];
51854         Roo.each(vals, function(k) {
51855             var r = this.findRecord(this.valueField, k);
51856             if(r){
51857                 tv.push(r.data[this.displayField]);
51858             }else if(this.valueNotFoundText !== undefined){
51859                 tv.push( this.valueNotFoundText );
51860             }
51861         },this);
51862        // Roo.log(tv);
51863         
51864         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
51865         this.hiddenField.value = v;
51866         this.value = v;
51867     }
51868     
51869 });/*
51870  * Based on:
51871  * Ext JS Library 1.1.1
51872  * Copyright(c) 2006-2007, Ext JS, LLC.
51873  *
51874  * Originally Released Under LGPL - original licence link has changed is not relivant.
51875  *
51876  * Fork - LGPL
51877  * <script type="text/javascript">
51878  */
51879  
51880 /**
51881  * @class Roo.form.Signature
51882  * @extends Roo.form.Field
51883  * Signature field.  
51884  * @constructor
51885  * 
51886  * @param {Object} config Configuration options
51887  */
51888
51889 Roo.form.Signature = function(config){
51890     Roo.form.Signature.superclass.constructor.call(this, config);
51891     
51892     this.addEvents({// not in used??
51893          /**
51894          * @event confirm
51895          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
51896              * @param {Roo.form.Signature} combo This combo box
51897              */
51898         'confirm' : true,
51899         /**
51900          * @event reset
51901          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
51902              * @param {Roo.form.ComboBox} combo This combo box
51903              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
51904              */
51905         'reset' : true
51906     });
51907 };
51908
51909 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
51910     /**
51911      * @cfg {Object} labels Label to use when rendering a form.
51912      * defaults to 
51913      * labels : { 
51914      *      clear : "Clear",
51915      *      confirm : "Confirm"
51916      *  }
51917      */
51918     labels : { 
51919         clear : "Clear",
51920         confirm : "Confirm"
51921     },
51922     /**
51923      * @cfg {Number} width The signature panel width (defaults to 300)
51924      */
51925     width: 300,
51926     /**
51927      * @cfg {Number} height The signature panel height (defaults to 100)
51928      */
51929     height : 100,
51930     /**
51931      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
51932      */
51933     allowBlank : false,
51934     
51935     //private
51936     // {Object} signPanel The signature SVG panel element (defaults to {})
51937     signPanel : {},
51938     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
51939     isMouseDown : false,
51940     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
51941     isConfirmed : false,
51942     // {String} signatureTmp SVG mapping string (defaults to empty string)
51943     signatureTmp : '',
51944     
51945     
51946     defaultAutoCreate : { // modified by initCompnoent..
51947         tag: "input",
51948         type:"hidden"
51949     },
51950
51951     // private
51952     onRender : function(ct, position){
51953         
51954         Roo.form.Signature.superclass.onRender.call(this, ct, position);
51955         
51956         this.wrap = this.el.wrap({
51957             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
51958         });
51959         
51960         this.createToolbar(this);
51961         this.signPanel = this.wrap.createChild({
51962                 tag: 'div',
51963                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
51964             }, this.el
51965         );
51966             
51967         this.svgID = Roo.id();
51968         this.svgEl = this.signPanel.createChild({
51969               xmlns : 'http://www.w3.org/2000/svg',
51970               tag : 'svg',
51971               id : this.svgID + "-svg",
51972               width: this.width,
51973               height: this.height,
51974               viewBox: '0 0 '+this.width+' '+this.height,
51975               cn : [
51976                 {
51977                     tag: "rect",
51978                     id: this.svgID + "-svg-r",
51979                     width: this.width,
51980                     height: this.height,
51981                     fill: "#ffa"
51982                 },
51983                 {
51984                     tag: "line",
51985                     id: this.svgID + "-svg-l",
51986                     x1: "0", // start
51987                     y1: (this.height*0.8), // start set the line in 80% of height
51988                     x2: this.width, // end
51989                     y2: (this.height*0.8), // end set the line in 80% of height
51990                     'stroke': "#666",
51991                     'stroke-width': "1",
51992                     'stroke-dasharray': "3",
51993                     'shape-rendering': "crispEdges",
51994                     'pointer-events': "none"
51995                 },
51996                 {
51997                     tag: "path",
51998                     id: this.svgID + "-svg-p",
51999                     'stroke': "navy",
52000                     'stroke-width': "3",
52001                     'fill': "none",
52002                     'pointer-events': 'none'
52003                 }
52004               ]
52005         });
52006         this.createSVG();
52007         this.svgBox = this.svgEl.dom.getScreenCTM();
52008     },
52009     createSVG : function(){ 
52010         var svg = this.signPanel;
52011         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
52012         var t = this;
52013
52014         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
52015         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
52016         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
52017         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
52018         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
52019         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
52020         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
52021         
52022     },
52023     isTouchEvent : function(e){
52024         return e.type.match(/^touch/);
52025     },
52026     getCoords : function (e) {
52027         var pt    = this.svgEl.dom.createSVGPoint();
52028         pt.x = e.clientX; 
52029         pt.y = e.clientY;
52030         if (this.isTouchEvent(e)) {
52031             pt.x =  e.targetTouches[0].clientX;
52032             pt.y = e.targetTouches[0].clientY;
52033         }
52034         var a = this.svgEl.dom.getScreenCTM();
52035         var b = a.inverse();
52036         var mx = pt.matrixTransform(b);
52037         return mx.x + ',' + mx.y;
52038     },
52039     //mouse event headler 
52040     down : function (e) {
52041         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
52042         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
52043         
52044         this.isMouseDown = true;
52045         
52046         e.preventDefault();
52047     },
52048     move : function (e) {
52049         if (this.isMouseDown) {
52050             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
52051             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
52052         }
52053         
52054         e.preventDefault();
52055     },
52056     up : function (e) {
52057         this.isMouseDown = false;
52058         var sp = this.signatureTmp.split(' ');
52059         
52060         if(sp.length > 1){
52061             if(!sp[sp.length-2].match(/^L/)){
52062                 sp.pop();
52063                 sp.pop();
52064                 sp.push("");
52065                 this.signatureTmp = sp.join(" ");
52066             }
52067         }
52068         if(this.getValue() != this.signatureTmp){
52069             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52070             this.isConfirmed = false;
52071         }
52072         e.preventDefault();
52073     },
52074     
52075     /**
52076      * Protected method that will not generally be called directly. It
52077      * is called when the editor creates its toolbar. Override this method if you need to
52078      * add custom toolbar buttons.
52079      * @param {HtmlEditor} editor
52080      */
52081     createToolbar : function(editor){
52082          function btn(id, toggle, handler){
52083             var xid = fid + '-'+ id ;
52084             return {
52085                 id : xid,
52086                 cmd : id,
52087                 cls : 'x-btn-icon x-edit-'+id,
52088                 enableToggle:toggle !== false,
52089                 scope: editor, // was editor...
52090                 handler:handler||editor.relayBtnCmd,
52091                 clickEvent:'mousedown',
52092                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52093                 tabIndex:-1
52094             };
52095         }
52096         
52097         
52098         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52099         this.tb = tb;
52100         this.tb.add(
52101            {
52102                 cls : ' x-signature-btn x-signature-'+id,
52103                 scope: editor, // was editor...
52104                 handler: this.reset,
52105                 clickEvent:'mousedown',
52106                 text: this.labels.clear
52107             },
52108             {
52109                  xtype : 'Fill',
52110                  xns: Roo.Toolbar
52111             }, 
52112             {
52113                 cls : '  x-signature-btn x-signature-'+id,
52114                 scope: editor, // was editor...
52115                 handler: this.confirmHandler,
52116                 clickEvent:'mousedown',
52117                 text: this.labels.confirm
52118             }
52119         );
52120     
52121     },
52122     //public
52123     /**
52124      * when user is clicked confirm then show this image.....
52125      * 
52126      * @return {String} Image Data URI
52127      */
52128     getImageDataURI : function(){
52129         var svg = this.svgEl.dom.parentNode.innerHTML;
52130         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
52131         return src; 
52132     },
52133     /**
52134      * 
52135      * @return {Boolean} this.isConfirmed
52136      */
52137     getConfirmed : function(){
52138         return this.isConfirmed;
52139     },
52140     /**
52141      * 
52142      * @return {Number} this.width
52143      */
52144     getWidth : function(){
52145         return this.width;
52146     },
52147     /**
52148      * 
52149      * @return {Number} this.height
52150      */
52151     getHeight : function(){
52152         return this.height;
52153     },
52154     // private
52155     getSignature : function(){
52156         return this.signatureTmp;
52157     },
52158     // private
52159     reset : function(){
52160         this.signatureTmp = '';
52161         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52162         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
52163         this.isConfirmed = false;
52164         Roo.form.Signature.superclass.reset.call(this);
52165     },
52166     setSignature : function(s){
52167         this.signatureTmp = s;
52168         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52169         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
52170         this.setValue(s);
52171         this.isConfirmed = false;
52172         Roo.form.Signature.superclass.reset.call(this);
52173     }, 
52174     test : function(){
52175 //        Roo.log(this.signPanel.dom.contentWindow.up())
52176     },
52177     //private
52178     setConfirmed : function(){
52179         
52180         
52181         
52182 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
52183     },
52184     // private
52185     confirmHandler : function(){
52186         if(!this.getSignature()){
52187             return;
52188         }
52189         
52190         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
52191         this.setValue(this.getSignature());
52192         this.isConfirmed = true;
52193         
52194         this.fireEvent('confirm', this);
52195     },
52196     // private
52197     // Subclasses should provide the validation implementation by overriding this
52198     validateValue : function(value){
52199         if(this.allowBlank){
52200             return true;
52201         }
52202         
52203         if(this.isConfirmed){
52204             return true;
52205         }
52206         return false;
52207     }
52208 });/*
52209  * Based on:
52210  * Ext JS Library 1.1.1
52211  * Copyright(c) 2006-2007, Ext JS, LLC.
52212  *
52213  * Originally Released Under LGPL - original licence link has changed is not relivant.
52214  *
52215  * Fork - LGPL
52216  * <script type="text/javascript">
52217  */
52218  
52219
52220 /**
52221  * @class Roo.form.ComboBox
52222  * @extends Roo.form.TriggerField
52223  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
52224  * @constructor
52225  * Create a new ComboBox.
52226  * @param {Object} config Configuration options
52227  */
52228 Roo.form.Select = function(config){
52229     Roo.form.Select.superclass.constructor.call(this, config);
52230      
52231 };
52232
52233 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
52234     /**
52235      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
52236      */
52237     /**
52238      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
52239      * rendering into an Roo.Editor, defaults to false)
52240      */
52241     /**
52242      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
52243      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
52244      */
52245     /**
52246      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
52247      */
52248     /**
52249      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
52250      * the dropdown list (defaults to undefined, with no header element)
52251      */
52252
52253      /**
52254      * @cfg {String/Roo.Template} tpl The template to use to render the output
52255      */
52256      
52257     // private
52258     defaultAutoCreate : {tag: "select"  },
52259     /**
52260      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
52261      */
52262     listWidth: undefined,
52263     /**
52264      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
52265      * mode = 'remote' or 'text' if mode = 'local')
52266      */
52267     displayField: undefined,
52268     /**
52269      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
52270      * mode = 'remote' or 'value' if mode = 'local'). 
52271      * Note: use of a valueField requires the user make a selection
52272      * in order for a value to be mapped.
52273      */
52274     valueField: undefined,
52275     
52276     
52277     /**
52278      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
52279      * field's data value (defaults to the underlying DOM element's name)
52280      */
52281     hiddenName: undefined,
52282     /**
52283      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
52284      */
52285     listClass: '',
52286     /**
52287      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
52288      */
52289     selectedClass: 'x-combo-selected',
52290     /**
52291      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
52292      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
52293      * which displays a downward arrow icon).
52294      */
52295     triggerClass : 'x-form-arrow-trigger',
52296     /**
52297      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
52298      */
52299     shadow:'sides',
52300     /**
52301      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
52302      * anchor positions (defaults to 'tl-bl')
52303      */
52304     listAlign: 'tl-bl?',
52305     /**
52306      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
52307      */
52308     maxHeight: 300,
52309     /**
52310      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
52311      * query specified by the allQuery config option (defaults to 'query')
52312      */
52313     triggerAction: 'query',
52314     /**
52315      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
52316      * (defaults to 4, does not apply if editable = false)
52317      */
52318     minChars : 4,
52319     /**
52320      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
52321      * delay (typeAheadDelay) if it matches a known value (defaults to false)
52322      */
52323     typeAhead: false,
52324     /**
52325      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
52326      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
52327      */
52328     queryDelay: 500,
52329     /**
52330      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
52331      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
52332      */
52333     pageSize: 0,
52334     /**
52335      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
52336      * when editable = true (defaults to false)
52337      */
52338     selectOnFocus:false,
52339     /**
52340      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
52341      */
52342     queryParam: 'query',
52343     /**
52344      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
52345      * when mode = 'remote' (defaults to 'Loading...')
52346      */
52347     loadingText: 'Loading...',
52348     /**
52349      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
52350      */
52351     resizable: false,
52352     /**
52353      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
52354      */
52355     handleHeight : 8,
52356     /**
52357      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
52358      * traditional select (defaults to true)
52359      */
52360     editable: true,
52361     /**
52362      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
52363      */
52364     allQuery: '',
52365     /**
52366      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
52367      */
52368     mode: 'remote',
52369     /**
52370      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
52371      * listWidth has a higher value)
52372      */
52373     minListWidth : 70,
52374     /**
52375      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
52376      * allow the user to set arbitrary text into the field (defaults to false)
52377      */
52378     forceSelection:false,
52379     /**
52380      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
52381      * if typeAhead = true (defaults to 250)
52382      */
52383     typeAheadDelay : 250,
52384     /**
52385      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
52386      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
52387      */
52388     valueNotFoundText : undefined,
52389     
52390     /**
52391      * @cfg {String} defaultValue The value displayed after loading the store.
52392      */
52393     defaultValue: '',
52394     
52395     /**
52396      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
52397      */
52398     blockFocus : false,
52399     
52400     /**
52401      * @cfg {Boolean} disableClear Disable showing of clear button.
52402      */
52403     disableClear : false,
52404     /**
52405      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
52406      */
52407     alwaysQuery : false,
52408     
52409     //private
52410     addicon : false,
52411     editicon: false,
52412     
52413     // element that contains real text value.. (when hidden is used..)
52414      
52415     // private
52416     onRender : function(ct, position){
52417         Roo.form.Field.prototype.onRender.call(this, ct, position);
52418         
52419         if(this.store){
52420             this.store.on('beforeload', this.onBeforeLoad, this);
52421             this.store.on('load', this.onLoad, this);
52422             this.store.on('loadexception', this.onLoadException, this);
52423             this.store.load({});
52424         }
52425         
52426         
52427         
52428     },
52429
52430     // private
52431     initEvents : function(){
52432         //Roo.form.ComboBox.superclass.initEvents.call(this);
52433  
52434     },
52435
52436     onDestroy : function(){
52437        
52438         if(this.store){
52439             this.store.un('beforeload', this.onBeforeLoad, this);
52440             this.store.un('load', this.onLoad, this);
52441             this.store.un('loadexception', this.onLoadException, this);
52442         }
52443         //Roo.form.ComboBox.superclass.onDestroy.call(this);
52444     },
52445
52446     // private
52447     fireKey : function(e){
52448         if(e.isNavKeyPress() && !this.list.isVisible()){
52449             this.fireEvent("specialkey", this, e);
52450         }
52451     },
52452
52453     // private
52454     onResize: function(w, h){
52455         
52456         return; 
52457     
52458         
52459     },
52460
52461     /**
52462      * Allow or prevent the user from directly editing the field text.  If false is passed,
52463      * the user will only be able to select from the items defined in the dropdown list.  This method
52464      * is the runtime equivalent of setting the 'editable' config option at config time.
52465      * @param {Boolean} value True to allow the user to directly edit the field text
52466      */
52467     setEditable : function(value){
52468          
52469     },
52470
52471     // private
52472     onBeforeLoad : function(){
52473         
52474         Roo.log("Select before load");
52475         return;
52476     
52477         this.innerList.update(this.loadingText ?
52478                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
52479         //this.restrictHeight();
52480         this.selectedIndex = -1;
52481     },
52482
52483     // private
52484     onLoad : function(){
52485
52486     
52487         var dom = this.el.dom;
52488         dom.innerHTML = '';
52489          var od = dom.ownerDocument;
52490          
52491         if (this.emptyText) {
52492             var op = od.createElement('option');
52493             op.setAttribute('value', '');
52494             op.innerHTML = String.format('{0}', this.emptyText);
52495             dom.appendChild(op);
52496         }
52497         if(this.store.getCount() > 0){
52498            
52499             var vf = this.valueField;
52500             var df = this.displayField;
52501             this.store.data.each(function(r) {
52502                 // which colmsn to use... testing - cdoe / title..
52503                 var op = od.createElement('option');
52504                 op.setAttribute('value', r.data[vf]);
52505                 op.innerHTML = String.format('{0}', r.data[df]);
52506                 dom.appendChild(op);
52507             });
52508             if (typeof(this.defaultValue != 'undefined')) {
52509                 this.setValue(this.defaultValue);
52510             }
52511             
52512              
52513         }else{
52514             //this.onEmptyResults();
52515         }
52516         //this.el.focus();
52517     },
52518     // private
52519     onLoadException : function()
52520     {
52521         dom.innerHTML = '';
52522             
52523         Roo.log("Select on load exception");
52524         return;
52525     
52526         this.collapse();
52527         Roo.log(this.store.reader.jsonData);
52528         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
52529             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
52530         }
52531         
52532         
52533     },
52534     // private
52535     onTypeAhead : function(){
52536          
52537     },
52538
52539     // private
52540     onSelect : function(record, index){
52541         Roo.log('on select?');
52542         return;
52543         if(this.fireEvent('beforeselect', this, record, index) !== false){
52544             this.setFromData(index > -1 ? record.data : false);
52545             this.collapse();
52546             this.fireEvent('select', this, record, index);
52547         }
52548     },
52549
52550     /**
52551      * Returns the currently selected field value or empty string if no value is set.
52552      * @return {String} value The selected value
52553      */
52554     getValue : function(){
52555         var dom = this.el.dom;
52556         this.value = dom.options[dom.selectedIndex].value;
52557         return this.value;
52558         
52559     },
52560
52561     /**
52562      * Clears any text/value currently set in the field
52563      */
52564     clearValue : function(){
52565         this.value = '';
52566         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
52567         
52568     },
52569
52570     /**
52571      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
52572      * will be displayed in the field.  If the value does not match the data value of an existing item,
52573      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
52574      * Otherwise the field will be blank (although the value will still be set).
52575      * @param {String} value The value to match
52576      */
52577     setValue : function(v){
52578         var d = this.el.dom;
52579         for (var i =0; i < d.options.length;i++) {
52580             if (v == d.options[i].value) {
52581                 d.selectedIndex = i;
52582                 this.value = v;
52583                 return;
52584             }
52585         }
52586         this.clearValue();
52587     },
52588     /**
52589      * @property {Object} the last set data for the element
52590      */
52591     
52592     lastData : false,
52593     /**
52594      * Sets the value of the field based on a object which is related to the record format for the store.
52595      * @param {Object} value the value to set as. or false on reset?
52596      */
52597     setFromData : function(o){
52598         Roo.log('setfrom data?');
52599          
52600         
52601         
52602     },
52603     // private
52604     reset : function(){
52605         this.clearValue();
52606     },
52607     // private
52608     findRecord : function(prop, value){
52609         
52610         return false;
52611     
52612         var record;
52613         if(this.store.getCount() > 0){
52614             this.store.each(function(r){
52615                 if(r.data[prop] == value){
52616                     record = r;
52617                     return false;
52618                 }
52619                 return true;
52620             });
52621         }
52622         return record;
52623     },
52624     
52625     getName: function()
52626     {
52627         // returns hidden if it's set..
52628         if (!this.rendered) {return ''};
52629         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
52630         
52631     },
52632      
52633
52634     
52635
52636     // private
52637     onEmptyResults : function(){
52638         Roo.log('empty results');
52639         //this.collapse();
52640     },
52641
52642     /**
52643      * Returns true if the dropdown list is expanded, else false.
52644      */
52645     isExpanded : function(){
52646         return false;
52647     },
52648
52649     /**
52650      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
52651      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52652      * @param {String} value The data value of the item to select
52653      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52654      * selected item if it is not currently in view (defaults to true)
52655      * @return {Boolean} True if the value matched an item in the list, else false
52656      */
52657     selectByValue : function(v, scrollIntoView){
52658         Roo.log('select By Value');
52659         return false;
52660     
52661         if(v !== undefined && v !== null){
52662             var r = this.findRecord(this.valueField || this.displayField, v);
52663             if(r){
52664                 this.select(this.store.indexOf(r), scrollIntoView);
52665                 return true;
52666             }
52667         }
52668         return false;
52669     },
52670
52671     /**
52672      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
52673      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
52674      * @param {Number} index The zero-based index of the list item to select
52675      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
52676      * selected item if it is not currently in view (defaults to true)
52677      */
52678     select : function(index, scrollIntoView){
52679         Roo.log('select ');
52680         return  ;
52681         
52682         this.selectedIndex = index;
52683         this.view.select(index);
52684         if(scrollIntoView !== false){
52685             var el = this.view.getNode(index);
52686             if(el){
52687                 this.innerList.scrollChildIntoView(el, false);
52688             }
52689         }
52690     },
52691
52692       
52693
52694     // private
52695     validateBlur : function(){
52696         
52697         return;
52698         
52699     },
52700
52701     // private
52702     initQuery : function(){
52703         this.doQuery(this.getRawValue());
52704     },
52705
52706     // private
52707     doForce : function(){
52708         if(this.el.dom.value.length > 0){
52709             this.el.dom.value =
52710                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
52711              
52712         }
52713     },
52714
52715     /**
52716      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
52717      * query allowing the query action to be canceled if needed.
52718      * @param {String} query The SQL query to execute
52719      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
52720      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
52721      * saved in the current store (defaults to false)
52722      */
52723     doQuery : function(q, forceAll){
52724         
52725         Roo.log('doQuery?');
52726         if(q === undefined || q === null){
52727             q = '';
52728         }
52729         var qe = {
52730             query: q,
52731             forceAll: forceAll,
52732             combo: this,
52733             cancel:false
52734         };
52735         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
52736             return false;
52737         }
52738         q = qe.query;
52739         forceAll = qe.forceAll;
52740         if(forceAll === true || (q.length >= this.minChars)){
52741             if(this.lastQuery != q || this.alwaysQuery){
52742                 this.lastQuery = q;
52743                 if(this.mode == 'local'){
52744                     this.selectedIndex = -1;
52745                     if(forceAll){
52746                         this.store.clearFilter();
52747                     }else{
52748                         this.store.filter(this.displayField, q);
52749                     }
52750                     this.onLoad();
52751                 }else{
52752                     this.store.baseParams[this.queryParam] = q;
52753                     this.store.load({
52754                         params: this.getParams(q)
52755                     });
52756                     this.expand();
52757                 }
52758             }else{
52759                 this.selectedIndex = -1;
52760                 this.onLoad();   
52761             }
52762         }
52763     },
52764
52765     // private
52766     getParams : function(q){
52767         var p = {};
52768         //p[this.queryParam] = q;
52769         if(this.pageSize){
52770             p.start = 0;
52771             p.limit = this.pageSize;
52772         }
52773         return p;
52774     },
52775
52776     /**
52777      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
52778      */
52779     collapse : function(){
52780         
52781     },
52782
52783     // private
52784     collapseIf : function(e){
52785         
52786     },
52787
52788     /**
52789      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
52790      */
52791     expand : function(){
52792         
52793     } ,
52794
52795     // private
52796      
52797
52798     /** 
52799     * @cfg {Boolean} grow 
52800     * @hide 
52801     */
52802     /** 
52803     * @cfg {Number} growMin 
52804     * @hide 
52805     */
52806     /** 
52807     * @cfg {Number} growMax 
52808     * @hide 
52809     */
52810     /**
52811      * @hide
52812      * @method autoSize
52813      */
52814     
52815     setWidth : function()
52816     {
52817         
52818     },
52819     getResizeEl : function(){
52820         return this.el;
52821     }
52822 });//<script type="text/javasscript">
52823  
52824
52825 /**
52826  * @class Roo.DDView
52827  * A DnD enabled version of Roo.View.
52828  * @param {Element/String} container The Element in which to create the View.
52829  * @param {String} tpl The template string used to create the markup for each element of the View
52830  * @param {Object} config The configuration properties. These include all the config options of
52831  * {@link Roo.View} plus some specific to this class.<br>
52832  * <p>
52833  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
52834  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
52835  * <p>
52836  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
52837 .x-view-drag-insert-above {
52838         border-top:1px dotted #3366cc;
52839 }
52840 .x-view-drag-insert-below {
52841         border-bottom:1px dotted #3366cc;
52842 }
52843 </code></pre>
52844  * 
52845  */
52846  
52847 Roo.DDView = function(container, tpl, config) {
52848     Roo.DDView.superclass.constructor.apply(this, arguments);
52849     this.getEl().setStyle("outline", "0px none");
52850     this.getEl().unselectable();
52851     if (this.dragGroup) {
52852         this.setDraggable(this.dragGroup.split(","));
52853     }
52854     if (this.dropGroup) {
52855         this.setDroppable(this.dropGroup.split(","));
52856     }
52857     if (this.deletable) {
52858         this.setDeletable();
52859     }
52860     this.isDirtyFlag = false;
52861         this.addEvents({
52862                 "drop" : true
52863         });
52864 };
52865
52866 Roo.extend(Roo.DDView, Roo.View, {
52867 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
52868 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
52869 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
52870 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
52871
52872         isFormField: true,
52873
52874         reset: Roo.emptyFn,
52875         
52876         clearInvalid: Roo.form.Field.prototype.clearInvalid,
52877
52878         validate: function() {
52879                 return true;
52880         },
52881         
52882         destroy: function() {
52883                 this.purgeListeners();
52884                 this.getEl.removeAllListeners();
52885                 this.getEl().remove();
52886                 if (this.dragZone) {
52887                         if (this.dragZone.destroy) {
52888                                 this.dragZone.destroy();
52889                         }
52890                 }
52891                 if (this.dropZone) {
52892                         if (this.dropZone.destroy) {
52893                                 this.dropZone.destroy();
52894                         }
52895                 }
52896         },
52897
52898 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
52899         getName: function() {
52900                 return this.name;
52901         },
52902
52903 /**     Loads the View from a JSON string representing the Records to put into the Store. */
52904         setValue: function(v) {
52905                 if (!this.store) {
52906                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
52907                 }
52908                 var data = {};
52909                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
52910                 this.store.proxy = new Roo.data.MemoryProxy(data);
52911                 this.store.load();
52912         },
52913
52914 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
52915         getValue: function() {
52916                 var result = '(';
52917                 this.store.each(function(rec) {
52918                         result += rec.id + ',';
52919                 });
52920                 return result.substr(0, result.length - 1) + ')';
52921         },
52922         
52923         getIds: function() {
52924                 var i = 0, result = new Array(this.store.getCount());
52925                 this.store.each(function(rec) {
52926                         result[i++] = rec.id;
52927                 });
52928                 return result;
52929         },
52930         
52931         isDirty: function() {
52932                 return this.isDirtyFlag;
52933         },
52934
52935 /**
52936  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
52937  *      whole Element becomes the target, and this causes the drop gesture to append.
52938  */
52939     getTargetFromEvent : function(e) {
52940                 var target = e.getTarget();
52941                 while ((target !== null) && (target.parentNode != this.el.dom)) {
52942                 target = target.parentNode;
52943                 }
52944                 if (!target) {
52945                         target = this.el.dom.lastChild || this.el.dom;
52946                 }
52947                 return target;
52948     },
52949
52950 /**
52951  *      Create the drag data which consists of an object which has the property "ddel" as
52952  *      the drag proxy element. 
52953  */
52954     getDragData : function(e) {
52955         var target = this.findItemFromChild(e.getTarget());
52956                 if(target) {
52957                         this.handleSelection(e);
52958                         var selNodes = this.getSelectedNodes();
52959             var dragData = {
52960                 source: this,
52961                 copy: this.copy || (this.allowCopy && e.ctrlKey),
52962                 nodes: selNodes,
52963                 records: []
52964                         };
52965                         var selectedIndices = this.getSelectedIndexes();
52966                         for (var i = 0; i < selectedIndices.length; i++) {
52967                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
52968                         }
52969                         if (selNodes.length == 1) {
52970                                 dragData.ddel = target.cloneNode(true); // the div element
52971                         } else {
52972                                 var div = document.createElement('div'); // create the multi element drag "ghost"
52973                                 div.className = 'multi-proxy';
52974                                 for (var i = 0, len = selNodes.length; i < len; i++) {
52975                                         div.appendChild(selNodes[i].cloneNode(true));
52976                                 }
52977                                 dragData.ddel = div;
52978                         }
52979             //console.log(dragData)
52980             //console.log(dragData.ddel.innerHTML)
52981                         return dragData;
52982                 }
52983         //console.log('nodragData')
52984                 return false;
52985     },
52986     
52987 /**     Specify to which ddGroup items in this DDView may be dragged. */
52988     setDraggable: function(ddGroup) {
52989         if (ddGroup instanceof Array) {
52990                 Roo.each(ddGroup, this.setDraggable, this);
52991                 return;
52992         }
52993         if (this.dragZone) {
52994                 this.dragZone.addToGroup(ddGroup);
52995         } else {
52996                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
52997                                 containerScroll: true,
52998                                 ddGroup: ddGroup 
52999
53000                         });
53001 //                      Draggability implies selection. DragZone's mousedown selects the element.
53002                         if (!this.multiSelect) { this.singleSelect = true; }
53003
53004 //                      Wire the DragZone's handlers up to methods in *this*
53005                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
53006                 }
53007     },
53008
53009 /**     Specify from which ddGroup this DDView accepts drops. */
53010     setDroppable: function(ddGroup) {
53011         if (ddGroup instanceof Array) {
53012                 Roo.each(ddGroup, this.setDroppable, this);
53013                 return;
53014         }
53015         if (this.dropZone) {
53016                 this.dropZone.addToGroup(ddGroup);
53017         } else {
53018                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
53019                                 containerScroll: true,
53020                                 ddGroup: ddGroup
53021                         });
53022
53023 //                      Wire the DropZone's handlers up to methods in *this*
53024                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
53025                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
53026                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
53027                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
53028                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
53029                 }
53030     },
53031
53032 /**     Decide whether to drop above or below a View node. */
53033     getDropPoint : function(e, n, dd){
53034         if (n == this.el.dom) { return "above"; }
53035                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
53036                 var c = t + (b - t) / 2;
53037                 var y = Roo.lib.Event.getPageY(e);
53038                 if(y <= c) {
53039                         return "above";
53040                 }else{
53041                         return "below";
53042                 }
53043     },
53044
53045     onNodeEnter : function(n, dd, e, data){
53046                 return false;
53047     },
53048     
53049     onNodeOver : function(n, dd, e, data){
53050                 var pt = this.getDropPoint(e, n, dd);
53051                 // set the insert point style on the target node
53052                 var dragElClass = this.dropNotAllowed;
53053                 if (pt) {
53054                         var targetElClass;
53055                         if (pt == "above"){
53056                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
53057                                 targetElClass = "x-view-drag-insert-above";
53058                         } else {
53059                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
53060                                 targetElClass = "x-view-drag-insert-below";
53061                         }
53062                         if (this.lastInsertClass != targetElClass){
53063                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
53064                                 this.lastInsertClass = targetElClass;
53065                         }
53066                 }
53067                 return dragElClass;
53068         },
53069
53070     onNodeOut : function(n, dd, e, data){
53071                 this.removeDropIndicators(n);
53072     },
53073
53074     onNodeDrop : function(n, dd, e, data){
53075         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
53076                 return false;
53077         }
53078         var pt = this.getDropPoint(e, n, dd);
53079                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
53080                 if (pt == "below") { insertAt++; }
53081                 for (var i = 0; i < data.records.length; i++) {
53082                         var r = data.records[i];
53083                         var dup = this.store.getById(r.id);
53084                         if (dup && (dd != this.dragZone)) {
53085                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
53086                         } else {
53087                                 if (data.copy) {
53088                                         this.store.insert(insertAt++, r.copy());
53089                                 } else {
53090                                         data.source.isDirtyFlag = true;
53091                                         r.store.remove(r);
53092                                         this.store.insert(insertAt++, r);
53093                                 }
53094                                 this.isDirtyFlag = true;
53095                         }
53096                 }
53097                 this.dragZone.cachedTarget = null;
53098                 return true;
53099     },
53100
53101     removeDropIndicators : function(n){
53102                 if(n){
53103                         Roo.fly(n).removeClass([
53104                                 "x-view-drag-insert-above",
53105                                 "x-view-drag-insert-below"]);
53106                         this.lastInsertClass = "_noclass";
53107                 }
53108     },
53109
53110 /**
53111  *      Utility method. Add a delete option to the DDView's context menu.
53112  *      @param {String} imageUrl The URL of the "delete" icon image.
53113  */
53114         setDeletable: function(imageUrl) {
53115                 if (!this.singleSelect && !this.multiSelect) {
53116                         this.singleSelect = true;
53117                 }
53118                 var c = this.getContextMenu();
53119                 this.contextMenu.on("itemclick", function(item) {
53120                         switch (item.id) {
53121                                 case "delete":
53122                                         this.remove(this.getSelectedIndexes());
53123                                         break;
53124                         }
53125                 }, this);
53126                 this.contextMenu.add({
53127                         icon: imageUrl,
53128                         id: "delete",
53129                         text: 'Delete'
53130                 });
53131         },
53132         
53133 /**     Return the context menu for this DDView. */
53134         getContextMenu: function() {
53135                 if (!this.contextMenu) {
53136 //                      Create the View's context menu
53137                         this.contextMenu = new Roo.menu.Menu({
53138                                 id: this.id + "-contextmenu"
53139                         });
53140                         this.el.on("contextmenu", this.showContextMenu, this);
53141                 }
53142                 return this.contextMenu;
53143         },
53144         
53145         disableContextMenu: function() {
53146                 if (this.contextMenu) {
53147                         this.el.un("contextmenu", this.showContextMenu, this);
53148                 }
53149         },
53150
53151         showContextMenu: function(e, item) {
53152         item = this.findItemFromChild(e.getTarget());
53153                 if (item) {
53154                         e.stopEvent();
53155                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
53156                         this.contextMenu.showAt(e.getXY());
53157             }
53158     },
53159
53160 /**
53161  *      Remove {@link Roo.data.Record}s at the specified indices.
53162  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
53163  */
53164     remove: function(selectedIndices) {
53165                 selectedIndices = [].concat(selectedIndices);
53166                 for (var i = 0; i < selectedIndices.length; i++) {
53167                         var rec = this.store.getAt(selectedIndices[i]);
53168                         this.store.remove(rec);
53169                 }
53170     },
53171
53172 /**
53173  *      Double click fires the event, but also, if this is draggable, and there is only one other
53174  *      related DropZone, it transfers the selected node.
53175  */
53176     onDblClick : function(e){
53177         var item = this.findItemFromChild(e.getTarget());
53178         if(item){
53179             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
53180                 return false;
53181             }
53182             if (this.dragGroup) {
53183                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
53184                     while (targets.indexOf(this.dropZone) > -1) {
53185                             targets.remove(this.dropZone);
53186                                 }
53187                     if (targets.length == 1) {
53188                                         this.dragZone.cachedTarget = null;
53189                         var el = Roo.get(targets[0].getEl());
53190                         var box = el.getBox(true);
53191                         targets[0].onNodeDrop(el.dom, {
53192                                 target: el.dom,
53193                                 xy: [box.x, box.y + box.height - 1]
53194                         }, null, this.getDragData(e));
53195                     }
53196                 }
53197         }
53198     },
53199     
53200     handleSelection: function(e) {
53201                 this.dragZone.cachedTarget = null;
53202         var item = this.findItemFromChild(e.getTarget());
53203         if (!item) {
53204                 this.clearSelections(true);
53205                 return;
53206         }
53207                 if (item && (this.multiSelect || this.singleSelect)){
53208                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
53209                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
53210                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
53211                                 this.unselect(item);
53212                         } else {
53213                                 this.select(item, this.multiSelect && e.ctrlKey);
53214                                 this.lastSelection = item;
53215                         }
53216                 }
53217     },
53218
53219     onItemClick : function(item, index, e){
53220                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
53221                         return false;
53222                 }
53223                 return true;
53224     },
53225
53226     unselect : function(nodeInfo, suppressEvent){
53227                 var node = this.getNode(nodeInfo);
53228                 if(node && this.isSelected(node)){
53229                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
53230                                 Roo.fly(node).removeClass(this.selectedClass);
53231                                 this.selections.remove(node);
53232                                 if(!suppressEvent){
53233                                         this.fireEvent("selectionchange", this, this.selections);
53234                                 }
53235                         }
53236                 }
53237     }
53238 });
53239 /*
53240  * Based on:
53241  * Ext JS Library 1.1.1
53242  * Copyright(c) 2006-2007, Ext JS, LLC.
53243  *
53244  * Originally Released Under LGPL - original licence link has changed is not relivant.
53245  *
53246  * Fork - LGPL
53247  * <script type="text/javascript">
53248  */
53249  
53250 /**
53251  * @class Roo.LayoutManager
53252  * @extends Roo.util.Observable
53253  * Base class for layout managers.
53254  */
53255 Roo.LayoutManager = function(container, config){
53256     Roo.LayoutManager.superclass.constructor.call(this);
53257     this.el = Roo.get(container);
53258     // ie scrollbar fix
53259     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
53260         document.body.scroll = "no";
53261     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
53262         this.el.position('relative');
53263     }
53264     this.id = this.el.id;
53265     this.el.addClass("x-layout-container");
53266     /** false to disable window resize monitoring @type Boolean */
53267     this.monitorWindowResize = true;
53268     this.regions = {};
53269     this.addEvents({
53270         /**
53271          * @event layout
53272          * Fires when a layout is performed. 
53273          * @param {Roo.LayoutManager} this
53274          */
53275         "layout" : true,
53276         /**
53277          * @event regionresized
53278          * Fires when the user resizes a region. 
53279          * @param {Roo.LayoutRegion} region The resized region
53280          * @param {Number} newSize The new size (width for east/west, height for north/south)
53281          */
53282         "regionresized" : true,
53283         /**
53284          * @event regioncollapsed
53285          * Fires when a region is collapsed. 
53286          * @param {Roo.LayoutRegion} region The collapsed region
53287          */
53288         "regioncollapsed" : true,
53289         /**
53290          * @event regionexpanded
53291          * Fires when a region is expanded.  
53292          * @param {Roo.LayoutRegion} region The expanded region
53293          */
53294         "regionexpanded" : true
53295     });
53296     this.updating = false;
53297     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53298 };
53299
53300 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
53301     /**
53302      * Returns true if this layout is currently being updated
53303      * @return {Boolean}
53304      */
53305     isUpdating : function(){
53306         return this.updating; 
53307     },
53308     
53309     /**
53310      * Suspend the LayoutManager from doing auto-layouts while
53311      * making multiple add or remove calls
53312      */
53313     beginUpdate : function(){
53314         this.updating = true;    
53315     },
53316     
53317     /**
53318      * Restore auto-layouts and optionally disable the manager from performing a layout
53319      * @param {Boolean} noLayout true to disable a layout update 
53320      */
53321     endUpdate : function(noLayout){
53322         this.updating = false;
53323         if(!noLayout){
53324             this.layout();
53325         }    
53326     },
53327     
53328     layout: function(){
53329         
53330     },
53331     
53332     onRegionResized : function(region, newSize){
53333         this.fireEvent("regionresized", region, newSize);
53334         this.layout();
53335     },
53336     
53337     onRegionCollapsed : function(region){
53338         this.fireEvent("regioncollapsed", region);
53339     },
53340     
53341     onRegionExpanded : function(region){
53342         this.fireEvent("regionexpanded", region);
53343     },
53344         
53345     /**
53346      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
53347      * performs box-model adjustments.
53348      * @return {Object} The size as an object {width: (the width), height: (the height)}
53349      */
53350     getViewSize : function(){
53351         var size;
53352         if(this.el.dom != document.body){
53353             size = this.el.getSize();
53354         }else{
53355             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
53356         }
53357         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
53358         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
53359         return size;
53360     },
53361     
53362     /**
53363      * Returns the Element this layout is bound to.
53364      * @return {Roo.Element}
53365      */
53366     getEl : function(){
53367         return this.el;
53368     },
53369     
53370     /**
53371      * Returns the specified region.
53372      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
53373      * @return {Roo.LayoutRegion}
53374      */
53375     getRegion : function(target){
53376         return this.regions[target.toLowerCase()];
53377     },
53378     
53379     onWindowResize : function(){
53380         if(this.monitorWindowResize){
53381             this.layout();
53382         }
53383     }
53384 });/*
53385  * Based on:
53386  * Ext JS Library 1.1.1
53387  * Copyright(c) 2006-2007, Ext JS, LLC.
53388  *
53389  * Originally Released Under LGPL - original licence link has changed is not relivant.
53390  *
53391  * Fork - LGPL
53392  * <script type="text/javascript">
53393  */
53394 /**
53395  * @class Roo.BorderLayout
53396  * @extends Roo.LayoutManager
53397  * @children Roo.ContentPanel
53398  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
53399  * please see: <br><br>
53400  * <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>
53401  * <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>
53402  * Example:
53403  <pre><code>
53404  var layout = new Roo.BorderLayout(document.body, {
53405     north: {
53406         initialSize: 25,
53407         titlebar: false
53408     },
53409     west: {
53410         split:true,
53411         initialSize: 200,
53412         minSize: 175,
53413         maxSize: 400,
53414         titlebar: true,
53415         collapsible: true
53416     },
53417     east: {
53418         split:true,
53419         initialSize: 202,
53420         minSize: 175,
53421         maxSize: 400,
53422         titlebar: true,
53423         collapsible: true
53424     },
53425     south: {
53426         split:true,
53427         initialSize: 100,
53428         minSize: 100,
53429         maxSize: 200,
53430         titlebar: true,
53431         collapsible: true
53432     },
53433     center: {
53434         titlebar: true,
53435         autoScroll:true,
53436         resizeTabs: true,
53437         minTabWidth: 50,
53438         preferredTabWidth: 150
53439     }
53440 });
53441
53442 // shorthand
53443 var CP = Roo.ContentPanel;
53444
53445 layout.beginUpdate();
53446 layout.add("north", new CP("north", "North"));
53447 layout.add("south", new CP("south", {title: "South", closable: true}));
53448 layout.add("west", new CP("west", {title: "West"}));
53449 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
53450 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
53451 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
53452 layout.getRegion("center").showPanel("center1");
53453 layout.endUpdate();
53454 </code></pre>
53455
53456 <b>The container the layout is rendered into can be either the body element or any other element.
53457 If it is not the body element, the container needs to either be an absolute positioned element,
53458 or you will need to add "position:relative" to the css of the container.  You will also need to specify
53459 the container size if it is not the body element.</b>
53460
53461 * @constructor
53462 * Create a new BorderLayout
53463 * @param {String/HTMLElement/Element} container The container this layout is bound to
53464 * @param {Object} config Configuration options
53465  */
53466 Roo.BorderLayout = function(container, config){
53467     config = config || {};
53468     Roo.BorderLayout.superclass.constructor.call(this, container, config);
53469     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
53470     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
53471         var target = this.factory.validRegions[i];
53472         if(config[target]){
53473             this.addRegion(target, config[target]);
53474         }
53475     }
53476 };
53477
53478 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
53479         
53480         /**
53481          * @cfg {Roo.LayoutRegion} east
53482          */
53483         /**
53484          * @cfg {Roo.LayoutRegion} west
53485          */
53486         /**
53487          * @cfg {Roo.LayoutRegion} north
53488          */
53489         /**
53490          * @cfg {Roo.LayoutRegion} south
53491          */
53492         /**
53493          * @cfg {Roo.LayoutRegion} center
53494          */
53495     /**
53496      * Creates and adds a new region if it doesn't already exist.
53497      * @param {String} target The target region key (north, south, east, west or center).
53498      * @param {Object} config The regions config object
53499      * @return {BorderLayoutRegion} The new region
53500      */
53501     addRegion : function(target, config){
53502         if(!this.regions[target]){
53503             var r = this.factory.create(target, this, config);
53504             this.bindRegion(target, r);
53505         }
53506         return this.regions[target];
53507     },
53508
53509     // private (kinda)
53510     bindRegion : function(name, r){
53511         this.regions[name] = r;
53512         r.on("visibilitychange", this.layout, this);
53513         r.on("paneladded", this.layout, this);
53514         r.on("panelremoved", this.layout, this);
53515         r.on("invalidated", this.layout, this);
53516         r.on("resized", this.onRegionResized, this);
53517         r.on("collapsed", this.onRegionCollapsed, this);
53518         r.on("expanded", this.onRegionExpanded, this);
53519     },
53520
53521     /**
53522      * Performs a layout update.
53523      */
53524     layout : function(){
53525         if(this.updating) {
53526             return;
53527         }
53528         var size = this.getViewSize();
53529         var w = size.width;
53530         var h = size.height;
53531         var centerW = w;
53532         var centerH = h;
53533         var centerY = 0;
53534         var centerX = 0;
53535         //var x = 0, y = 0;
53536
53537         var rs = this.regions;
53538         var north = rs["north"];
53539         var south = rs["south"]; 
53540         var west = rs["west"];
53541         var east = rs["east"];
53542         var center = rs["center"];
53543         //if(this.hideOnLayout){ // not supported anymore
53544             //c.el.setStyle("display", "none");
53545         //}
53546         if(north && north.isVisible()){
53547             var b = north.getBox();
53548             var m = north.getMargins();
53549             b.width = w - (m.left+m.right);
53550             b.x = m.left;
53551             b.y = m.top;
53552             centerY = b.height + b.y + m.bottom;
53553             centerH -= centerY;
53554             north.updateBox(this.safeBox(b));
53555         }
53556         if(south && south.isVisible()){
53557             var b = south.getBox();
53558             var m = south.getMargins();
53559             b.width = w - (m.left+m.right);
53560             b.x = m.left;
53561             var totalHeight = (b.height + m.top + m.bottom);
53562             b.y = h - totalHeight + m.top;
53563             centerH -= totalHeight;
53564             south.updateBox(this.safeBox(b));
53565         }
53566         if(west && west.isVisible()){
53567             var b = west.getBox();
53568             var m = west.getMargins();
53569             b.height = centerH - (m.top+m.bottom);
53570             b.x = m.left;
53571             b.y = centerY + m.top;
53572             var totalWidth = (b.width + m.left + m.right);
53573             centerX += totalWidth;
53574             centerW -= totalWidth;
53575             west.updateBox(this.safeBox(b));
53576         }
53577         if(east && east.isVisible()){
53578             var b = east.getBox();
53579             var m = east.getMargins();
53580             b.height = centerH - (m.top+m.bottom);
53581             var totalWidth = (b.width + m.left + m.right);
53582             b.x = w - totalWidth + m.left;
53583             b.y = centerY + m.top;
53584             centerW -= totalWidth;
53585             east.updateBox(this.safeBox(b));
53586         }
53587         if(center){
53588             var m = center.getMargins();
53589             var centerBox = {
53590                 x: centerX + m.left,
53591                 y: centerY + m.top,
53592                 width: centerW - (m.left+m.right),
53593                 height: centerH - (m.top+m.bottom)
53594             };
53595             //if(this.hideOnLayout){
53596                 //center.el.setStyle("display", "block");
53597             //}
53598             center.updateBox(this.safeBox(centerBox));
53599         }
53600         this.el.repaint();
53601         this.fireEvent("layout", this);
53602     },
53603
53604     // private
53605     safeBox : function(box){
53606         box.width = Math.max(0, box.width);
53607         box.height = Math.max(0, box.height);
53608         return box;
53609     },
53610
53611     /**
53612      * Adds a ContentPanel (or subclass) to this layout.
53613      * @param {String} target The target region key (north, south, east, west or center).
53614      * @param {Roo.ContentPanel} panel The panel to add
53615      * @return {Roo.ContentPanel} The added panel
53616      */
53617     add : function(target, panel){
53618          
53619         target = target.toLowerCase();
53620         return this.regions[target].add(panel);
53621     },
53622
53623     /**
53624      * Remove a ContentPanel (or subclass) to this layout.
53625      * @param {String} target The target region key (north, south, east, west or center).
53626      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
53627      * @return {Roo.ContentPanel} The removed panel
53628      */
53629     remove : function(target, panel){
53630         target = target.toLowerCase();
53631         return this.regions[target].remove(panel);
53632     },
53633
53634     /**
53635      * Searches all regions for a panel with the specified id
53636      * @param {String} panelId
53637      * @return {Roo.ContentPanel} The panel or null if it wasn't found
53638      */
53639     findPanel : function(panelId){
53640         var rs = this.regions;
53641         for(var target in rs){
53642             if(typeof rs[target] != "function"){
53643                 var p = rs[target].getPanel(panelId);
53644                 if(p){
53645                     return p;
53646                 }
53647             }
53648         }
53649         return null;
53650     },
53651
53652     /**
53653      * Searches all regions for a panel with the specified id and activates (shows) it.
53654      * @param {String/ContentPanel} panelId The panels id or the panel itself
53655      * @return {Roo.ContentPanel} The shown panel or null
53656      */
53657     showPanel : function(panelId) {
53658       var rs = this.regions;
53659       for(var target in rs){
53660          var r = rs[target];
53661          if(typeof r != "function"){
53662             if(r.hasPanel(panelId)){
53663                return r.showPanel(panelId);
53664             }
53665          }
53666       }
53667       return null;
53668    },
53669
53670    /**
53671      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
53672      * @param {Roo.state.Provider} provider (optional) An alternate state provider
53673      */
53674     restoreState : function(provider){
53675         if(!provider){
53676             provider = Roo.state.Manager;
53677         }
53678         var sm = new Roo.LayoutStateManager();
53679         sm.init(this, provider);
53680     },
53681
53682     /**
53683      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
53684      * object should contain properties for each region to add ContentPanels to, and each property's value should be
53685      * a valid ContentPanel config object.  Example:
53686      * <pre><code>
53687 // Create the main layout
53688 var layout = new Roo.BorderLayout('main-ct', {
53689     west: {
53690         split:true,
53691         minSize: 175,
53692         titlebar: true
53693     },
53694     center: {
53695         title:'Components'
53696     }
53697 }, 'main-ct');
53698
53699 // Create and add multiple ContentPanels at once via configs
53700 layout.batchAdd({
53701    west: {
53702        id: 'source-files',
53703        autoCreate:true,
53704        title:'Ext Source Files',
53705        autoScroll:true,
53706        fitToFrame:true
53707    },
53708    center : {
53709        el: cview,
53710        autoScroll:true,
53711        fitToFrame:true,
53712        toolbar: tb,
53713        resizeEl:'cbody'
53714    }
53715 });
53716 </code></pre>
53717      * @param {Object} regions An object containing ContentPanel configs by region name
53718      */
53719     batchAdd : function(regions){
53720         this.beginUpdate();
53721         for(var rname in regions){
53722             var lr = this.regions[rname];
53723             if(lr){
53724                 this.addTypedPanels(lr, regions[rname]);
53725             }
53726         }
53727         this.endUpdate();
53728     },
53729
53730     // private
53731     addTypedPanels : function(lr, ps){
53732         if(typeof ps == 'string'){
53733             lr.add(new Roo.ContentPanel(ps));
53734         }
53735         else if(ps instanceof Array){
53736             for(var i =0, len = ps.length; i < len; i++){
53737                 this.addTypedPanels(lr, ps[i]);
53738             }
53739         }
53740         else if(!ps.events){ // raw config?
53741             var el = ps.el;
53742             delete ps.el; // prevent conflict
53743             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
53744         }
53745         else {  // panel object assumed!
53746             lr.add(ps);
53747         }
53748     },
53749     /**
53750      * Adds a xtype elements to the layout.
53751      * <pre><code>
53752
53753 layout.addxtype({
53754        xtype : 'ContentPanel',
53755        region: 'west',
53756        items: [ .... ]
53757    }
53758 );
53759
53760 layout.addxtype({
53761         xtype : 'NestedLayoutPanel',
53762         region: 'west',
53763         layout: {
53764            center: { },
53765            west: { }   
53766         },
53767         items : [ ... list of content panels or nested layout panels.. ]
53768    }
53769 );
53770 </code></pre>
53771      * @param {Object} cfg Xtype definition of item to add.
53772      */
53773     addxtype : function(cfg)
53774     {
53775         // basically accepts a pannel...
53776         // can accept a layout region..!?!?
53777         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
53778         
53779         if (!cfg.xtype.match(/Panel$/)) {
53780             return false;
53781         }
53782         var ret = false;
53783         
53784         if (typeof(cfg.region) == 'undefined') {
53785             Roo.log("Failed to add Panel, region was not set");
53786             Roo.log(cfg);
53787             return false;
53788         }
53789         var region = cfg.region;
53790         delete cfg.region;
53791         
53792           
53793         var xitems = [];
53794         if (cfg.items) {
53795             xitems = cfg.items;
53796             delete cfg.items;
53797         }
53798         var nb = false;
53799         
53800         switch(cfg.xtype) 
53801         {
53802             case 'ContentPanel':  // ContentPanel (el, cfg)
53803             case 'ScrollPanel':  // ContentPanel (el, cfg)
53804             case 'ViewPanel': 
53805                 if(cfg.autoCreate) {
53806                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53807                 } else {
53808                     var el = this.el.createChild();
53809                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
53810                 }
53811                 
53812                 this.add(region, ret);
53813                 break;
53814             
53815             
53816             case 'TreePanel': // our new panel!
53817                 cfg.el = this.el.createChild();
53818                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53819                 this.add(region, ret);
53820                 break;
53821             
53822             case 'NestedLayoutPanel': 
53823                 // create a new Layout (which is  a Border Layout...
53824                 var el = this.el.createChild();
53825                 var clayout = cfg.layout;
53826                 delete cfg.layout;
53827                 clayout.items   = clayout.items  || [];
53828                 // replace this exitems with the clayout ones..
53829                 xitems = clayout.items;
53830                  
53831                 
53832                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
53833                     cfg.background = false;
53834                 }
53835                 var layout = new Roo.BorderLayout(el, clayout);
53836                 
53837                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
53838                 //console.log('adding nested layout panel '  + cfg.toSource());
53839                 this.add(region, ret);
53840                 nb = {}; /// find first...
53841                 break;
53842                 
53843             case 'GridPanel': 
53844             
53845                 // needs grid and region
53846                 
53847                 //var el = this.getRegion(region).el.createChild();
53848                 var el = this.el.createChild();
53849                 // create the grid first...
53850                 
53851                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
53852                 delete cfg.grid;
53853                 if (region == 'center' && this.active ) {
53854                     cfg.background = false;
53855                 }
53856                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
53857                 
53858                 this.add(region, ret);
53859                 if (cfg.background) {
53860                     ret.on('activate', function(gp) {
53861                         if (!gp.grid.rendered) {
53862                             gp.grid.render();
53863                         }
53864                     });
53865                 } else {
53866                     grid.render();
53867                 }
53868                 break;
53869            
53870            
53871            
53872                 
53873                 
53874                 
53875             default:
53876                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
53877                     
53878                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
53879                     this.add(region, ret);
53880                 } else {
53881                 
53882                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
53883                     return null;
53884                 }
53885                 
53886              // GridPanel (grid, cfg)
53887             
53888         }
53889         this.beginUpdate();
53890         // add children..
53891         var region = '';
53892         var abn = {};
53893         Roo.each(xitems, function(i)  {
53894             region = nb && i.region ? i.region : false;
53895             
53896             var add = ret.addxtype(i);
53897            
53898             if (region) {
53899                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
53900                 if (!i.background) {
53901                     abn[region] = nb[region] ;
53902                 }
53903             }
53904             
53905         });
53906         this.endUpdate();
53907
53908         // make the last non-background panel active..
53909         //if (nb) { Roo.log(abn); }
53910         if (nb) {
53911             
53912             for(var r in abn) {
53913                 region = this.getRegion(r);
53914                 if (region) {
53915                     // tried using nb[r], but it does not work..
53916                      
53917                     region.showPanel(abn[r]);
53918                    
53919                 }
53920             }
53921         }
53922         return ret;
53923         
53924     }
53925 });
53926
53927 /**
53928  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
53929  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
53930  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
53931  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
53932  * <pre><code>
53933 // shorthand
53934 var CP = Roo.ContentPanel;
53935
53936 var layout = Roo.BorderLayout.create({
53937     north: {
53938         initialSize: 25,
53939         titlebar: false,
53940         panels: [new CP("north", "North")]
53941     },
53942     west: {
53943         split:true,
53944         initialSize: 200,
53945         minSize: 175,
53946         maxSize: 400,
53947         titlebar: true,
53948         collapsible: true,
53949         panels: [new CP("west", {title: "West"})]
53950     },
53951     east: {
53952         split:true,
53953         initialSize: 202,
53954         minSize: 175,
53955         maxSize: 400,
53956         titlebar: true,
53957         collapsible: true,
53958         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
53959     },
53960     south: {
53961         split:true,
53962         initialSize: 100,
53963         minSize: 100,
53964         maxSize: 200,
53965         titlebar: true,
53966         collapsible: true,
53967         panels: [new CP("south", {title: "South", closable: true})]
53968     },
53969     center: {
53970         titlebar: true,
53971         autoScroll:true,
53972         resizeTabs: true,
53973         minTabWidth: 50,
53974         preferredTabWidth: 150,
53975         panels: [
53976             new CP("center1", {title: "Close Me", closable: true}),
53977             new CP("center2", {title: "Center Panel", closable: false})
53978         ]
53979     }
53980 }, document.body);
53981
53982 layout.getRegion("center").showPanel("center1");
53983 </code></pre>
53984  * @param config
53985  * @param targetEl
53986  */
53987 Roo.BorderLayout.create = function(config, targetEl){
53988     var layout = new Roo.BorderLayout(targetEl || document.body, config);
53989     layout.beginUpdate();
53990     var regions = Roo.BorderLayout.RegionFactory.validRegions;
53991     for(var j = 0, jlen = regions.length; j < jlen; j++){
53992         var lr = regions[j];
53993         if(layout.regions[lr] && config[lr].panels){
53994             var r = layout.regions[lr];
53995             var ps = config[lr].panels;
53996             layout.addTypedPanels(r, ps);
53997         }
53998     }
53999     layout.endUpdate();
54000     return layout;
54001 };
54002
54003 // private
54004 Roo.BorderLayout.RegionFactory = {
54005     // private
54006     validRegions : ["north","south","east","west","center"],
54007
54008     // private
54009     create : function(target, mgr, config){
54010         target = target.toLowerCase();
54011         if(config.lightweight || config.basic){
54012             return new Roo.BasicLayoutRegion(mgr, config, target);
54013         }
54014         switch(target){
54015             case "north":
54016                 return new Roo.NorthLayoutRegion(mgr, config);
54017             case "south":
54018                 return new Roo.SouthLayoutRegion(mgr, config);
54019             case "east":
54020                 return new Roo.EastLayoutRegion(mgr, config);
54021             case "west":
54022                 return new Roo.WestLayoutRegion(mgr, config);
54023             case "center":
54024                 return new Roo.CenterLayoutRegion(mgr, config);
54025         }
54026         throw 'Layout region "'+target+'" not supported.';
54027     }
54028 };/*
54029  * Based on:
54030  * Ext JS Library 1.1.1
54031  * Copyright(c) 2006-2007, Ext JS, LLC.
54032  *
54033  * Originally Released Under LGPL - original licence link has changed is not relivant.
54034  *
54035  * Fork - LGPL
54036  * <script type="text/javascript">
54037  */
54038  
54039 /**
54040  * @class Roo.BasicLayoutRegion
54041  * @extends Roo.util.Observable
54042  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
54043  * and does not have a titlebar, tabs or any other features. All it does is size and position 
54044  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
54045  */
54046 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
54047     this.mgr = mgr;
54048     this.position  = pos;
54049     this.events = {
54050         /**
54051          * @scope Roo.BasicLayoutRegion
54052          */
54053         
54054         /**
54055          * @event beforeremove
54056          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
54057          * @param {Roo.LayoutRegion} this
54058          * @param {Roo.ContentPanel} panel The panel
54059          * @param {Object} e The cancel event object
54060          */
54061         "beforeremove" : true,
54062         /**
54063          * @event invalidated
54064          * Fires when the layout for this region is changed.
54065          * @param {Roo.LayoutRegion} this
54066          */
54067         "invalidated" : true,
54068         /**
54069          * @event visibilitychange
54070          * Fires when this region is shown or hidden 
54071          * @param {Roo.LayoutRegion} this
54072          * @param {Boolean} visibility true or false
54073          */
54074         "visibilitychange" : true,
54075         /**
54076          * @event paneladded
54077          * Fires when a panel is added. 
54078          * @param {Roo.LayoutRegion} this
54079          * @param {Roo.ContentPanel} panel The panel
54080          */
54081         "paneladded" : true,
54082         /**
54083          * @event panelremoved
54084          * Fires when a panel is removed. 
54085          * @param {Roo.LayoutRegion} this
54086          * @param {Roo.ContentPanel} panel The panel
54087          */
54088         "panelremoved" : true,
54089         /**
54090          * @event beforecollapse
54091          * Fires when this region before collapse.
54092          * @param {Roo.LayoutRegion} this
54093          */
54094         "beforecollapse" : true,
54095         /**
54096          * @event collapsed
54097          * Fires when this region is collapsed.
54098          * @param {Roo.LayoutRegion} this
54099          */
54100         "collapsed" : true,
54101         /**
54102          * @event expanded
54103          * Fires when this region is expanded.
54104          * @param {Roo.LayoutRegion} this
54105          */
54106         "expanded" : true,
54107         /**
54108          * @event slideshow
54109          * Fires when this region is slid into view.
54110          * @param {Roo.LayoutRegion} this
54111          */
54112         "slideshow" : true,
54113         /**
54114          * @event slidehide
54115          * Fires when this region slides out of view. 
54116          * @param {Roo.LayoutRegion} this
54117          */
54118         "slidehide" : true,
54119         /**
54120          * @event panelactivated
54121          * Fires when a panel is activated. 
54122          * @param {Roo.LayoutRegion} this
54123          * @param {Roo.ContentPanel} panel The activated panel
54124          */
54125         "panelactivated" : true,
54126         /**
54127          * @event resized
54128          * Fires when the user resizes this region. 
54129          * @param {Roo.LayoutRegion} this
54130          * @param {Number} newSize The new size (width for east/west, height for north/south)
54131          */
54132         "resized" : true
54133     };
54134     /** A collection of panels in this region. @type Roo.util.MixedCollection */
54135     this.panels = new Roo.util.MixedCollection();
54136     this.panels.getKey = this.getPanelId.createDelegate(this);
54137     this.box = null;
54138     this.activePanel = null;
54139     // ensure listeners are added...
54140     
54141     if (config.listeners || config.events) {
54142         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
54143             listeners : config.listeners || {},
54144             events : config.events || {}
54145         });
54146     }
54147     
54148     if(skipConfig !== true){
54149         this.applyConfig(config);
54150     }
54151 };
54152
54153 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
54154     getPanelId : function(p){
54155         return p.getId();
54156     },
54157     
54158     applyConfig : function(config){
54159         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54160         this.config = config;
54161         
54162     },
54163     
54164     /**
54165      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
54166      * the width, for horizontal (north, south) the height.
54167      * @param {Number} newSize The new width or height
54168      */
54169     resizeTo : function(newSize){
54170         var el = this.el ? this.el :
54171                  (this.activePanel ? this.activePanel.getEl() : null);
54172         if(el){
54173             switch(this.position){
54174                 case "east":
54175                 case "west":
54176                     el.setWidth(newSize);
54177                     this.fireEvent("resized", this, newSize);
54178                 break;
54179                 case "north":
54180                 case "south":
54181                     el.setHeight(newSize);
54182                     this.fireEvent("resized", this, newSize);
54183                 break;                
54184             }
54185         }
54186     },
54187     
54188     getBox : function(){
54189         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
54190     },
54191     
54192     getMargins : function(){
54193         return this.margins;
54194     },
54195     
54196     updateBox : function(box){
54197         this.box = box;
54198         var el = this.activePanel.getEl();
54199         el.dom.style.left = box.x + "px";
54200         el.dom.style.top = box.y + "px";
54201         this.activePanel.setSize(box.width, box.height);
54202     },
54203     
54204     /**
54205      * Returns the container element for this region.
54206      * @return {Roo.Element}
54207      */
54208     getEl : function(){
54209         return this.activePanel;
54210     },
54211     
54212     /**
54213      * Returns true if this region is currently visible.
54214      * @return {Boolean}
54215      */
54216     isVisible : function(){
54217         return this.activePanel ? true : false;
54218     },
54219     
54220     setActivePanel : function(panel){
54221         panel = this.getPanel(panel);
54222         if(this.activePanel && this.activePanel != panel){
54223             this.activePanel.setActiveState(false);
54224             this.activePanel.getEl().setLeftTop(-10000,-10000);
54225         }
54226         this.activePanel = panel;
54227         panel.setActiveState(true);
54228         if(this.box){
54229             panel.setSize(this.box.width, this.box.height);
54230         }
54231         this.fireEvent("panelactivated", this, panel);
54232         this.fireEvent("invalidated");
54233     },
54234     
54235     /**
54236      * Show the specified panel.
54237      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
54238      * @return {Roo.ContentPanel} The shown panel or null
54239      */
54240     showPanel : function(panel){
54241         if(panel = this.getPanel(panel)){
54242             this.setActivePanel(panel);
54243         }
54244         return panel;
54245     },
54246     
54247     /**
54248      * Get the active panel for this region.
54249      * @return {Roo.ContentPanel} The active panel or null
54250      */
54251     getActivePanel : function(){
54252         return this.activePanel;
54253     },
54254     
54255     /**
54256      * Add the passed ContentPanel(s)
54257      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54258      * @return {Roo.ContentPanel} The panel added (if only one was added)
54259      */
54260     add : function(panel){
54261         if(arguments.length > 1){
54262             for(var i = 0, len = arguments.length; i < len; i++) {
54263                 this.add(arguments[i]);
54264             }
54265             return null;
54266         }
54267         if(this.hasPanel(panel)){
54268             this.showPanel(panel);
54269             return panel;
54270         }
54271         var el = panel.getEl();
54272         if(el.dom.parentNode != this.mgr.el.dom){
54273             this.mgr.el.dom.appendChild(el.dom);
54274         }
54275         if(panel.setRegion){
54276             panel.setRegion(this);
54277         }
54278         this.panels.add(panel);
54279         el.setStyle("position", "absolute");
54280         if(!panel.background){
54281             this.setActivePanel(panel);
54282             if(this.config.initialSize && this.panels.getCount()==1){
54283                 this.resizeTo(this.config.initialSize);
54284             }
54285         }
54286         this.fireEvent("paneladded", this, panel);
54287         return panel;
54288     },
54289     
54290     /**
54291      * Returns true if the panel is in this region.
54292      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54293      * @return {Boolean}
54294      */
54295     hasPanel : function(panel){
54296         if(typeof panel == "object"){ // must be panel obj
54297             panel = panel.getId();
54298         }
54299         return this.getPanel(panel) ? true : false;
54300     },
54301     
54302     /**
54303      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54304      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54305      * @param {Boolean} preservePanel Overrides the config preservePanel option
54306      * @return {Roo.ContentPanel} The panel that was removed
54307      */
54308     remove : function(panel, preservePanel){
54309         panel = this.getPanel(panel);
54310         if(!panel){
54311             return null;
54312         }
54313         var e = {};
54314         this.fireEvent("beforeremove", this, panel, e);
54315         if(e.cancel === true){
54316             return null;
54317         }
54318         var panelId = panel.getId();
54319         this.panels.removeKey(panelId);
54320         return panel;
54321     },
54322     
54323     /**
54324      * Returns the panel specified or null if it's not in this region.
54325      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54326      * @return {Roo.ContentPanel}
54327      */
54328     getPanel : function(id){
54329         if(typeof id == "object"){ // must be panel obj
54330             return id;
54331         }
54332         return this.panels.get(id);
54333     },
54334     
54335     /**
54336      * Returns this regions position (north/south/east/west/center).
54337      * @return {String} 
54338      */
54339     getPosition: function(){
54340         return this.position;    
54341     }
54342 });/*
54343  * Based on:
54344  * Ext JS Library 1.1.1
54345  * Copyright(c) 2006-2007, Ext JS, LLC.
54346  *
54347  * Originally Released Under LGPL - original licence link has changed is not relivant.
54348  *
54349  * Fork - LGPL
54350  * <script type="text/javascript">
54351  */
54352  
54353 /**
54354  * @class Roo.LayoutRegion
54355  * @extends Roo.BasicLayoutRegion
54356  * This class represents a region in a layout manager.
54357  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
54358  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
54359  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
54360  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
54361  * @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})
54362  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
54363  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
54364  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
54365  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
54366  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
54367  * @cfg {String}    title           The title for the region (overrides panel titles)
54368  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
54369  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
54370  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
54371  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
54372  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
54373  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
54374  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
54375  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
54376  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
54377  * @cfg {Boolean}   showPin         True to show a pin button
54378  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
54379  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
54380  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
54381  * @cfg {Number}    width           For East/West panels
54382  * @cfg {Number}    height          For North/South panels
54383  * @cfg {Boolean}   split           To show the splitter
54384  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
54385  */
54386 Roo.LayoutRegion = function(mgr, config, pos){
54387     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
54388     var dh = Roo.DomHelper;
54389     /** This region's container element 
54390     * @type Roo.Element */
54391     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
54392     /** This region's title element 
54393     * @type Roo.Element */
54394
54395     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
54396         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
54397         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
54398     ]}, true);
54399     this.titleEl.enableDisplayMode();
54400     /** This region's title text element 
54401     * @type HTMLElement */
54402     this.titleTextEl = this.titleEl.dom.firstChild;
54403     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
54404     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
54405     this.closeBtn.enableDisplayMode();
54406     this.closeBtn.on("click", this.closeClicked, this);
54407     this.closeBtn.hide();
54408
54409     this.createBody(config);
54410     this.visible = true;
54411     this.collapsed = false;
54412
54413     if(config.hideWhenEmpty){
54414         this.hide();
54415         this.on("paneladded", this.validateVisibility, this);
54416         this.on("panelremoved", this.validateVisibility, this);
54417     }
54418     this.applyConfig(config);
54419 };
54420
54421 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
54422
54423     createBody : function(){
54424         /** This region's body element 
54425         * @type Roo.Element */
54426         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
54427     },
54428
54429     applyConfig : function(c){
54430         if(c.collapsible && this.position != "center" && !this.collapsedEl){
54431             var dh = Roo.DomHelper;
54432             if(c.titlebar !== false){
54433                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
54434                 this.collapseBtn.on("click", this.collapse, this);
54435                 this.collapseBtn.enableDisplayMode();
54436
54437                 if(c.showPin === true || this.showPin){
54438                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
54439                     this.stickBtn.enableDisplayMode();
54440                     this.stickBtn.on("click", this.expand, this);
54441                     this.stickBtn.hide();
54442                 }
54443             }
54444             /** This region's collapsed element
54445             * @type Roo.Element */
54446             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
54447                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
54448             ]}, true);
54449             if(c.floatable !== false){
54450                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
54451                this.collapsedEl.on("click", this.collapseClick, this);
54452             }
54453
54454             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
54455                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
54456                    id: "message", unselectable: "on", style:{"float":"left"}});
54457                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
54458              }
54459             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
54460             this.expandBtn.on("click", this.expand, this);
54461         }
54462         if(this.collapseBtn){
54463             this.collapseBtn.setVisible(c.collapsible == true);
54464         }
54465         this.cmargins = c.cmargins || this.cmargins ||
54466                          (this.position == "west" || this.position == "east" ?
54467                              {top: 0, left: 2, right:2, bottom: 0} :
54468                              {top: 2, left: 0, right:0, bottom: 2});
54469         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54470         this.bottomTabs = c.tabPosition != "top";
54471         this.autoScroll = c.autoScroll || false;
54472         if(this.autoScroll){
54473             this.bodyEl.setStyle("overflow", "auto");
54474         }else{
54475             this.bodyEl.setStyle("overflow", "hidden");
54476         }
54477         //if(c.titlebar !== false){
54478             if((!c.titlebar && !c.title) || c.titlebar === false){
54479                 this.titleEl.hide();
54480             }else{
54481                 this.titleEl.show();
54482                 if(c.title){
54483                     this.titleTextEl.innerHTML = c.title;
54484                 }
54485             }
54486         //}
54487         this.duration = c.duration || .30;
54488         this.slideDuration = c.slideDuration || .45;
54489         this.config = c;
54490         if(c.collapsed){
54491             this.collapse(true);
54492         }
54493         if(c.hidden){
54494             this.hide();
54495         }
54496     },
54497     /**
54498      * Returns true if this region is currently visible.
54499      * @return {Boolean}
54500      */
54501     isVisible : function(){
54502         return this.visible;
54503     },
54504
54505     /**
54506      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
54507      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
54508      */
54509     setCollapsedTitle : function(title){
54510         title = title || "&#160;";
54511         if(this.collapsedTitleTextEl){
54512             this.collapsedTitleTextEl.innerHTML = title;
54513         }
54514     },
54515
54516     getBox : function(){
54517         var b;
54518         if(!this.collapsed){
54519             b = this.el.getBox(false, true);
54520         }else{
54521             b = this.collapsedEl.getBox(false, true);
54522         }
54523         return b;
54524     },
54525
54526     getMargins : function(){
54527         return this.collapsed ? this.cmargins : this.margins;
54528     },
54529
54530     highlight : function(){
54531         this.el.addClass("x-layout-panel-dragover");
54532     },
54533
54534     unhighlight : function(){
54535         this.el.removeClass("x-layout-panel-dragover");
54536     },
54537
54538     updateBox : function(box){
54539         this.box = box;
54540         if(!this.collapsed){
54541             this.el.dom.style.left = box.x + "px";
54542             this.el.dom.style.top = box.y + "px";
54543             this.updateBody(box.width, box.height);
54544         }else{
54545             this.collapsedEl.dom.style.left = box.x + "px";
54546             this.collapsedEl.dom.style.top = box.y + "px";
54547             this.collapsedEl.setSize(box.width, box.height);
54548         }
54549         if(this.tabs){
54550             this.tabs.autoSizeTabs();
54551         }
54552     },
54553
54554     updateBody : function(w, h){
54555         if(w !== null){
54556             this.el.setWidth(w);
54557             w -= this.el.getBorderWidth("rl");
54558             if(this.config.adjustments){
54559                 w += this.config.adjustments[0];
54560             }
54561         }
54562         if(h !== null){
54563             this.el.setHeight(h);
54564             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
54565             h -= this.el.getBorderWidth("tb");
54566             if(this.config.adjustments){
54567                 h += this.config.adjustments[1];
54568             }
54569             this.bodyEl.setHeight(h);
54570             if(this.tabs){
54571                 h = this.tabs.syncHeight(h);
54572             }
54573         }
54574         if(this.panelSize){
54575             w = w !== null ? w : this.panelSize.width;
54576             h = h !== null ? h : this.panelSize.height;
54577         }
54578         if(this.activePanel){
54579             var el = this.activePanel.getEl();
54580             w = w !== null ? w : el.getWidth();
54581             h = h !== null ? h : el.getHeight();
54582             this.panelSize = {width: w, height: h};
54583             this.activePanel.setSize(w, h);
54584         }
54585         if(Roo.isIE && this.tabs){
54586             this.tabs.el.repaint();
54587         }
54588     },
54589
54590     /**
54591      * Returns the container element for this region.
54592      * @return {Roo.Element}
54593      */
54594     getEl : function(){
54595         return this.el;
54596     },
54597
54598     /**
54599      * Hides this region.
54600      */
54601     hide : function(){
54602         if(!this.collapsed){
54603             this.el.dom.style.left = "-2000px";
54604             this.el.hide();
54605         }else{
54606             this.collapsedEl.dom.style.left = "-2000px";
54607             this.collapsedEl.hide();
54608         }
54609         this.visible = false;
54610         this.fireEvent("visibilitychange", this, false);
54611     },
54612
54613     /**
54614      * Shows this region if it was previously hidden.
54615      */
54616     show : function(){
54617         if(!this.collapsed){
54618             this.el.show();
54619         }else{
54620             this.collapsedEl.show();
54621         }
54622         this.visible = true;
54623         this.fireEvent("visibilitychange", this, true);
54624     },
54625
54626     closeClicked : function(){
54627         if(this.activePanel){
54628             this.remove(this.activePanel);
54629         }
54630     },
54631
54632     collapseClick : function(e){
54633         if(this.isSlid){
54634            e.stopPropagation();
54635            this.slideIn();
54636         }else{
54637            e.stopPropagation();
54638            this.slideOut();
54639         }
54640     },
54641
54642     /**
54643      * Collapses this region.
54644      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
54645      */
54646     collapse : function(skipAnim, skipCheck){
54647         if(this.collapsed) {
54648             return;
54649         }
54650         
54651         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
54652             
54653             this.collapsed = true;
54654             if(this.split){
54655                 this.split.el.hide();
54656             }
54657             if(this.config.animate && skipAnim !== true){
54658                 this.fireEvent("invalidated", this);
54659                 this.animateCollapse();
54660             }else{
54661                 this.el.setLocation(-20000,-20000);
54662                 this.el.hide();
54663                 this.collapsedEl.show();
54664                 this.fireEvent("collapsed", this);
54665                 this.fireEvent("invalidated", this);
54666             }
54667         }
54668         
54669     },
54670
54671     animateCollapse : function(){
54672         // overridden
54673     },
54674
54675     /**
54676      * Expands this region if it was previously collapsed.
54677      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
54678      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
54679      */
54680     expand : function(e, skipAnim){
54681         if(e) {
54682             e.stopPropagation();
54683         }
54684         if(!this.collapsed || this.el.hasActiveFx()) {
54685             return;
54686         }
54687         if(this.isSlid){
54688             this.afterSlideIn();
54689             skipAnim = true;
54690         }
54691         this.collapsed = false;
54692         if(this.config.animate && skipAnim !== true){
54693             this.animateExpand();
54694         }else{
54695             this.el.show();
54696             if(this.split){
54697                 this.split.el.show();
54698             }
54699             this.collapsedEl.setLocation(-2000,-2000);
54700             this.collapsedEl.hide();
54701             this.fireEvent("invalidated", this);
54702             this.fireEvent("expanded", this);
54703         }
54704     },
54705
54706     animateExpand : function(){
54707         // overridden
54708     },
54709
54710     initTabs : function()
54711     {
54712         this.bodyEl.setStyle("overflow", "hidden");
54713         var ts = new Roo.TabPanel(
54714                 this.bodyEl.dom,
54715                 {
54716                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
54717                     disableTooltips: this.config.disableTabTips,
54718                     toolbar : this.config.toolbar
54719                 }
54720         );
54721         if(this.config.hideTabs){
54722             ts.stripWrap.setDisplayed(false);
54723         }
54724         this.tabs = ts;
54725         ts.resizeTabs = this.config.resizeTabs === true;
54726         ts.minTabWidth = this.config.minTabWidth || 40;
54727         ts.maxTabWidth = this.config.maxTabWidth || 250;
54728         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
54729         ts.monitorResize = false;
54730         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54731         ts.bodyEl.addClass('x-layout-tabs-body');
54732         this.panels.each(this.initPanelAsTab, this);
54733     },
54734
54735     initPanelAsTab : function(panel){
54736         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
54737                     this.config.closeOnTab && panel.isClosable());
54738         if(panel.tabTip !== undefined){
54739             ti.setTooltip(panel.tabTip);
54740         }
54741         ti.on("activate", function(){
54742               this.setActivePanel(panel);
54743         }, this);
54744         if(this.config.closeOnTab){
54745             ti.on("beforeclose", function(t, e){
54746                 e.cancel = true;
54747                 this.remove(panel);
54748             }, this);
54749         }
54750         return ti;
54751     },
54752
54753     updatePanelTitle : function(panel, title){
54754         if(this.activePanel == panel){
54755             this.updateTitle(title);
54756         }
54757         if(this.tabs){
54758             var ti = this.tabs.getTab(panel.getEl().id);
54759             ti.setText(title);
54760             if(panel.tabTip !== undefined){
54761                 ti.setTooltip(panel.tabTip);
54762             }
54763         }
54764     },
54765
54766     updateTitle : function(title){
54767         if(this.titleTextEl && !this.config.title){
54768             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
54769         }
54770     },
54771
54772     setActivePanel : function(panel){
54773         panel = this.getPanel(panel);
54774         if(this.activePanel && this.activePanel != panel){
54775             this.activePanel.setActiveState(false);
54776         }
54777         this.activePanel = panel;
54778         panel.setActiveState(true);
54779         if(this.panelSize){
54780             panel.setSize(this.panelSize.width, this.panelSize.height);
54781         }
54782         if(this.closeBtn){
54783             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
54784         }
54785         this.updateTitle(panel.getTitle());
54786         if(this.tabs){
54787             this.fireEvent("invalidated", this);
54788         }
54789         this.fireEvent("panelactivated", this, panel);
54790     },
54791
54792     /**
54793      * Shows the specified panel.
54794      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
54795      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
54796      */
54797     showPanel : function(panel)
54798     {
54799         panel = this.getPanel(panel);
54800         if(panel){
54801             if(this.tabs){
54802                 var tab = this.tabs.getTab(panel.getEl().id);
54803                 if(tab.isHidden()){
54804                     this.tabs.unhideTab(tab.id);
54805                 }
54806                 tab.activate();
54807             }else{
54808                 this.setActivePanel(panel);
54809             }
54810         }
54811         return panel;
54812     },
54813
54814     /**
54815      * Get the active panel for this region.
54816      * @return {Roo.ContentPanel} The active panel or null
54817      */
54818     getActivePanel : function(){
54819         return this.activePanel;
54820     },
54821
54822     validateVisibility : function(){
54823         if(this.panels.getCount() < 1){
54824             this.updateTitle("&#160;");
54825             this.closeBtn.hide();
54826             this.hide();
54827         }else{
54828             if(!this.isVisible()){
54829                 this.show();
54830             }
54831         }
54832     },
54833
54834     /**
54835      * Adds the passed ContentPanel(s) to this region.
54836      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54837      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
54838      */
54839     add : function(panel){
54840         if(arguments.length > 1){
54841             for(var i = 0, len = arguments.length; i < len; i++) {
54842                 this.add(arguments[i]);
54843             }
54844             return null;
54845         }
54846         if(this.hasPanel(panel)){
54847             this.showPanel(panel);
54848             return panel;
54849         }
54850         panel.setRegion(this);
54851         this.panels.add(panel);
54852         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
54853             this.bodyEl.dom.appendChild(panel.getEl().dom);
54854             if(panel.background !== true){
54855                 this.setActivePanel(panel);
54856             }
54857             this.fireEvent("paneladded", this, panel);
54858             return panel;
54859         }
54860         if(!this.tabs){
54861             this.initTabs();
54862         }else{
54863             this.initPanelAsTab(panel);
54864         }
54865         if(panel.background !== true){
54866             this.tabs.activate(panel.getEl().id);
54867         }
54868         this.fireEvent("paneladded", this, panel);
54869         return panel;
54870     },
54871
54872     /**
54873      * Hides the tab for the specified panel.
54874      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54875      */
54876     hidePanel : function(panel){
54877         if(this.tabs && (panel = this.getPanel(panel))){
54878             this.tabs.hideTab(panel.getEl().id);
54879         }
54880     },
54881
54882     /**
54883      * Unhides the tab for a previously hidden panel.
54884      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54885      */
54886     unhidePanel : function(panel){
54887         if(this.tabs && (panel = this.getPanel(panel))){
54888             this.tabs.unhideTab(panel.getEl().id);
54889         }
54890     },
54891
54892     clearPanels : function(){
54893         while(this.panels.getCount() > 0){
54894              this.remove(this.panels.first());
54895         }
54896     },
54897
54898     /**
54899      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54900      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
54901      * @param {Boolean} preservePanel Overrides the config preservePanel option
54902      * @return {Roo.ContentPanel} The panel that was removed
54903      */
54904     remove : function(panel, preservePanel){
54905         panel = this.getPanel(panel);
54906         if(!panel){
54907             return null;
54908         }
54909         var e = {};
54910         this.fireEvent("beforeremove", this, panel, e);
54911         if(e.cancel === true){
54912             return null;
54913         }
54914         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
54915         var panelId = panel.getId();
54916         this.panels.removeKey(panelId);
54917         if(preservePanel){
54918             document.body.appendChild(panel.getEl().dom);
54919         }
54920         if(this.tabs){
54921             this.tabs.removeTab(panel.getEl().id);
54922         }else if (!preservePanel){
54923             this.bodyEl.dom.removeChild(panel.getEl().dom);
54924         }
54925         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
54926             var p = this.panels.first();
54927             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
54928             tempEl.appendChild(p.getEl().dom);
54929             this.bodyEl.update("");
54930             this.bodyEl.dom.appendChild(p.getEl().dom);
54931             tempEl = null;
54932             this.updateTitle(p.getTitle());
54933             this.tabs = null;
54934             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
54935             this.setActivePanel(p);
54936         }
54937         panel.setRegion(null);
54938         if(this.activePanel == panel){
54939             this.activePanel = null;
54940         }
54941         if(this.config.autoDestroy !== false && preservePanel !== true){
54942             try{panel.destroy();}catch(e){}
54943         }
54944         this.fireEvent("panelremoved", this, panel);
54945         return panel;
54946     },
54947
54948     /**
54949      * Returns the TabPanel component used by this region
54950      * @return {Roo.TabPanel}
54951      */
54952     getTabs : function(){
54953         return this.tabs;
54954     },
54955
54956     createTool : function(parentEl, className){
54957         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
54958             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
54959         btn.addClassOnOver("x-layout-tools-button-over");
54960         return btn;
54961     }
54962 });/*
54963  * Based on:
54964  * Ext JS Library 1.1.1
54965  * Copyright(c) 2006-2007, Ext JS, LLC.
54966  *
54967  * Originally Released Under LGPL - original licence link has changed is not relivant.
54968  *
54969  * Fork - LGPL
54970  * <script type="text/javascript">
54971  */
54972  
54973
54974
54975 /**
54976  * @class Roo.SplitLayoutRegion
54977  * @extends Roo.LayoutRegion
54978  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
54979  */
54980 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
54981     this.cursor = cursor;
54982     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
54983 };
54984
54985 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
54986     splitTip : "Drag to resize.",
54987     collapsibleSplitTip : "Drag to resize. Double click to hide.",
54988     useSplitTips : false,
54989
54990     applyConfig : function(config){
54991         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
54992         if(config.split){
54993             if(!this.split){
54994                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
54995                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
54996                 /** The SplitBar for this region 
54997                 * @type Roo.SplitBar */
54998                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
54999                 this.split.on("moved", this.onSplitMove, this);
55000                 this.split.useShim = config.useShim === true;
55001                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
55002                 if(this.useSplitTips){
55003                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
55004                 }
55005                 if(config.collapsible){
55006                     this.split.el.on("dblclick", this.collapse,  this);
55007                 }
55008             }
55009             if(typeof config.minSize != "undefined"){
55010                 this.split.minSize = config.minSize;
55011             }
55012             if(typeof config.maxSize != "undefined"){
55013                 this.split.maxSize = config.maxSize;
55014             }
55015             if(config.hideWhenEmpty || config.hidden || config.collapsed){
55016                 this.hideSplitter();
55017             }
55018         }
55019     },
55020
55021     getHMaxSize : function(){
55022          var cmax = this.config.maxSize || 10000;
55023          var center = this.mgr.getRegion("center");
55024          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
55025     },
55026
55027     getVMaxSize : function(){
55028          var cmax = this.config.maxSize || 10000;
55029          var center = this.mgr.getRegion("center");
55030          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
55031     },
55032
55033     onSplitMove : function(split, newSize){
55034         this.fireEvent("resized", this, newSize);
55035     },
55036     
55037     /** 
55038      * Returns the {@link Roo.SplitBar} for this region.
55039      * @return {Roo.SplitBar}
55040      */
55041     getSplitBar : function(){
55042         return this.split;
55043     },
55044     
55045     hide : function(){
55046         this.hideSplitter();
55047         Roo.SplitLayoutRegion.superclass.hide.call(this);
55048     },
55049
55050     hideSplitter : function(){
55051         if(this.split){
55052             this.split.el.setLocation(-2000,-2000);
55053             this.split.el.hide();
55054         }
55055     },
55056
55057     show : function(){
55058         if(this.split){
55059             this.split.el.show();
55060         }
55061         Roo.SplitLayoutRegion.superclass.show.call(this);
55062     },
55063     
55064     beforeSlide: function(){
55065         if(Roo.isGecko){// firefox overflow auto bug workaround
55066             this.bodyEl.clip();
55067             if(this.tabs) {
55068                 this.tabs.bodyEl.clip();
55069             }
55070             if(this.activePanel){
55071                 this.activePanel.getEl().clip();
55072                 
55073                 if(this.activePanel.beforeSlide){
55074                     this.activePanel.beforeSlide();
55075                 }
55076             }
55077         }
55078     },
55079     
55080     afterSlide : function(){
55081         if(Roo.isGecko){// firefox overflow auto bug workaround
55082             this.bodyEl.unclip();
55083             if(this.tabs) {
55084                 this.tabs.bodyEl.unclip();
55085             }
55086             if(this.activePanel){
55087                 this.activePanel.getEl().unclip();
55088                 if(this.activePanel.afterSlide){
55089                     this.activePanel.afterSlide();
55090                 }
55091             }
55092         }
55093     },
55094
55095     initAutoHide : function(){
55096         if(this.autoHide !== false){
55097             if(!this.autoHideHd){
55098                 var st = new Roo.util.DelayedTask(this.slideIn, this);
55099                 this.autoHideHd = {
55100                     "mouseout": function(e){
55101                         if(!e.within(this.el, true)){
55102                             st.delay(500);
55103                         }
55104                     },
55105                     "mouseover" : function(e){
55106                         st.cancel();
55107                     },
55108                     scope : this
55109                 };
55110             }
55111             this.el.on(this.autoHideHd);
55112         }
55113     },
55114
55115     clearAutoHide : function(){
55116         if(this.autoHide !== false){
55117             this.el.un("mouseout", this.autoHideHd.mouseout);
55118             this.el.un("mouseover", this.autoHideHd.mouseover);
55119         }
55120     },
55121
55122     clearMonitor : function(){
55123         Roo.get(document).un("click", this.slideInIf, this);
55124     },
55125
55126     // these names are backwards but not changed for compat
55127     slideOut : function(){
55128         if(this.isSlid || this.el.hasActiveFx()){
55129             return;
55130         }
55131         this.isSlid = true;
55132         if(this.collapseBtn){
55133             this.collapseBtn.hide();
55134         }
55135         this.closeBtnState = this.closeBtn.getStyle('display');
55136         this.closeBtn.hide();
55137         if(this.stickBtn){
55138             this.stickBtn.show();
55139         }
55140         this.el.show();
55141         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
55142         this.beforeSlide();
55143         this.el.setStyle("z-index", 10001);
55144         this.el.slideIn(this.getSlideAnchor(), {
55145             callback: function(){
55146                 this.afterSlide();
55147                 this.initAutoHide();
55148                 Roo.get(document).on("click", this.slideInIf, this);
55149                 this.fireEvent("slideshow", this);
55150             },
55151             scope: this,
55152             block: true
55153         });
55154     },
55155
55156     afterSlideIn : function(){
55157         this.clearAutoHide();
55158         this.isSlid = false;
55159         this.clearMonitor();
55160         this.el.setStyle("z-index", "");
55161         if(this.collapseBtn){
55162             this.collapseBtn.show();
55163         }
55164         this.closeBtn.setStyle('display', this.closeBtnState);
55165         if(this.stickBtn){
55166             this.stickBtn.hide();
55167         }
55168         this.fireEvent("slidehide", this);
55169     },
55170
55171     slideIn : function(cb){
55172         if(!this.isSlid || this.el.hasActiveFx()){
55173             Roo.callback(cb);
55174             return;
55175         }
55176         this.isSlid = false;
55177         this.beforeSlide();
55178         this.el.slideOut(this.getSlideAnchor(), {
55179             callback: function(){
55180                 this.el.setLeftTop(-10000, -10000);
55181                 this.afterSlide();
55182                 this.afterSlideIn();
55183                 Roo.callback(cb);
55184             },
55185             scope: this,
55186             block: true
55187         });
55188     },
55189     
55190     slideInIf : function(e){
55191         if(!e.within(this.el)){
55192             this.slideIn();
55193         }
55194     },
55195
55196     animateCollapse : function(){
55197         this.beforeSlide();
55198         this.el.setStyle("z-index", 20000);
55199         var anchor = this.getSlideAnchor();
55200         this.el.slideOut(anchor, {
55201             callback : function(){
55202                 this.el.setStyle("z-index", "");
55203                 this.collapsedEl.slideIn(anchor, {duration:.3});
55204                 this.afterSlide();
55205                 this.el.setLocation(-10000,-10000);
55206                 this.el.hide();
55207                 this.fireEvent("collapsed", this);
55208             },
55209             scope: this,
55210             block: true
55211         });
55212     },
55213
55214     animateExpand : function(){
55215         this.beforeSlide();
55216         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
55217         this.el.setStyle("z-index", 20000);
55218         this.collapsedEl.hide({
55219             duration:.1
55220         });
55221         this.el.slideIn(this.getSlideAnchor(), {
55222             callback : function(){
55223                 this.el.setStyle("z-index", "");
55224                 this.afterSlide();
55225                 if(this.split){
55226                     this.split.el.show();
55227                 }
55228                 this.fireEvent("invalidated", this);
55229                 this.fireEvent("expanded", this);
55230             },
55231             scope: this,
55232             block: true
55233         });
55234     },
55235
55236     anchors : {
55237         "west" : "left",
55238         "east" : "right",
55239         "north" : "top",
55240         "south" : "bottom"
55241     },
55242
55243     sanchors : {
55244         "west" : "l",
55245         "east" : "r",
55246         "north" : "t",
55247         "south" : "b"
55248     },
55249
55250     canchors : {
55251         "west" : "tl-tr",
55252         "east" : "tr-tl",
55253         "north" : "tl-bl",
55254         "south" : "bl-tl"
55255     },
55256
55257     getAnchor : function(){
55258         return this.anchors[this.position];
55259     },
55260
55261     getCollapseAnchor : function(){
55262         return this.canchors[this.position];
55263     },
55264
55265     getSlideAnchor : function(){
55266         return this.sanchors[this.position];
55267     },
55268
55269     getAlignAdj : function(){
55270         var cm = this.cmargins;
55271         switch(this.position){
55272             case "west":
55273                 return [0, 0];
55274             break;
55275             case "east":
55276                 return [0, 0];
55277             break;
55278             case "north":
55279                 return [0, 0];
55280             break;
55281             case "south":
55282                 return [0, 0];
55283             break;
55284         }
55285     },
55286
55287     getExpandAdj : function(){
55288         var c = this.collapsedEl, cm = this.cmargins;
55289         switch(this.position){
55290             case "west":
55291                 return [-(cm.right+c.getWidth()+cm.left), 0];
55292             break;
55293             case "east":
55294                 return [cm.right+c.getWidth()+cm.left, 0];
55295             break;
55296             case "north":
55297                 return [0, -(cm.top+cm.bottom+c.getHeight())];
55298             break;
55299             case "south":
55300                 return [0, cm.top+cm.bottom+c.getHeight()];
55301             break;
55302         }
55303     }
55304 });/*
55305  * Based on:
55306  * Ext JS Library 1.1.1
55307  * Copyright(c) 2006-2007, Ext JS, LLC.
55308  *
55309  * Originally Released Under LGPL - original licence link has changed is not relivant.
55310  *
55311  * Fork - LGPL
55312  * <script type="text/javascript">
55313  */
55314 /*
55315  * These classes are private internal classes
55316  */
55317 Roo.CenterLayoutRegion = function(mgr, config){
55318     Roo.LayoutRegion.call(this, mgr, config, "center");
55319     this.visible = true;
55320     this.minWidth = config.minWidth || 20;
55321     this.minHeight = config.minHeight || 20;
55322 };
55323
55324 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
55325     hide : function(){
55326         // center panel can't be hidden
55327     },
55328     
55329     show : function(){
55330         // center panel can't be hidden
55331     },
55332     
55333     getMinWidth: function(){
55334         return this.minWidth;
55335     },
55336     
55337     getMinHeight: function(){
55338         return this.minHeight;
55339     }
55340 });
55341
55342
55343 Roo.NorthLayoutRegion = function(mgr, config){
55344     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
55345     if(this.split){
55346         this.split.placement = Roo.SplitBar.TOP;
55347         this.split.orientation = Roo.SplitBar.VERTICAL;
55348         this.split.el.addClass("x-layout-split-v");
55349     }
55350     var size = config.initialSize || config.height;
55351     if(typeof size != "undefined"){
55352         this.el.setHeight(size);
55353     }
55354 };
55355 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
55356     orientation: Roo.SplitBar.VERTICAL,
55357     getBox : function(){
55358         if(this.collapsed){
55359             return this.collapsedEl.getBox();
55360         }
55361         var box = this.el.getBox();
55362         if(this.split){
55363             box.height += this.split.el.getHeight();
55364         }
55365         return box;
55366     },
55367     
55368     updateBox : function(box){
55369         if(this.split && !this.collapsed){
55370             box.height -= this.split.el.getHeight();
55371             this.split.el.setLeft(box.x);
55372             this.split.el.setTop(box.y+box.height);
55373             this.split.el.setWidth(box.width);
55374         }
55375         if(this.collapsed){
55376             this.updateBody(box.width, null);
55377         }
55378         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55379     }
55380 });
55381
55382 Roo.SouthLayoutRegion = function(mgr, config){
55383     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
55384     if(this.split){
55385         this.split.placement = Roo.SplitBar.BOTTOM;
55386         this.split.orientation = Roo.SplitBar.VERTICAL;
55387         this.split.el.addClass("x-layout-split-v");
55388     }
55389     var size = config.initialSize || config.height;
55390     if(typeof size != "undefined"){
55391         this.el.setHeight(size);
55392     }
55393 };
55394 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
55395     orientation: Roo.SplitBar.VERTICAL,
55396     getBox : function(){
55397         if(this.collapsed){
55398             return this.collapsedEl.getBox();
55399         }
55400         var box = this.el.getBox();
55401         if(this.split){
55402             var sh = this.split.el.getHeight();
55403             box.height += sh;
55404             box.y -= sh;
55405         }
55406         return box;
55407     },
55408     
55409     updateBox : function(box){
55410         if(this.split && !this.collapsed){
55411             var sh = this.split.el.getHeight();
55412             box.height -= sh;
55413             box.y += sh;
55414             this.split.el.setLeft(box.x);
55415             this.split.el.setTop(box.y-sh);
55416             this.split.el.setWidth(box.width);
55417         }
55418         if(this.collapsed){
55419             this.updateBody(box.width, null);
55420         }
55421         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55422     }
55423 });
55424
55425 Roo.EastLayoutRegion = function(mgr, config){
55426     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
55427     if(this.split){
55428         this.split.placement = Roo.SplitBar.RIGHT;
55429         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55430         this.split.el.addClass("x-layout-split-h");
55431     }
55432     var size = config.initialSize || config.width;
55433     if(typeof size != "undefined"){
55434         this.el.setWidth(size);
55435     }
55436 };
55437 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
55438     orientation: Roo.SplitBar.HORIZONTAL,
55439     getBox : function(){
55440         if(this.collapsed){
55441             return this.collapsedEl.getBox();
55442         }
55443         var box = this.el.getBox();
55444         if(this.split){
55445             var sw = this.split.el.getWidth();
55446             box.width += sw;
55447             box.x -= sw;
55448         }
55449         return box;
55450     },
55451
55452     updateBox : function(box){
55453         if(this.split && !this.collapsed){
55454             var sw = this.split.el.getWidth();
55455             box.width -= sw;
55456             this.split.el.setLeft(box.x);
55457             this.split.el.setTop(box.y);
55458             this.split.el.setHeight(box.height);
55459             box.x += sw;
55460         }
55461         if(this.collapsed){
55462             this.updateBody(null, box.height);
55463         }
55464         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55465     }
55466 });
55467
55468 Roo.WestLayoutRegion = function(mgr, config){
55469     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
55470     if(this.split){
55471         this.split.placement = Roo.SplitBar.LEFT;
55472         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55473         this.split.el.addClass("x-layout-split-h");
55474     }
55475     var size = config.initialSize || config.width;
55476     if(typeof size != "undefined"){
55477         this.el.setWidth(size);
55478     }
55479 };
55480 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
55481     orientation: Roo.SplitBar.HORIZONTAL,
55482     getBox : function(){
55483         if(this.collapsed){
55484             return this.collapsedEl.getBox();
55485         }
55486         var box = this.el.getBox();
55487         if(this.split){
55488             box.width += this.split.el.getWidth();
55489         }
55490         return box;
55491     },
55492     
55493     updateBox : function(box){
55494         if(this.split && !this.collapsed){
55495             var sw = this.split.el.getWidth();
55496             box.width -= sw;
55497             this.split.el.setLeft(box.x+box.width);
55498             this.split.el.setTop(box.y);
55499             this.split.el.setHeight(box.height);
55500         }
55501         if(this.collapsed){
55502             this.updateBody(null, box.height);
55503         }
55504         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55505     }
55506 });
55507 /*
55508  * Based on:
55509  * Ext JS Library 1.1.1
55510  * Copyright(c) 2006-2007, Ext JS, LLC.
55511  *
55512  * Originally Released Under LGPL - original licence link has changed is not relivant.
55513  *
55514  * Fork - LGPL
55515  * <script type="text/javascript">
55516  */
55517  
55518  
55519 /*
55520  * Private internal class for reading and applying state
55521  */
55522 Roo.LayoutStateManager = function(layout){
55523      // default empty state
55524      this.state = {
55525         north: {},
55526         south: {},
55527         east: {},
55528         west: {}       
55529     };
55530 };
55531
55532 Roo.LayoutStateManager.prototype = {
55533     init : function(layout, provider){
55534         this.provider = provider;
55535         var state = provider.get(layout.id+"-layout-state");
55536         if(state){
55537             var wasUpdating = layout.isUpdating();
55538             if(!wasUpdating){
55539                 layout.beginUpdate();
55540             }
55541             for(var key in state){
55542                 if(typeof state[key] != "function"){
55543                     var rstate = state[key];
55544                     var r = layout.getRegion(key);
55545                     if(r && rstate){
55546                         if(rstate.size){
55547                             r.resizeTo(rstate.size);
55548                         }
55549                         if(rstate.collapsed == true){
55550                             r.collapse(true);
55551                         }else{
55552                             r.expand(null, true);
55553                         }
55554                     }
55555                 }
55556             }
55557             if(!wasUpdating){
55558                 layout.endUpdate();
55559             }
55560             this.state = state; 
55561         }
55562         this.layout = layout;
55563         layout.on("regionresized", this.onRegionResized, this);
55564         layout.on("regioncollapsed", this.onRegionCollapsed, this);
55565         layout.on("regionexpanded", this.onRegionExpanded, this);
55566     },
55567     
55568     storeState : function(){
55569         this.provider.set(this.layout.id+"-layout-state", this.state);
55570     },
55571     
55572     onRegionResized : function(region, newSize){
55573         this.state[region.getPosition()].size = newSize;
55574         this.storeState();
55575     },
55576     
55577     onRegionCollapsed : function(region){
55578         this.state[region.getPosition()].collapsed = true;
55579         this.storeState();
55580     },
55581     
55582     onRegionExpanded : function(region){
55583         this.state[region.getPosition()].collapsed = false;
55584         this.storeState();
55585     }
55586 };/*
55587  * Based on:
55588  * Ext JS Library 1.1.1
55589  * Copyright(c) 2006-2007, Ext JS, LLC.
55590  *
55591  * Originally Released Under LGPL - original licence link has changed is not relivant.
55592  *
55593  * Fork - LGPL
55594  * <script type="text/javascript">
55595  */
55596 /**
55597  * @class Roo.ContentPanel
55598  * @extends Roo.util.Observable
55599  * @children Roo.form.Form Roo.JsonView Roo.View
55600  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
55601  * A basic ContentPanel element.
55602  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
55603  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
55604  * @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
55605  * @cfg {Boolean}   closable      True if the panel can be closed/removed
55606  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
55607  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
55608  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
55609  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
55610  * @cfg {String} title          The title for this panel
55611  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
55612  * @cfg {String} url            Calls {@link #setUrl} with this value
55613  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
55614  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
55615  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
55616  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
55617  * @cfg {String}    style  Extra style to add to the content panel
55618  * @cfg {Roo.menu.Menu} menu  popup menu
55619
55620  * @constructor
55621  * Create a new ContentPanel.
55622  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
55623  * @param {String/Object} config A string to set only the title or a config object
55624  * @param {String} content (optional) Set the HTML content for this panel
55625  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
55626  */
55627 Roo.ContentPanel = function(el, config, content){
55628     
55629      
55630     /*
55631     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
55632         config = el;
55633         el = Roo.id();
55634     }
55635     if (config && config.parentLayout) { 
55636         el = config.parentLayout.el.createChild(); 
55637     }
55638     */
55639     if(el.autoCreate){ // xtype is available if this is called from factory
55640         config = el;
55641         el = Roo.id();
55642     }
55643     this.el = Roo.get(el);
55644     if(!this.el && config && config.autoCreate){
55645         if(typeof config.autoCreate == "object"){
55646             if(!config.autoCreate.id){
55647                 config.autoCreate.id = config.id||el;
55648             }
55649             this.el = Roo.DomHelper.append(document.body,
55650                         config.autoCreate, true);
55651         }else{
55652             this.el = Roo.DomHelper.append(document.body,
55653                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
55654         }
55655     }
55656     
55657     
55658     this.closable = false;
55659     this.loaded = false;
55660     this.active = false;
55661     if(typeof config == "string"){
55662         this.title = config;
55663     }else{
55664         Roo.apply(this, config);
55665     }
55666     
55667     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
55668         this.wrapEl = this.el.wrap();
55669         this.toolbar.container = this.el.insertSibling(false, 'before');
55670         this.toolbar = new Roo.Toolbar(this.toolbar);
55671     }
55672     
55673     // xtype created footer. - not sure if will work as we normally have to render first..
55674     if (this.footer && !this.footer.el && this.footer.xtype) {
55675         if (!this.wrapEl) {
55676             this.wrapEl = this.el.wrap();
55677         }
55678     
55679         this.footer.container = this.wrapEl.createChild();
55680          
55681         this.footer = Roo.factory(this.footer, Roo);
55682         
55683     }
55684     
55685     if(this.resizeEl){
55686         this.resizeEl = Roo.get(this.resizeEl, true);
55687     }else{
55688         this.resizeEl = this.el;
55689     }
55690     // handle view.xtype
55691     
55692  
55693     
55694     
55695     this.addEvents({
55696         /**
55697          * @event activate
55698          * Fires when this panel is activated. 
55699          * @param {Roo.ContentPanel} this
55700          */
55701         "activate" : true,
55702         /**
55703          * @event deactivate
55704          * Fires when this panel is activated. 
55705          * @param {Roo.ContentPanel} this
55706          */
55707         "deactivate" : true,
55708
55709         /**
55710          * @event resize
55711          * Fires when this panel is resized if fitToFrame is true.
55712          * @param {Roo.ContentPanel} this
55713          * @param {Number} width The width after any component adjustments
55714          * @param {Number} height The height after any component adjustments
55715          */
55716         "resize" : true,
55717         
55718          /**
55719          * @event render
55720          * Fires when this tab is created
55721          * @param {Roo.ContentPanel} this
55722          */
55723         "render" : true
55724          
55725         
55726     });
55727     
55728
55729     
55730     
55731     if(this.autoScroll){
55732         this.resizeEl.setStyle("overflow", "auto");
55733     } else {
55734         // fix randome scrolling
55735         this.el.on('scroll', function() {
55736             Roo.log('fix random scolling');
55737             this.scrollTo('top',0); 
55738         });
55739     }
55740     content = content || this.content;
55741     if(content){
55742         this.setContent(content);
55743     }
55744     if(config && config.url){
55745         this.setUrl(this.url, this.params, this.loadOnce);
55746     }
55747     
55748     
55749     
55750     Roo.ContentPanel.superclass.constructor.call(this);
55751     
55752     if (this.view && typeof(this.view.xtype) != 'undefined') {
55753         this.view.el = this.el.appendChild(document.createElement("div"));
55754         this.view = Roo.factory(this.view); 
55755         this.view.render  &&  this.view.render(false, '');  
55756     }
55757     
55758     
55759     this.fireEvent('render', this);
55760 };
55761
55762 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
55763     tabTip:'',
55764     setRegion : function(region){
55765         this.region = region;
55766         if(region){
55767            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
55768         }else{
55769            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
55770         } 
55771     },
55772     
55773     /**
55774      * Returns the toolbar for this Panel if one was configured. 
55775      * @return {Roo.Toolbar} 
55776      */
55777     getToolbar : function(){
55778         return this.toolbar;
55779     },
55780     
55781     setActiveState : function(active){
55782         this.active = active;
55783         if(!active){
55784             this.fireEvent("deactivate", this);
55785         }else{
55786             this.fireEvent("activate", this);
55787         }
55788     },
55789     /**
55790      * Updates this panel's element
55791      * @param {String} content The new content
55792      * @param {Boolean} loadScripts (optional) true to look for and process scripts
55793     */
55794     setContent : function(content, loadScripts){
55795         this.el.update(content, loadScripts);
55796     },
55797
55798     ignoreResize : function(w, h){
55799         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
55800             return true;
55801         }else{
55802             this.lastSize = {width: w, height: h};
55803             return false;
55804         }
55805     },
55806     /**
55807      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
55808      * @return {Roo.UpdateManager} The UpdateManager
55809      */
55810     getUpdateManager : function(){
55811         return this.el.getUpdateManager();
55812     },
55813      /**
55814      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
55815      * @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:
55816 <pre><code>
55817 panel.load({
55818     url: "your-url.php",
55819     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
55820     callback: yourFunction,
55821     scope: yourObject, //(optional scope)
55822     discardUrl: false,
55823     nocache: false,
55824     text: "Loading...",
55825     timeout: 30,
55826     scripts: false
55827 });
55828 </code></pre>
55829      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
55830      * 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.
55831      * @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}
55832      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
55833      * @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.
55834      * @return {Roo.ContentPanel} this
55835      */
55836     load : function(){
55837         var um = this.el.getUpdateManager();
55838         um.update.apply(um, arguments);
55839         return this;
55840     },
55841
55842
55843     /**
55844      * 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.
55845      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
55846      * @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)
55847      * @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)
55848      * @return {Roo.UpdateManager} The UpdateManager
55849      */
55850     setUrl : function(url, params, loadOnce){
55851         if(this.refreshDelegate){
55852             this.removeListener("activate", this.refreshDelegate);
55853         }
55854         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
55855         this.on("activate", this.refreshDelegate);
55856         return this.el.getUpdateManager();
55857     },
55858     
55859     _handleRefresh : function(url, params, loadOnce){
55860         if(!loadOnce || !this.loaded){
55861             var updater = this.el.getUpdateManager();
55862             updater.update(url, params, this._setLoaded.createDelegate(this));
55863         }
55864     },
55865     
55866     _setLoaded : function(){
55867         this.loaded = true;
55868     }, 
55869     
55870     /**
55871      * Returns this panel's id
55872      * @return {String} 
55873      */
55874     getId : function(){
55875         return this.el.id;
55876     },
55877     
55878     /** 
55879      * Returns this panel's element - used by regiosn to add.
55880      * @return {Roo.Element} 
55881      */
55882     getEl : function(){
55883         return this.wrapEl || this.el;
55884     },
55885     
55886     adjustForComponents : function(width, height)
55887     {
55888         //Roo.log('adjustForComponents ');
55889         if(this.resizeEl != this.el){
55890             width -= this.el.getFrameWidth('lr');
55891             height -= this.el.getFrameWidth('tb');
55892         }
55893         if(this.toolbar){
55894             var te = this.toolbar.getEl();
55895             height -= te.getHeight();
55896             te.setWidth(width);
55897         }
55898         if(this.footer){
55899             var te = this.footer.getEl();
55900             //Roo.log("footer:" + te.getHeight());
55901             
55902             height -= te.getHeight();
55903             te.setWidth(width);
55904         }
55905         
55906         
55907         if(this.adjustments){
55908             width += this.adjustments[0];
55909             height += this.adjustments[1];
55910         }
55911         return {"width": width, "height": height};
55912     },
55913     
55914     setSize : function(width, height){
55915         if(this.fitToFrame && !this.ignoreResize(width, height)){
55916             if(this.fitContainer && this.resizeEl != this.el){
55917                 this.el.setSize(width, height);
55918             }
55919             var size = this.adjustForComponents(width, height);
55920             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
55921             this.fireEvent('resize', this, size.width, size.height);
55922         }
55923     },
55924     
55925     /**
55926      * Returns this panel's title
55927      * @return {String} 
55928      */
55929     getTitle : function(){
55930         return this.title;
55931     },
55932     
55933     /**
55934      * Set this panel's title
55935      * @param {String} title
55936      */
55937     setTitle : function(title){
55938         this.title = title;
55939         if(this.region){
55940             this.region.updatePanelTitle(this, title);
55941         }
55942     },
55943     
55944     /**
55945      * Returns true is this panel was configured to be closable
55946      * @return {Boolean} 
55947      */
55948     isClosable : function(){
55949         return this.closable;
55950     },
55951     
55952     beforeSlide : function(){
55953         this.el.clip();
55954         this.resizeEl.clip();
55955     },
55956     
55957     afterSlide : function(){
55958         this.el.unclip();
55959         this.resizeEl.unclip();
55960     },
55961     
55962     /**
55963      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
55964      *   Will fail silently if the {@link #setUrl} method has not been called.
55965      *   This does not activate the panel, just updates its content.
55966      */
55967     refresh : function(){
55968         if(this.refreshDelegate){
55969            this.loaded = false;
55970            this.refreshDelegate();
55971         }
55972     },
55973     
55974     /**
55975      * Destroys this panel
55976      */
55977     destroy : function(){
55978         this.el.removeAllListeners();
55979         var tempEl = document.createElement("span");
55980         tempEl.appendChild(this.el.dom);
55981         tempEl.innerHTML = "";
55982         this.el.remove();
55983         this.el = null;
55984     },
55985     
55986     /**
55987      * form - if the content panel contains a form - this is a reference to it.
55988      * @type {Roo.form.Form}
55989      */
55990     form : false,
55991     /**
55992      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
55993      *    This contains a reference to it.
55994      * @type {Roo.View}
55995      */
55996     view : false,
55997     
55998       /**
55999      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
56000      * <pre><code>
56001
56002 layout.addxtype({
56003        xtype : 'Form',
56004        items: [ .... ]
56005    }
56006 );
56007
56008 </code></pre>
56009      * @param {Object} cfg Xtype definition of item to add.
56010      */
56011     
56012     addxtype : function(cfg) {
56013         // add form..
56014         if (cfg.xtype.match(/^Form$/)) {
56015             
56016             var el;
56017             //if (this.footer) {
56018             //    el = this.footer.container.insertSibling(false, 'before');
56019             //} else {
56020                 el = this.el.createChild();
56021             //}
56022
56023             this.form = new  Roo.form.Form(cfg);
56024             
56025             
56026             if ( this.form.allItems.length) {
56027                 this.form.render(el.dom);
56028             }
56029             return this.form;
56030         }
56031         // should only have one of theses..
56032         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
56033             // views.. should not be just added - used named prop 'view''
56034             
56035             cfg.el = this.el.appendChild(document.createElement("div"));
56036             // factory?
56037             
56038             var ret = new Roo.factory(cfg);
56039              
56040              ret.render && ret.render(false, ''); // render blank..
56041             this.view = ret;
56042             return ret;
56043         }
56044         return false;
56045     }
56046 });
56047
56048 /**
56049  * @class Roo.GridPanel
56050  * @extends Roo.ContentPanel
56051  * @constructor
56052  * Create a new GridPanel.
56053  * @param {Roo.grid.Grid} grid The grid for this panel
56054  * @param {String/Object} config A string to set only the panel's title, or a config object
56055  */
56056 Roo.GridPanel = function(grid, config){
56057     
56058   
56059     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
56060         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
56061         
56062     this.wrapper.dom.appendChild(grid.getGridEl().dom);
56063     
56064     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
56065     
56066     if(this.toolbar){
56067         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
56068     }
56069     // xtype created footer. - not sure if will work as we normally have to render first..
56070     if (this.footer && !this.footer.el && this.footer.xtype) {
56071         
56072         this.footer.container = this.grid.getView().getFooterPanel(true);
56073         this.footer.dataSource = this.grid.dataSource;
56074         this.footer = Roo.factory(this.footer, Roo);
56075         
56076     }
56077     
56078     grid.monitorWindowResize = false; // turn off autosizing
56079     grid.autoHeight = false;
56080     grid.autoWidth = false;
56081     this.grid = grid;
56082     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
56083 };
56084
56085 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
56086     getId : function(){
56087         return this.grid.id;
56088     },
56089     
56090     /**
56091      * Returns the grid for this panel
56092      * @return {Roo.grid.Grid} 
56093      */
56094     getGrid : function(){
56095         return this.grid;    
56096     },
56097     
56098     setSize : function(width, height){
56099         if(!this.ignoreResize(width, height)){
56100             var grid = this.grid;
56101             var size = this.adjustForComponents(width, height);
56102             grid.getGridEl().setSize(size.width, size.height);
56103             grid.autoSize();
56104         }
56105     },
56106     
56107     beforeSlide : function(){
56108         this.grid.getView().scroller.clip();
56109     },
56110     
56111     afterSlide : function(){
56112         this.grid.getView().scroller.unclip();
56113     },
56114     
56115     destroy : function(){
56116         this.grid.destroy();
56117         delete this.grid;
56118         Roo.GridPanel.superclass.destroy.call(this); 
56119     }
56120 });
56121
56122
56123 /**
56124  * @class Roo.NestedLayoutPanel
56125  * @extends Roo.ContentPanel
56126  * @constructor
56127  * Create a new NestedLayoutPanel.
56128  * 
56129  * 
56130  * @param {Roo.BorderLayout} layout [required] The layout for this panel
56131  * @param {String/Object} config A string to set only the title or a config object
56132  */
56133 Roo.NestedLayoutPanel = function(layout, config)
56134 {
56135     // construct with only one argument..
56136     /* FIXME - implement nicer consturctors
56137     if (layout.layout) {
56138         config = layout;
56139         layout = config.layout;
56140         delete config.layout;
56141     }
56142     if (layout.xtype && !layout.getEl) {
56143         // then layout needs constructing..
56144         layout = Roo.factory(layout, Roo);
56145     }
56146     */
56147     
56148     
56149     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
56150     
56151     layout.monitorWindowResize = false; // turn off autosizing
56152     this.layout = layout;
56153     this.layout.getEl().addClass("x-layout-nested-layout");
56154     
56155     
56156     
56157     
56158 };
56159
56160 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
56161
56162     setSize : function(width, height){
56163         if(!this.ignoreResize(width, height)){
56164             var size = this.adjustForComponents(width, height);
56165             var el = this.layout.getEl();
56166             el.setSize(size.width, size.height);
56167             var touch = el.dom.offsetWidth;
56168             this.layout.layout();
56169             // ie requires a double layout on the first pass
56170             if(Roo.isIE && !this.initialized){
56171                 this.initialized = true;
56172                 this.layout.layout();
56173             }
56174         }
56175     },
56176     
56177     // activate all subpanels if not currently active..
56178     
56179     setActiveState : function(active){
56180         this.active = active;
56181         if(!active){
56182             this.fireEvent("deactivate", this);
56183             return;
56184         }
56185         
56186         this.fireEvent("activate", this);
56187         // not sure if this should happen before or after..
56188         if (!this.layout) {
56189             return; // should not happen..
56190         }
56191         var reg = false;
56192         for (var r in this.layout.regions) {
56193             reg = this.layout.getRegion(r);
56194             if (reg.getActivePanel()) {
56195                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
56196                 reg.setActivePanel(reg.getActivePanel());
56197                 continue;
56198             }
56199             if (!reg.panels.length) {
56200                 continue;
56201             }
56202             reg.showPanel(reg.getPanel(0));
56203         }
56204         
56205         
56206         
56207         
56208     },
56209     
56210     /**
56211      * Returns the nested BorderLayout for this panel
56212      * @return {Roo.BorderLayout} 
56213      */
56214     getLayout : function(){
56215         return this.layout;
56216     },
56217     
56218      /**
56219      * Adds a xtype elements to the layout of the nested panel
56220      * <pre><code>
56221
56222 panel.addxtype({
56223        xtype : 'ContentPanel',
56224        region: 'west',
56225        items: [ .... ]
56226    }
56227 );
56228
56229 panel.addxtype({
56230         xtype : 'NestedLayoutPanel',
56231         region: 'west',
56232         layout: {
56233            center: { },
56234            west: { }   
56235         },
56236         items : [ ... list of content panels or nested layout panels.. ]
56237    }
56238 );
56239 </code></pre>
56240      * @param {Object} cfg Xtype definition of item to add.
56241      */
56242     addxtype : function(cfg) {
56243         return this.layout.addxtype(cfg);
56244     
56245     }
56246 });
56247
56248 Roo.ScrollPanel = function(el, config, content){
56249     config = config || {};
56250     config.fitToFrame = true;
56251     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
56252     
56253     this.el.dom.style.overflow = "hidden";
56254     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
56255     this.el.removeClass("x-layout-inactive-content");
56256     this.el.on("mousewheel", this.onWheel, this);
56257
56258     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
56259     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
56260     up.unselectable(); down.unselectable();
56261     up.on("click", this.scrollUp, this);
56262     down.on("click", this.scrollDown, this);
56263     up.addClassOnOver("x-scroller-btn-over");
56264     down.addClassOnOver("x-scroller-btn-over");
56265     up.addClassOnClick("x-scroller-btn-click");
56266     down.addClassOnClick("x-scroller-btn-click");
56267     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
56268
56269     this.resizeEl = this.el;
56270     this.el = wrap; this.up = up; this.down = down;
56271 };
56272
56273 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
56274     increment : 100,
56275     wheelIncrement : 5,
56276     scrollUp : function(){
56277         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
56278     },
56279
56280     scrollDown : function(){
56281         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
56282     },
56283
56284     afterScroll : function(){
56285         var el = this.resizeEl;
56286         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
56287         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56288         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56289     },
56290
56291     setSize : function(){
56292         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
56293         this.afterScroll();
56294     },
56295
56296     onWheel : function(e){
56297         var d = e.getWheelDelta();
56298         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
56299         this.afterScroll();
56300         e.stopEvent();
56301     },
56302
56303     setContent : function(content, loadScripts){
56304         this.resizeEl.update(content, loadScripts);
56305     }
56306
56307 });
56308
56309
56310
56311 /**
56312  * @class Roo.TreePanel
56313  * @extends Roo.ContentPanel
56314  * Treepanel component
56315  * 
56316  * @constructor
56317  * Create a new TreePanel. - defaults to fit/scoll contents.
56318  * @param {String/Object} config A string to set only the panel's title, or a config object
56319  */
56320 Roo.TreePanel = function(config){
56321     var el = config.el;
56322     var tree = config.tree;
56323     delete config.tree; 
56324     delete config.el; // hopefull!
56325     
56326     // wrapper for IE7 strict & safari scroll issue
56327     
56328     var treeEl = el.createChild();
56329     config.resizeEl = treeEl;
56330     
56331     
56332     
56333     Roo.TreePanel.superclass.constructor.call(this, el, config);
56334  
56335  
56336     this.tree = new Roo.tree.TreePanel(treeEl , tree);
56337     //console.log(tree);
56338     this.on('activate', function()
56339     {
56340         if (this.tree.rendered) {
56341             return;
56342         }
56343         //console.log('render tree');
56344         this.tree.render();
56345     });
56346     // this should not be needed.. - it's actually the 'el' that resizes?
56347     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
56348     
56349     //this.on('resize',  function (cp, w, h) {
56350     //        this.tree.innerCt.setWidth(w);
56351     //        this.tree.innerCt.setHeight(h);
56352     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
56353     //});
56354
56355         
56356     
56357 };
56358
56359 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
56360     fitToFrame : true,
56361     autoScroll : true,
56362     /*
56363      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
56364      */
56365     tree : false
56366
56367 });
56368
56369
56370
56371
56372
56373
56374
56375
56376
56377
56378
56379 /*
56380  * Based on:
56381  * Ext JS Library 1.1.1
56382  * Copyright(c) 2006-2007, Ext JS, LLC.
56383  *
56384  * Originally Released Under LGPL - original licence link has changed is not relivant.
56385  *
56386  * Fork - LGPL
56387  * <script type="text/javascript">
56388  */
56389  
56390
56391 /**
56392  * @class Roo.ReaderLayout
56393  * @extends Roo.BorderLayout
56394  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
56395  * center region containing two nested regions (a top one for a list view and one for item preview below),
56396  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
56397  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
56398  * expedites the setup of the overall layout and regions for this common application style.
56399  * Example:
56400  <pre><code>
56401 var reader = new Roo.ReaderLayout();
56402 var CP = Roo.ContentPanel;  // shortcut for adding
56403
56404 reader.beginUpdate();
56405 reader.add("north", new CP("north", "North"));
56406 reader.add("west", new CP("west", {title: "West"}));
56407 reader.add("east", new CP("east", {title: "East"}));
56408
56409 reader.regions.listView.add(new CP("listView", "List"));
56410 reader.regions.preview.add(new CP("preview", "Preview"));
56411 reader.endUpdate();
56412 </code></pre>
56413 * @constructor
56414 * Create a new ReaderLayout
56415 * @param {Object} config Configuration options
56416 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
56417 * document.body if omitted)
56418 */
56419 Roo.ReaderLayout = function(config, renderTo){
56420     var c = config || {size:{}};
56421     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
56422         north: c.north !== false ? Roo.apply({
56423             split:false,
56424             initialSize: 32,
56425             titlebar: false
56426         }, c.north) : false,
56427         west: c.west !== false ? Roo.apply({
56428             split:true,
56429             initialSize: 200,
56430             minSize: 175,
56431             maxSize: 400,
56432             titlebar: true,
56433             collapsible: true,
56434             animate: true,
56435             margins:{left:5,right:0,bottom:5,top:5},
56436             cmargins:{left:5,right:5,bottom:5,top:5}
56437         }, c.west) : false,
56438         east: c.east !== false ? Roo.apply({
56439             split:true,
56440             initialSize: 200,
56441             minSize: 175,
56442             maxSize: 400,
56443             titlebar: true,
56444             collapsible: true,
56445             animate: true,
56446             margins:{left:0,right:5,bottom:5,top:5},
56447             cmargins:{left:5,right:5,bottom:5,top:5}
56448         }, c.east) : false,
56449         center: Roo.apply({
56450             tabPosition: 'top',
56451             autoScroll:false,
56452             closeOnTab: true,
56453             titlebar:false,
56454             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
56455         }, c.center)
56456     });
56457
56458     this.el.addClass('x-reader');
56459
56460     this.beginUpdate();
56461
56462     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
56463         south: c.preview !== false ? Roo.apply({
56464             split:true,
56465             initialSize: 200,
56466             minSize: 100,
56467             autoScroll:true,
56468             collapsible:true,
56469             titlebar: true,
56470             cmargins:{top:5,left:0, right:0, bottom:0}
56471         }, c.preview) : false,
56472         center: Roo.apply({
56473             autoScroll:false,
56474             titlebar:false,
56475             minHeight:200
56476         }, c.listView)
56477     });
56478     this.add('center', new Roo.NestedLayoutPanel(inner,
56479             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
56480
56481     this.endUpdate();
56482
56483     this.regions.preview = inner.getRegion('south');
56484     this.regions.listView = inner.getRegion('center');
56485 };
56486
56487 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
56488  * Based on:
56489  * Ext JS Library 1.1.1
56490  * Copyright(c) 2006-2007, Ext JS, LLC.
56491  *
56492  * Originally Released Under LGPL - original licence link has changed is not relivant.
56493  *
56494  * Fork - LGPL
56495  * <script type="text/javascript">
56496  */
56497  
56498 /**
56499  * @class Roo.grid.Grid
56500  * @extends Roo.util.Observable
56501  * This class represents the primary interface of a component based grid control.
56502  * <br><br>Usage:<pre><code>
56503  var grid = new Roo.grid.Grid("my-container-id", {
56504      ds: myDataStore,
56505      cm: myColModel,
56506      selModel: mySelectionModel,
56507      autoSizeColumns: true,
56508      monitorWindowResize: false,
56509      trackMouseOver: true
56510  });
56511  // set any options
56512  grid.render();
56513  * </code></pre>
56514  * <b>Common Problems:</b><br/>
56515  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
56516  * element will correct this<br/>
56517  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
56518  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
56519  * are unpredictable.<br/>
56520  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
56521  * grid to calculate dimensions/offsets.<br/>
56522   * @constructor
56523  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
56524  * The container MUST have some type of size defined for the grid to fill. The container will be
56525  * automatically set to position relative if it isn't already.
56526  * @param {Object} config A config object that sets properties on this grid.
56527  */
56528 Roo.grid.Grid = function(container, config){
56529         // initialize the container
56530         this.container = Roo.get(container);
56531         this.container.update("");
56532         this.container.setStyle("overflow", "hidden");
56533     this.container.addClass('x-grid-container');
56534
56535     this.id = this.container.id;
56536
56537     Roo.apply(this, config);
56538     // check and correct shorthanded configs
56539     if(this.ds){
56540         this.dataSource = this.ds;
56541         delete this.ds;
56542     }
56543     if(this.cm){
56544         this.colModel = this.cm;
56545         delete this.cm;
56546     }
56547     if(this.sm){
56548         this.selModel = this.sm;
56549         delete this.sm;
56550     }
56551
56552     if (this.selModel) {
56553         this.selModel = Roo.factory(this.selModel, Roo.grid);
56554         this.sm = this.selModel;
56555         this.sm.xmodule = this.xmodule || false;
56556     }
56557     if (typeof(this.colModel.config) == 'undefined') {
56558         this.colModel = new Roo.grid.ColumnModel(this.colModel);
56559         this.cm = this.colModel;
56560         this.cm.xmodule = this.xmodule || false;
56561     }
56562     if (this.dataSource) {
56563         this.dataSource= Roo.factory(this.dataSource, Roo.data);
56564         this.ds = this.dataSource;
56565         this.ds.xmodule = this.xmodule || false;
56566          
56567     }
56568     
56569     
56570     
56571     if(this.width){
56572         this.container.setWidth(this.width);
56573     }
56574
56575     if(this.height){
56576         this.container.setHeight(this.height);
56577     }
56578     /** @private */
56579         this.addEvents({
56580         // raw events
56581         /**
56582          * @event click
56583          * The raw click event for the entire grid.
56584          * @param {Roo.EventObject} e
56585          */
56586         "click" : true,
56587         /**
56588          * @event dblclick
56589          * The raw dblclick event for the entire grid.
56590          * @param {Roo.EventObject} e
56591          */
56592         "dblclick" : true,
56593         /**
56594          * @event contextmenu
56595          * The raw contextmenu event for the entire grid.
56596          * @param {Roo.EventObject} e
56597          */
56598         "contextmenu" : true,
56599         /**
56600          * @event mousedown
56601          * The raw mousedown event for the entire grid.
56602          * @param {Roo.EventObject} e
56603          */
56604         "mousedown" : true,
56605         /**
56606          * @event mouseup
56607          * The raw mouseup event for the entire grid.
56608          * @param {Roo.EventObject} e
56609          */
56610         "mouseup" : true,
56611         /**
56612          * @event mouseover
56613          * The raw mouseover event for the entire grid.
56614          * @param {Roo.EventObject} e
56615          */
56616         "mouseover" : true,
56617         /**
56618          * @event mouseout
56619          * The raw mouseout event for the entire grid.
56620          * @param {Roo.EventObject} e
56621          */
56622         "mouseout" : true,
56623         /**
56624          * @event keypress
56625          * The raw keypress event for the entire grid.
56626          * @param {Roo.EventObject} e
56627          */
56628         "keypress" : true,
56629         /**
56630          * @event keydown
56631          * The raw keydown event for the entire grid.
56632          * @param {Roo.EventObject} e
56633          */
56634         "keydown" : true,
56635
56636         // custom events
56637
56638         /**
56639          * @event cellclick
56640          * Fires when a cell is clicked
56641          * @param {Grid} this
56642          * @param {Number} rowIndex
56643          * @param {Number} columnIndex
56644          * @param {Roo.EventObject} e
56645          */
56646         "cellclick" : true,
56647         /**
56648          * @event celldblclick
56649          * Fires when a cell is double clicked
56650          * @param {Grid} this
56651          * @param {Number} rowIndex
56652          * @param {Number} columnIndex
56653          * @param {Roo.EventObject} e
56654          */
56655         "celldblclick" : true,
56656         /**
56657          * @event rowclick
56658          * Fires when a row is clicked
56659          * @param {Grid} this
56660          * @param {Number} rowIndex
56661          * @param {Roo.EventObject} e
56662          */
56663         "rowclick" : true,
56664         /**
56665          * @event rowdblclick
56666          * Fires when a row is double clicked
56667          * @param {Grid} this
56668          * @param {Number} rowIndex
56669          * @param {Roo.EventObject} e
56670          */
56671         "rowdblclick" : true,
56672         /**
56673          * @event headerclick
56674          * Fires when a header is clicked
56675          * @param {Grid} this
56676          * @param {Number} columnIndex
56677          * @param {Roo.EventObject} e
56678          */
56679         "headerclick" : true,
56680         /**
56681          * @event headerdblclick
56682          * Fires when a header cell is double clicked
56683          * @param {Grid} this
56684          * @param {Number} columnIndex
56685          * @param {Roo.EventObject} e
56686          */
56687         "headerdblclick" : true,
56688         /**
56689          * @event rowcontextmenu
56690          * Fires when a row is right clicked
56691          * @param {Grid} this
56692          * @param {Number} rowIndex
56693          * @param {Roo.EventObject} e
56694          */
56695         "rowcontextmenu" : true,
56696         /**
56697          * @event cellcontextmenu
56698          * Fires when a cell is right clicked
56699          * @param {Grid} this
56700          * @param {Number} rowIndex
56701          * @param {Number} cellIndex
56702          * @param {Roo.EventObject} e
56703          */
56704          "cellcontextmenu" : true,
56705         /**
56706          * @event headercontextmenu
56707          * Fires when a header is right clicked
56708          * @param {Grid} this
56709          * @param {Number} columnIndex
56710          * @param {Roo.EventObject} e
56711          */
56712         "headercontextmenu" : true,
56713         /**
56714          * @event bodyscroll
56715          * Fires when the body element is scrolled
56716          * @param {Number} scrollLeft
56717          * @param {Number} scrollTop
56718          */
56719         "bodyscroll" : true,
56720         /**
56721          * @event columnresize
56722          * Fires when the user resizes a column
56723          * @param {Number} columnIndex
56724          * @param {Number} newSize
56725          */
56726         "columnresize" : true,
56727         /**
56728          * @event columnmove
56729          * Fires when the user moves a column
56730          * @param {Number} oldIndex
56731          * @param {Number} newIndex
56732          */
56733         "columnmove" : true,
56734         /**
56735          * @event startdrag
56736          * Fires when row(s) start being dragged
56737          * @param {Grid} this
56738          * @param {Roo.GridDD} dd The drag drop object
56739          * @param {event} e The raw browser event
56740          */
56741         "startdrag" : true,
56742         /**
56743          * @event enddrag
56744          * Fires when a drag operation is complete
56745          * @param {Grid} this
56746          * @param {Roo.GridDD} dd The drag drop object
56747          * @param {event} e The raw browser event
56748          */
56749         "enddrag" : true,
56750         /**
56751          * @event dragdrop
56752          * Fires when dragged row(s) are dropped on a valid DD target
56753          * @param {Grid} this
56754          * @param {Roo.GridDD} dd The drag drop object
56755          * @param {String} targetId The target drag drop object
56756          * @param {event} e The raw browser event
56757          */
56758         "dragdrop" : true,
56759         /**
56760          * @event dragover
56761          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
56762          * @param {Grid} this
56763          * @param {Roo.GridDD} dd The drag drop object
56764          * @param {String} targetId The target drag drop object
56765          * @param {event} e The raw browser event
56766          */
56767         "dragover" : true,
56768         /**
56769          * @event dragenter
56770          *  Fires when the dragged row(s) first cross another DD target while being dragged
56771          * @param {Grid} this
56772          * @param {Roo.GridDD} dd The drag drop object
56773          * @param {String} targetId The target drag drop object
56774          * @param {event} e The raw browser event
56775          */
56776         "dragenter" : true,
56777         /**
56778          * @event dragout
56779          * Fires when the dragged row(s) leave another DD target while being dragged
56780          * @param {Grid} this
56781          * @param {Roo.GridDD} dd The drag drop object
56782          * @param {String} targetId The target drag drop object
56783          * @param {event} e The raw browser event
56784          */
56785         "dragout" : true,
56786         /**
56787          * @event rowclass
56788          * Fires when a row is rendered, so you can change add a style to it.
56789          * @param {GridView} gridview   The grid view
56790          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
56791          */
56792         'rowclass' : true,
56793
56794         /**
56795          * @event render
56796          * Fires when the grid is rendered
56797          * @param {Grid} grid
56798          */
56799         'render' : true
56800     });
56801
56802     Roo.grid.Grid.superclass.constructor.call(this);
56803 };
56804 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
56805     
56806     /**
56807          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
56808          */
56809         /**
56810          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
56811          */
56812         /**
56813          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
56814          */
56815         /**
56816          * @cfg {Roo.grid.Store} ds The data store for the grid
56817          */
56818         /**
56819          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
56820          */
56821         /**
56822      * @cfg {String} ddGroup - drag drop group.
56823      */
56824       /**
56825      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
56826      */
56827
56828     /**
56829      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
56830      */
56831     minColumnWidth : 25,
56832
56833     /**
56834      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
56835      * <b>on initial render.</b> It is more efficient to explicitly size the columns
56836      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
56837      */
56838     autoSizeColumns : false,
56839
56840     /**
56841      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
56842      */
56843     autoSizeHeaders : true,
56844
56845     /**
56846      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
56847      */
56848     monitorWindowResize : true,
56849
56850     /**
56851      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
56852      * rows measured to get a columns size. Default is 0 (all rows).
56853      */
56854     maxRowsToMeasure : 0,
56855
56856     /**
56857      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
56858      */
56859     trackMouseOver : true,
56860
56861     /**
56862     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
56863     */
56864       /**
56865     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
56866     */
56867     
56868     /**
56869     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
56870     */
56871     enableDragDrop : false,
56872     
56873     /**
56874     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
56875     */
56876     enableColumnMove : true,
56877     
56878     /**
56879     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
56880     */
56881     enableColumnHide : true,
56882     
56883     /**
56884     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
56885     */
56886     enableRowHeightSync : false,
56887     
56888     /**
56889     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
56890     */
56891     stripeRows : true,
56892     
56893     /**
56894     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
56895     */
56896     autoHeight : false,
56897
56898     /**
56899      * @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.
56900      */
56901     autoExpandColumn : false,
56902
56903     /**
56904     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
56905     * Default is 50.
56906     */
56907     autoExpandMin : 50,
56908
56909     /**
56910     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
56911     */
56912     autoExpandMax : 1000,
56913
56914     /**
56915     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
56916     */
56917     view : null,
56918
56919     /**
56920     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
56921     */
56922     loadMask : false,
56923     /**
56924     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
56925     */
56926     dropTarget: false,
56927     
56928    
56929     
56930     // private
56931     rendered : false,
56932
56933     /**
56934     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
56935     * of a fixed width. Default is false.
56936     */
56937     /**
56938     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
56939     */
56940     
56941     
56942     /**
56943     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
56944     * %0 is replaced with the number of selected rows.
56945     */
56946     ddText : "{0} selected row{1}",
56947     
56948     
56949     /**
56950      * Called once after all setup has been completed and the grid is ready to be rendered.
56951      * @return {Roo.grid.Grid} this
56952      */
56953     render : function()
56954     {
56955         var c = this.container;
56956         // try to detect autoHeight/width mode
56957         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
56958             this.autoHeight = true;
56959         }
56960         var view = this.getView();
56961         view.init(this);
56962
56963         c.on("click", this.onClick, this);
56964         c.on("dblclick", this.onDblClick, this);
56965         c.on("contextmenu", this.onContextMenu, this);
56966         c.on("keydown", this.onKeyDown, this);
56967         if (Roo.isTouch) {
56968             c.on("touchstart", this.onTouchStart, this);
56969         }
56970
56971         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
56972
56973         this.getSelectionModel().init(this);
56974
56975         view.render();
56976
56977         if(this.loadMask){
56978             this.loadMask = new Roo.LoadMask(this.container,
56979                     Roo.apply({store:this.dataSource}, this.loadMask));
56980         }
56981         
56982         
56983         if (this.toolbar && this.toolbar.xtype) {
56984             this.toolbar.container = this.getView().getHeaderPanel(true);
56985             this.toolbar = new Roo.Toolbar(this.toolbar);
56986         }
56987         if (this.footer && this.footer.xtype) {
56988             this.footer.dataSource = this.getDataSource();
56989             this.footer.container = this.getView().getFooterPanel(true);
56990             this.footer = Roo.factory(this.footer, Roo);
56991         }
56992         if (this.dropTarget && this.dropTarget.xtype) {
56993             delete this.dropTarget.xtype;
56994             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
56995         }
56996         
56997         
56998         this.rendered = true;
56999         this.fireEvent('render', this);
57000         return this;
57001     },
57002
57003     /**
57004      * Reconfigures the grid to use a different Store and Column Model.
57005      * The View will be bound to the new objects and refreshed.
57006      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
57007      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
57008      */
57009     reconfigure : function(dataSource, colModel){
57010         if(this.loadMask){
57011             this.loadMask.destroy();
57012             this.loadMask = new Roo.LoadMask(this.container,
57013                     Roo.apply({store:dataSource}, this.loadMask));
57014         }
57015         this.view.bind(dataSource, colModel);
57016         this.dataSource = dataSource;
57017         this.colModel = colModel;
57018         this.view.refresh(true);
57019     },
57020     /**
57021      * addColumns
57022      * Add's a column, default at the end..
57023      
57024      * @param {int} position to add (default end)
57025      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
57026      */
57027     addColumns : function(pos, ar)
57028     {
57029         
57030         for (var i =0;i< ar.length;i++) {
57031             var cfg = ar[i];
57032             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
57033             this.cm.lookup[cfg.id] = cfg;
57034         }
57035         
57036         
57037         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
57038             pos = this.cm.config.length; //this.cm.config.push(cfg);
57039         } 
57040         pos = Math.max(0,pos);
57041         ar.unshift(0);
57042         ar.unshift(pos);
57043         this.cm.config.splice.apply(this.cm.config, ar);
57044         
57045         
57046         
57047         this.view.generateRules(this.cm);
57048         this.view.refresh(true);
57049         
57050     },
57051     
57052     
57053     
57054     
57055     // private
57056     onKeyDown : function(e){
57057         this.fireEvent("keydown", e);
57058     },
57059
57060     /**
57061      * Destroy this grid.
57062      * @param {Boolean} removeEl True to remove the element
57063      */
57064     destroy : function(removeEl, keepListeners){
57065         if(this.loadMask){
57066             this.loadMask.destroy();
57067         }
57068         var c = this.container;
57069         c.removeAllListeners();
57070         this.view.destroy();
57071         this.colModel.purgeListeners();
57072         if(!keepListeners){
57073             this.purgeListeners();
57074         }
57075         c.update("");
57076         if(removeEl === true){
57077             c.remove();
57078         }
57079     },
57080
57081     // private
57082     processEvent : function(name, e){
57083         // does this fire select???
57084         //Roo.log('grid:processEvent '  + name);
57085         
57086         if (name != 'touchstart' ) {
57087             this.fireEvent(name, e);    
57088         }
57089         
57090         var t = e.getTarget();
57091         var v = this.view;
57092         var header = v.findHeaderIndex(t);
57093         if(header !== false){
57094             var ename = name == 'touchstart' ? 'click' : name;
57095              
57096             this.fireEvent("header" + ename, this, header, e);
57097         }else{
57098             var row = v.findRowIndex(t);
57099             var cell = v.findCellIndex(t);
57100             if (name == 'touchstart') {
57101                 // first touch is always a click.
57102                 // hopefull this happens after selection is updated.?
57103                 name = false;
57104                 
57105                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
57106                     var cs = this.selModel.getSelectedCell();
57107                     if (row == cs[0] && cell == cs[1]){
57108                         name = 'dblclick';
57109                     }
57110                 }
57111                 if (typeof(this.selModel.getSelections) != 'undefined') {
57112                     var cs = this.selModel.getSelections();
57113                     var ds = this.dataSource;
57114                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
57115                         name = 'dblclick';
57116                     }
57117                 }
57118                 if (!name) {
57119                     return;
57120                 }
57121             }
57122             
57123             
57124             if(row !== false){
57125                 this.fireEvent("row" + name, this, row, e);
57126                 if(cell !== false){
57127                     this.fireEvent("cell" + name, this, row, cell, e);
57128                 }
57129             }
57130         }
57131     },
57132
57133     // private
57134     onClick : function(e){
57135         this.processEvent("click", e);
57136     },
57137    // private
57138     onTouchStart : function(e){
57139         this.processEvent("touchstart", e);
57140     },
57141
57142     // private
57143     onContextMenu : function(e, t){
57144         this.processEvent("contextmenu", e);
57145     },
57146
57147     // private
57148     onDblClick : function(e){
57149         this.processEvent("dblclick", e);
57150     },
57151
57152     // private
57153     walkCells : function(row, col, step, fn, scope){
57154         var cm = this.colModel, clen = cm.getColumnCount();
57155         var ds = this.dataSource, rlen = ds.getCount(), first = true;
57156         if(step < 0){
57157             if(col < 0){
57158                 row--;
57159                 first = false;
57160             }
57161             while(row >= 0){
57162                 if(!first){
57163                     col = clen-1;
57164                 }
57165                 first = false;
57166                 while(col >= 0){
57167                     if(fn.call(scope || this, row, col, cm) === true){
57168                         return [row, col];
57169                     }
57170                     col--;
57171                 }
57172                 row--;
57173             }
57174         } else {
57175             if(col >= clen){
57176                 row++;
57177                 first = false;
57178             }
57179             while(row < rlen){
57180                 if(!first){
57181                     col = 0;
57182                 }
57183                 first = false;
57184                 while(col < clen){
57185                     if(fn.call(scope || this, row, col, cm) === true){
57186                         return [row, col];
57187                     }
57188                     col++;
57189                 }
57190                 row++;
57191             }
57192         }
57193         return null;
57194     },
57195
57196     // private
57197     getSelections : function(){
57198         return this.selModel.getSelections();
57199     },
57200
57201     /**
57202      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
57203      * but if manual update is required this method will initiate it.
57204      */
57205     autoSize : function(){
57206         if(this.rendered){
57207             this.view.layout();
57208             if(this.view.adjustForScroll){
57209                 this.view.adjustForScroll();
57210             }
57211         }
57212     },
57213
57214     /**
57215      * Returns the grid's underlying element.
57216      * @return {Element} The element
57217      */
57218     getGridEl : function(){
57219         return this.container;
57220     },
57221
57222     // private for compatibility, overridden by editor grid
57223     stopEditing : function(){},
57224
57225     /**
57226      * Returns the grid's SelectionModel.
57227      * @return {SelectionModel}
57228      */
57229     getSelectionModel : function(){
57230         if(!this.selModel){
57231             this.selModel = new Roo.grid.RowSelectionModel();
57232         }
57233         return this.selModel;
57234     },
57235
57236     /**
57237      * Returns the grid's DataSource.
57238      * @return {DataSource}
57239      */
57240     getDataSource : function(){
57241         return this.dataSource;
57242     },
57243
57244     /**
57245      * Returns the grid's ColumnModel.
57246      * @return {ColumnModel}
57247      */
57248     getColumnModel : function(){
57249         return this.colModel;
57250     },
57251
57252     /**
57253      * Returns the grid's GridView object.
57254      * @return {GridView}
57255      */
57256     getView : function(){
57257         if(!this.view){
57258             this.view = new Roo.grid.GridView(this.viewConfig);
57259             this.relayEvents(this.view, [
57260                 "beforerowremoved", "beforerowsinserted",
57261                 "beforerefresh", "rowremoved",
57262                 "rowsinserted", "rowupdated" ,"refresh"
57263             ]);
57264         }
57265         return this.view;
57266     },
57267     /**
57268      * Called to get grid's drag proxy text, by default returns this.ddText.
57269      * Override this to put something different in the dragged text.
57270      * @return {String}
57271      */
57272     getDragDropText : function(){
57273         var count = this.selModel.getCount();
57274         return String.format(this.ddText, count, count == 1 ? '' : 's');
57275     }
57276 });
57277 /*
57278  * Based on:
57279  * Ext JS Library 1.1.1
57280  * Copyright(c) 2006-2007, Ext JS, LLC.
57281  *
57282  * Originally Released Under LGPL - original licence link has changed is not relivant.
57283  *
57284  * Fork - LGPL
57285  * <script type="text/javascript">
57286  */
57287  /**
57288  * @class Roo.grid.AbstractGridView
57289  * @extends Roo.util.Observable
57290  * @abstract
57291  * Abstract base class for grid Views
57292  * @constructor
57293  */
57294 Roo.grid.AbstractGridView = function(){
57295         this.grid = null;
57296         
57297         this.events = {
57298             "beforerowremoved" : true,
57299             "beforerowsinserted" : true,
57300             "beforerefresh" : true,
57301             "rowremoved" : true,
57302             "rowsinserted" : true,
57303             "rowupdated" : true,
57304             "refresh" : true
57305         };
57306     Roo.grid.AbstractGridView.superclass.constructor.call(this);
57307 };
57308
57309 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
57310     rowClass : "x-grid-row",
57311     cellClass : "x-grid-cell",
57312     tdClass : "x-grid-td",
57313     hdClass : "x-grid-hd",
57314     splitClass : "x-grid-hd-split",
57315     
57316     init: function(grid){
57317         this.grid = grid;
57318                 var cid = this.grid.getGridEl().id;
57319         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
57320         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
57321         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
57322         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
57323         },
57324         
57325     getColumnRenderers : function(){
57326         var renderers = [];
57327         var cm = this.grid.colModel;
57328         var colCount = cm.getColumnCount();
57329         for(var i = 0; i < colCount; i++){
57330             renderers[i] = cm.getRenderer(i);
57331         }
57332         return renderers;
57333     },
57334     
57335     getColumnIds : function(){
57336         var ids = [];
57337         var cm = this.grid.colModel;
57338         var colCount = cm.getColumnCount();
57339         for(var i = 0; i < colCount; i++){
57340             ids[i] = cm.getColumnId(i);
57341         }
57342         return ids;
57343     },
57344     
57345     getDataIndexes : function(){
57346         if(!this.indexMap){
57347             this.indexMap = this.buildIndexMap();
57348         }
57349         return this.indexMap.colToData;
57350     },
57351     
57352     getColumnIndexByDataIndex : function(dataIndex){
57353         if(!this.indexMap){
57354             this.indexMap = this.buildIndexMap();
57355         }
57356         return this.indexMap.dataToCol[dataIndex];
57357     },
57358     
57359     /**
57360      * Set a css style for a column dynamically. 
57361      * @param {Number} colIndex The index of the column
57362      * @param {String} name The css property name
57363      * @param {String} value The css value
57364      */
57365     setCSSStyle : function(colIndex, name, value){
57366         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
57367         Roo.util.CSS.updateRule(selector, name, value);
57368     },
57369     
57370     generateRules : function(cm){
57371         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
57372         Roo.util.CSS.removeStyleSheet(rulesId);
57373         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57374             var cid = cm.getColumnId(i);
57375             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
57376                          this.tdSelector, cid, " {\n}\n",
57377                          this.hdSelector, cid, " {\n}\n",
57378                          this.splitSelector, cid, " {\n}\n");
57379         }
57380         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57381     }
57382 });/*
57383  * Based on:
57384  * Ext JS Library 1.1.1
57385  * Copyright(c) 2006-2007, Ext JS, LLC.
57386  *
57387  * Originally Released Under LGPL - original licence link has changed is not relivant.
57388  *
57389  * Fork - LGPL
57390  * <script type="text/javascript">
57391  */
57392
57393 // private
57394 // This is a support class used internally by the Grid components
57395 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
57396     this.grid = grid;
57397     this.view = grid.getView();
57398     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57399     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
57400     if(hd2){
57401         this.setHandleElId(Roo.id(hd));
57402         this.setOuterHandleElId(Roo.id(hd2));
57403     }
57404     this.scroll = false;
57405 };
57406 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
57407     maxDragWidth: 120,
57408     getDragData : function(e){
57409         var t = Roo.lib.Event.getTarget(e);
57410         var h = this.view.findHeaderCell(t);
57411         if(h){
57412             return {ddel: h.firstChild, header:h};
57413         }
57414         return false;
57415     },
57416
57417     onInitDrag : function(e){
57418         this.view.headersDisabled = true;
57419         var clone = this.dragData.ddel.cloneNode(true);
57420         clone.id = Roo.id();
57421         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
57422         this.proxy.update(clone);
57423         return true;
57424     },
57425
57426     afterValidDrop : function(){
57427         var v = this.view;
57428         setTimeout(function(){
57429             v.headersDisabled = false;
57430         }, 50);
57431     },
57432
57433     afterInvalidDrop : function(){
57434         var v = this.view;
57435         setTimeout(function(){
57436             v.headersDisabled = false;
57437         }, 50);
57438     }
57439 });
57440 /*
57441  * Based on:
57442  * Ext JS Library 1.1.1
57443  * Copyright(c) 2006-2007, Ext JS, LLC.
57444  *
57445  * Originally Released Under LGPL - original licence link has changed is not relivant.
57446  *
57447  * Fork - LGPL
57448  * <script type="text/javascript">
57449  */
57450 // private
57451 // This is a support class used internally by the Grid components
57452 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
57453     this.grid = grid;
57454     this.view = grid.getView();
57455     // split the proxies so they don't interfere with mouse events
57456     this.proxyTop = Roo.DomHelper.append(document.body, {
57457         cls:"col-move-top", html:"&#160;"
57458     }, true);
57459     this.proxyBottom = Roo.DomHelper.append(document.body, {
57460         cls:"col-move-bottom", html:"&#160;"
57461     }, true);
57462     this.proxyTop.hide = this.proxyBottom.hide = function(){
57463         this.setLeftTop(-100,-100);
57464         this.setStyle("visibility", "hidden");
57465     };
57466     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57467     // temporarily disabled
57468     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
57469     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
57470 };
57471 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
57472     proxyOffsets : [-4, -9],
57473     fly: Roo.Element.fly,
57474
57475     getTargetFromEvent : function(e){
57476         var t = Roo.lib.Event.getTarget(e);
57477         var cindex = this.view.findCellIndex(t);
57478         if(cindex !== false){
57479             return this.view.getHeaderCell(cindex);
57480         }
57481         return null;
57482     },
57483
57484     nextVisible : function(h){
57485         var v = this.view, cm = this.grid.colModel;
57486         h = h.nextSibling;
57487         while(h){
57488             if(!cm.isHidden(v.getCellIndex(h))){
57489                 return h;
57490             }
57491             h = h.nextSibling;
57492         }
57493         return null;
57494     },
57495
57496     prevVisible : function(h){
57497         var v = this.view, cm = this.grid.colModel;
57498         h = h.prevSibling;
57499         while(h){
57500             if(!cm.isHidden(v.getCellIndex(h))){
57501                 return h;
57502             }
57503             h = h.prevSibling;
57504         }
57505         return null;
57506     },
57507
57508     positionIndicator : function(h, n, e){
57509         var x = Roo.lib.Event.getPageX(e);
57510         var r = Roo.lib.Dom.getRegion(n.firstChild);
57511         var px, pt, py = r.top + this.proxyOffsets[1];
57512         if((r.right - x) <= (r.right-r.left)/2){
57513             px = r.right+this.view.borderWidth;
57514             pt = "after";
57515         }else{
57516             px = r.left;
57517             pt = "before";
57518         }
57519         var oldIndex = this.view.getCellIndex(h);
57520         var newIndex = this.view.getCellIndex(n);
57521
57522         if(this.grid.colModel.isFixed(newIndex)){
57523             return false;
57524         }
57525
57526         var locked = this.grid.colModel.isLocked(newIndex);
57527
57528         if(pt == "after"){
57529             newIndex++;
57530         }
57531         if(oldIndex < newIndex){
57532             newIndex--;
57533         }
57534         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
57535             return false;
57536         }
57537         px +=  this.proxyOffsets[0];
57538         this.proxyTop.setLeftTop(px, py);
57539         this.proxyTop.show();
57540         if(!this.bottomOffset){
57541             this.bottomOffset = this.view.mainHd.getHeight();
57542         }
57543         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
57544         this.proxyBottom.show();
57545         return pt;
57546     },
57547
57548     onNodeEnter : function(n, dd, e, data){
57549         if(data.header != n){
57550             this.positionIndicator(data.header, n, e);
57551         }
57552     },
57553
57554     onNodeOver : function(n, dd, e, data){
57555         var result = false;
57556         if(data.header != n){
57557             result = this.positionIndicator(data.header, n, e);
57558         }
57559         if(!result){
57560             this.proxyTop.hide();
57561             this.proxyBottom.hide();
57562         }
57563         return result ? this.dropAllowed : this.dropNotAllowed;
57564     },
57565
57566     onNodeOut : function(n, dd, e, data){
57567         this.proxyTop.hide();
57568         this.proxyBottom.hide();
57569     },
57570
57571     onNodeDrop : function(n, dd, e, data){
57572         var h = data.header;
57573         if(h != n){
57574             var cm = this.grid.colModel;
57575             var x = Roo.lib.Event.getPageX(e);
57576             var r = Roo.lib.Dom.getRegion(n.firstChild);
57577             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
57578             var oldIndex = this.view.getCellIndex(h);
57579             var newIndex = this.view.getCellIndex(n);
57580             var locked = cm.isLocked(newIndex);
57581             if(pt == "after"){
57582                 newIndex++;
57583             }
57584             if(oldIndex < newIndex){
57585                 newIndex--;
57586             }
57587             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
57588                 return false;
57589             }
57590             cm.setLocked(oldIndex, locked, true);
57591             cm.moveColumn(oldIndex, newIndex);
57592             this.grid.fireEvent("columnmove", oldIndex, newIndex);
57593             return true;
57594         }
57595         return false;
57596     }
57597 });
57598 /*
57599  * Based on:
57600  * Ext JS Library 1.1.1
57601  * Copyright(c) 2006-2007, Ext JS, LLC.
57602  *
57603  * Originally Released Under LGPL - original licence link has changed is not relivant.
57604  *
57605  * Fork - LGPL
57606  * <script type="text/javascript">
57607  */
57608   
57609 /**
57610  * @class Roo.grid.GridView
57611  * @extends Roo.util.Observable
57612  *
57613  * @constructor
57614  * @param {Object} config
57615  */
57616 Roo.grid.GridView = function(config){
57617     Roo.grid.GridView.superclass.constructor.call(this);
57618     this.el = null;
57619
57620     Roo.apply(this, config);
57621 };
57622
57623 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
57624
57625     unselectable :  'unselectable="on"',
57626     unselectableCls :  'x-unselectable',
57627     
57628     
57629     rowClass : "x-grid-row",
57630
57631     cellClass : "x-grid-col",
57632
57633     tdClass : "x-grid-td",
57634
57635     hdClass : "x-grid-hd",
57636
57637     splitClass : "x-grid-split",
57638
57639     sortClasses : ["sort-asc", "sort-desc"],
57640
57641     enableMoveAnim : false,
57642
57643     hlColor: "C3DAF9",
57644
57645     dh : Roo.DomHelper,
57646
57647     fly : Roo.Element.fly,
57648
57649     css : Roo.util.CSS,
57650
57651     borderWidth: 1,
57652
57653     splitOffset: 3,
57654
57655     scrollIncrement : 22,
57656
57657     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
57658
57659     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
57660
57661     bind : function(ds, cm){
57662         if(this.ds){
57663             this.ds.un("load", this.onLoad, this);
57664             this.ds.un("datachanged", this.onDataChange, this);
57665             this.ds.un("add", this.onAdd, this);
57666             this.ds.un("remove", this.onRemove, this);
57667             this.ds.un("update", this.onUpdate, this);
57668             this.ds.un("clear", this.onClear, this);
57669         }
57670         if(ds){
57671             ds.on("load", this.onLoad, this);
57672             ds.on("datachanged", this.onDataChange, this);
57673             ds.on("add", this.onAdd, this);
57674             ds.on("remove", this.onRemove, this);
57675             ds.on("update", this.onUpdate, this);
57676             ds.on("clear", this.onClear, this);
57677         }
57678         this.ds = ds;
57679
57680         if(this.cm){
57681             this.cm.un("widthchange", this.onColWidthChange, this);
57682             this.cm.un("headerchange", this.onHeaderChange, this);
57683             this.cm.un("hiddenchange", this.onHiddenChange, this);
57684             this.cm.un("columnmoved", this.onColumnMove, this);
57685             this.cm.un("columnlockchange", this.onColumnLock, this);
57686         }
57687         if(cm){
57688             this.generateRules(cm);
57689             cm.on("widthchange", this.onColWidthChange, this);
57690             cm.on("headerchange", this.onHeaderChange, this);
57691             cm.on("hiddenchange", this.onHiddenChange, this);
57692             cm.on("columnmoved", this.onColumnMove, this);
57693             cm.on("columnlockchange", this.onColumnLock, this);
57694         }
57695         this.cm = cm;
57696     },
57697
57698     init: function(grid){
57699         Roo.grid.GridView.superclass.init.call(this, grid);
57700
57701         this.bind(grid.dataSource, grid.colModel);
57702
57703         grid.on("headerclick", this.handleHeaderClick, this);
57704
57705         if(grid.trackMouseOver){
57706             grid.on("mouseover", this.onRowOver, this);
57707             grid.on("mouseout", this.onRowOut, this);
57708         }
57709         grid.cancelTextSelection = function(){};
57710         this.gridId = grid.id;
57711
57712         var tpls = this.templates || {};
57713
57714         if(!tpls.master){
57715             tpls.master = new Roo.Template(
57716                '<div class="x-grid" hidefocus="true">',
57717                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
57718                   '<div class="x-grid-topbar"></div>',
57719                   '<div class="x-grid-scroller"><div></div></div>',
57720                   '<div class="x-grid-locked">',
57721                       '<div class="x-grid-header">{lockedHeader}</div>',
57722                       '<div class="x-grid-body">{lockedBody}</div>',
57723                   "</div>",
57724                   '<div class="x-grid-viewport">',
57725                       '<div class="x-grid-header">{header}</div>',
57726                       '<div class="x-grid-body">{body}</div>',
57727                   "</div>",
57728                   '<div class="x-grid-bottombar"></div>',
57729                  
57730                   '<div class="x-grid-resize-proxy">&#160;</div>',
57731                "</div>"
57732             );
57733             tpls.master.disableformats = true;
57734         }
57735
57736         if(!tpls.header){
57737             tpls.header = new Roo.Template(
57738                '<table border="0" cellspacing="0" cellpadding="0">',
57739                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
57740                "</table>{splits}"
57741             );
57742             tpls.header.disableformats = true;
57743         }
57744         tpls.header.compile();
57745
57746         if(!tpls.hcell){
57747             tpls.hcell = new Roo.Template(
57748                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
57749                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
57750                 "</div></td>"
57751              );
57752              tpls.hcell.disableFormats = true;
57753         }
57754         tpls.hcell.compile();
57755
57756         if(!tpls.hsplit){
57757             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
57758                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
57759             tpls.hsplit.disableFormats = true;
57760         }
57761         tpls.hsplit.compile();
57762
57763         if(!tpls.body){
57764             tpls.body = new Roo.Template(
57765                '<table border="0" cellspacing="0" cellpadding="0">',
57766                "<tbody>{rows}</tbody>",
57767                "</table>"
57768             );
57769             tpls.body.disableFormats = true;
57770         }
57771         tpls.body.compile();
57772
57773         if(!tpls.row){
57774             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
57775             tpls.row.disableFormats = true;
57776         }
57777         tpls.row.compile();
57778
57779         if(!tpls.cell){
57780             tpls.cell = new Roo.Template(
57781                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
57782                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
57783                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
57784                 "</td>"
57785             );
57786             tpls.cell.disableFormats = true;
57787         }
57788         tpls.cell.compile();
57789
57790         this.templates = tpls;
57791     },
57792
57793     // remap these for backwards compat
57794     onColWidthChange : function(){
57795         this.updateColumns.apply(this, arguments);
57796     },
57797     onHeaderChange : function(){
57798         this.updateHeaders.apply(this, arguments);
57799     }, 
57800     onHiddenChange : function(){
57801         this.handleHiddenChange.apply(this, arguments);
57802     },
57803     onColumnMove : function(){
57804         this.handleColumnMove.apply(this, arguments);
57805     },
57806     onColumnLock : function(){
57807         this.handleLockChange.apply(this, arguments);
57808     },
57809
57810     onDataChange : function(){
57811         this.refresh();
57812         this.updateHeaderSortState();
57813     },
57814
57815     onClear : function(){
57816         this.refresh();
57817     },
57818
57819     onUpdate : function(ds, record){
57820         this.refreshRow(record);
57821     },
57822
57823     refreshRow : function(record){
57824         var ds = this.ds, index;
57825         if(typeof record == 'number'){
57826             index = record;
57827             record = ds.getAt(index);
57828         }else{
57829             index = ds.indexOf(record);
57830         }
57831         this.insertRows(ds, index, index, true);
57832         this.onRemove(ds, record, index+1, true);
57833         this.syncRowHeights(index, index);
57834         this.layout();
57835         this.fireEvent("rowupdated", this, index, record);
57836     },
57837
57838     onAdd : function(ds, records, index){
57839         this.insertRows(ds, index, index + (records.length-1));
57840     },
57841
57842     onRemove : function(ds, record, index, isUpdate){
57843         if(isUpdate !== true){
57844             this.fireEvent("beforerowremoved", this, index, record);
57845         }
57846         var bt = this.getBodyTable(), lt = this.getLockedTable();
57847         if(bt.rows[index]){
57848             bt.firstChild.removeChild(bt.rows[index]);
57849         }
57850         if(lt.rows[index]){
57851             lt.firstChild.removeChild(lt.rows[index]);
57852         }
57853         if(isUpdate !== true){
57854             this.stripeRows(index);
57855             this.syncRowHeights(index, index);
57856             this.layout();
57857             this.fireEvent("rowremoved", this, index, record);
57858         }
57859     },
57860
57861     onLoad : function(){
57862         this.scrollToTop();
57863     },
57864
57865     /**
57866      * Scrolls the grid to the top
57867      */
57868     scrollToTop : function(){
57869         if(this.scroller){
57870             this.scroller.dom.scrollTop = 0;
57871             this.syncScroll();
57872         }
57873     },
57874
57875     /**
57876      * Gets a panel in the header of the grid that can be used for toolbars etc.
57877      * After modifying the contents of this panel a call to grid.autoSize() may be
57878      * required to register any changes in size.
57879      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
57880      * @return Roo.Element
57881      */
57882     getHeaderPanel : function(doShow){
57883         if(doShow){
57884             this.headerPanel.show();
57885         }
57886         return this.headerPanel;
57887     },
57888
57889     /**
57890      * Gets a panel in the footer of the grid that can be used for toolbars etc.
57891      * After modifying the contents of this panel a call to grid.autoSize() may be
57892      * required to register any changes in size.
57893      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
57894      * @return Roo.Element
57895      */
57896     getFooterPanel : function(doShow){
57897         if(doShow){
57898             this.footerPanel.show();
57899         }
57900         return this.footerPanel;
57901     },
57902
57903     initElements : function(){
57904         var E = Roo.Element;
57905         var el = this.grid.getGridEl().dom.firstChild;
57906         var cs = el.childNodes;
57907
57908         this.el = new E(el);
57909         
57910          this.focusEl = new E(el.firstChild);
57911         this.focusEl.swallowEvent("click", true);
57912         
57913         this.headerPanel = new E(cs[1]);
57914         this.headerPanel.enableDisplayMode("block");
57915
57916         this.scroller = new E(cs[2]);
57917         this.scrollSizer = new E(this.scroller.dom.firstChild);
57918
57919         this.lockedWrap = new E(cs[3]);
57920         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
57921         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
57922
57923         this.mainWrap = new E(cs[4]);
57924         this.mainHd = new E(this.mainWrap.dom.firstChild);
57925         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
57926
57927         this.footerPanel = new E(cs[5]);
57928         this.footerPanel.enableDisplayMode("block");
57929
57930         this.resizeProxy = new E(cs[6]);
57931
57932         this.headerSelector = String.format(
57933            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
57934            this.lockedHd.id, this.mainHd.id
57935         );
57936
57937         this.splitterSelector = String.format(
57938            '#{0} div.x-grid-split, #{1} div.x-grid-split',
57939            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
57940         );
57941     },
57942     idToCssName : function(s)
57943     {
57944         return s.replace(/[^a-z0-9]+/ig, '-');
57945     },
57946
57947     getHeaderCell : function(index){
57948         return Roo.DomQuery.select(this.headerSelector)[index];
57949     },
57950
57951     getHeaderCellMeasure : function(index){
57952         return this.getHeaderCell(index).firstChild;
57953     },
57954
57955     getHeaderCellText : function(index){
57956         return this.getHeaderCell(index).firstChild.firstChild;
57957     },
57958
57959     getLockedTable : function(){
57960         return this.lockedBody.dom.firstChild;
57961     },
57962
57963     getBodyTable : function(){
57964         return this.mainBody.dom.firstChild;
57965     },
57966
57967     getLockedRow : function(index){
57968         return this.getLockedTable().rows[index];
57969     },
57970
57971     getRow : function(index){
57972         return this.getBodyTable().rows[index];
57973     },
57974
57975     getRowComposite : function(index){
57976         if(!this.rowEl){
57977             this.rowEl = new Roo.CompositeElementLite();
57978         }
57979         var els = [], lrow, mrow;
57980         if(lrow = this.getLockedRow(index)){
57981             els.push(lrow);
57982         }
57983         if(mrow = this.getRow(index)){
57984             els.push(mrow);
57985         }
57986         this.rowEl.elements = els;
57987         return this.rowEl;
57988     },
57989     /**
57990      * Gets the 'td' of the cell
57991      * 
57992      * @param {Integer} rowIndex row to select
57993      * @param {Integer} colIndex column to select
57994      * 
57995      * @return {Object} 
57996      */
57997     getCell : function(rowIndex, colIndex){
57998         var locked = this.cm.getLockedCount();
57999         var source;
58000         if(colIndex < locked){
58001             source = this.lockedBody.dom.firstChild;
58002         }else{
58003             source = this.mainBody.dom.firstChild;
58004             colIndex -= locked;
58005         }
58006         return source.rows[rowIndex].childNodes[colIndex];
58007     },
58008
58009     getCellText : function(rowIndex, colIndex){
58010         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
58011     },
58012
58013     getCellBox : function(cell){
58014         var b = this.fly(cell).getBox();
58015         if(Roo.isOpera){ // opera fails to report the Y
58016             b.y = cell.offsetTop + this.mainBody.getY();
58017         }
58018         return b;
58019     },
58020
58021     getCellIndex : function(cell){
58022         var id = String(cell.className).match(this.cellRE);
58023         if(id){
58024             return parseInt(id[1], 10);
58025         }
58026         return 0;
58027     },
58028
58029     findHeaderIndex : function(n){
58030         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
58031         return r ? this.getCellIndex(r) : false;
58032     },
58033
58034     findHeaderCell : function(n){
58035         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
58036         return r ? r : false;
58037     },
58038
58039     findRowIndex : function(n){
58040         if(!n){
58041             return false;
58042         }
58043         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
58044         return r ? r.rowIndex : false;
58045     },
58046
58047     findCellIndex : function(node){
58048         var stop = this.el.dom;
58049         while(node && node != stop){
58050             if(this.findRE.test(node.className)){
58051                 return this.getCellIndex(node);
58052             }
58053             node = node.parentNode;
58054         }
58055         return false;
58056     },
58057
58058     getColumnId : function(index){
58059         return this.cm.getColumnId(index);
58060     },
58061
58062     getSplitters : function()
58063     {
58064         if(this.splitterSelector){
58065            return Roo.DomQuery.select(this.splitterSelector);
58066         }else{
58067             return null;
58068       }
58069     },
58070
58071     getSplitter : function(index){
58072         return this.getSplitters()[index];
58073     },
58074
58075     onRowOver : function(e, t){
58076         var row;
58077         if((row = this.findRowIndex(t)) !== false){
58078             this.getRowComposite(row).addClass("x-grid-row-over");
58079         }
58080     },
58081
58082     onRowOut : function(e, t){
58083         var row;
58084         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
58085             this.getRowComposite(row).removeClass("x-grid-row-over");
58086         }
58087     },
58088
58089     renderHeaders : function(){
58090         var cm = this.cm;
58091         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
58092         var cb = [], lb = [], sb = [], lsb = [], p = {};
58093         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58094             p.cellId = "x-grid-hd-0-" + i;
58095             p.splitId = "x-grid-csplit-0-" + i;
58096             p.id = cm.getColumnId(i);
58097             p.value = cm.getColumnHeader(i) || "";
58098             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
58099             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
58100             if(!cm.isLocked(i)){
58101                 cb[cb.length] = ct.apply(p);
58102                 sb[sb.length] = st.apply(p);
58103             }else{
58104                 lb[lb.length] = ct.apply(p);
58105                 lsb[lsb.length] = st.apply(p);
58106             }
58107         }
58108         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
58109                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
58110     },
58111
58112     updateHeaders : function(){
58113         var html = this.renderHeaders();
58114         this.lockedHd.update(html[0]);
58115         this.mainHd.update(html[1]);
58116     },
58117
58118     /**
58119      * Focuses the specified row.
58120      * @param {Number} row The row index
58121      */
58122     focusRow : function(row)
58123     {
58124         //Roo.log('GridView.focusRow');
58125         var x = this.scroller.dom.scrollLeft;
58126         this.focusCell(row, 0, false);
58127         this.scroller.dom.scrollLeft = x;
58128     },
58129
58130     /**
58131      * Focuses the specified cell.
58132      * @param {Number} row The row index
58133      * @param {Number} col The column index
58134      * @param {Boolean} hscroll false to disable horizontal scrolling
58135      */
58136     focusCell : function(row, col, hscroll)
58137     {
58138         //Roo.log('GridView.focusCell');
58139         var el = this.ensureVisible(row, col, hscroll);
58140         this.focusEl.alignTo(el, "tl-tl");
58141         if(Roo.isGecko){
58142             this.focusEl.focus();
58143         }else{
58144             this.focusEl.focus.defer(1, this.focusEl);
58145         }
58146     },
58147
58148     /**
58149      * Scrolls the specified cell into view
58150      * @param {Number} row The row index
58151      * @param {Number} col The column index
58152      * @param {Boolean} hscroll false to disable horizontal scrolling
58153      */
58154     ensureVisible : function(row, col, hscroll)
58155     {
58156         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
58157         //return null; //disable for testing.
58158         if(typeof row != "number"){
58159             row = row.rowIndex;
58160         }
58161         if(row < 0 && row >= this.ds.getCount()){
58162             return  null;
58163         }
58164         col = (col !== undefined ? col : 0);
58165         var cm = this.grid.colModel;
58166         while(cm.isHidden(col)){
58167             col++;
58168         }
58169
58170         var el = this.getCell(row, col);
58171         if(!el){
58172             return null;
58173         }
58174         var c = this.scroller.dom;
58175
58176         var ctop = parseInt(el.offsetTop, 10);
58177         var cleft = parseInt(el.offsetLeft, 10);
58178         var cbot = ctop + el.offsetHeight;
58179         var cright = cleft + el.offsetWidth;
58180         
58181         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
58182         var stop = parseInt(c.scrollTop, 10);
58183         var sleft = parseInt(c.scrollLeft, 10);
58184         var sbot = stop + ch;
58185         var sright = sleft + c.clientWidth;
58186         /*
58187         Roo.log('GridView.ensureVisible:' +
58188                 ' ctop:' + ctop +
58189                 ' c.clientHeight:' + c.clientHeight +
58190                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
58191                 ' stop:' + stop +
58192                 ' cbot:' + cbot +
58193                 ' sbot:' + sbot +
58194                 ' ch:' + ch  
58195                 );
58196         */
58197         if(ctop < stop){
58198             c.scrollTop = ctop;
58199             //Roo.log("set scrolltop to ctop DISABLE?");
58200         }else if(cbot > sbot){
58201             //Roo.log("set scrolltop to cbot-ch");
58202             c.scrollTop = cbot-ch;
58203         }
58204         
58205         if(hscroll !== false){
58206             if(cleft < sleft){
58207                 c.scrollLeft = cleft;
58208             }else if(cright > sright){
58209                 c.scrollLeft = cright-c.clientWidth;
58210             }
58211         }
58212          
58213         return el;
58214     },
58215
58216     updateColumns : function(){
58217         this.grid.stopEditing();
58218         var cm = this.grid.colModel, colIds = this.getColumnIds();
58219         //var totalWidth = cm.getTotalWidth();
58220         var pos = 0;
58221         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58222             //if(cm.isHidden(i)) continue;
58223             var w = cm.getColumnWidth(i);
58224             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58225             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58226         }
58227         this.updateSplitters();
58228     },
58229
58230     generateRules : function(cm){
58231         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
58232         Roo.util.CSS.removeStyleSheet(rulesId);
58233         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58234             var cid = cm.getColumnId(i);
58235             var align = '';
58236             if(cm.config[i].align){
58237                 align = 'text-align:'+cm.config[i].align+';';
58238             }
58239             var hidden = '';
58240             if(cm.isHidden(i)){
58241                 hidden = 'display:none;';
58242             }
58243             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
58244             ruleBuf.push(
58245                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
58246                     this.hdSelector, cid, " {\n", align, width, "}\n",
58247                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
58248                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
58249         }
58250         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58251     },
58252
58253     updateSplitters : function(){
58254         var cm = this.cm, s = this.getSplitters();
58255         if(s){ // splitters not created yet
58256             var pos = 0, locked = true;
58257             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58258                 if(cm.isHidden(i)) {
58259                     continue;
58260                 }
58261                 var w = cm.getColumnWidth(i); // make sure it's a number
58262                 if(!cm.isLocked(i) && locked){
58263                     pos = 0;
58264                     locked = false;
58265                 }
58266                 pos += w;
58267                 s[i].style.left = (pos-this.splitOffset) + "px";
58268             }
58269         }
58270     },
58271
58272     handleHiddenChange : function(colModel, colIndex, hidden){
58273         if(hidden){
58274             this.hideColumn(colIndex);
58275         }else{
58276             this.unhideColumn(colIndex);
58277         }
58278     },
58279
58280     hideColumn : function(colIndex){
58281         var cid = this.getColumnId(colIndex);
58282         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
58283         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
58284         if(Roo.isSafari){
58285             this.updateHeaders();
58286         }
58287         this.updateSplitters();
58288         this.layout();
58289     },
58290
58291     unhideColumn : function(colIndex){
58292         var cid = this.getColumnId(colIndex);
58293         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
58294         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
58295
58296         if(Roo.isSafari){
58297             this.updateHeaders();
58298         }
58299         this.updateSplitters();
58300         this.layout();
58301     },
58302
58303     insertRows : function(dm, firstRow, lastRow, isUpdate){
58304         if(firstRow == 0 && lastRow == dm.getCount()-1){
58305             this.refresh();
58306         }else{
58307             if(!isUpdate){
58308                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
58309             }
58310             var s = this.getScrollState();
58311             var markup = this.renderRows(firstRow, lastRow);
58312             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
58313             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
58314             this.restoreScroll(s);
58315             if(!isUpdate){
58316                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
58317                 this.syncRowHeights(firstRow, lastRow);
58318                 this.stripeRows(firstRow);
58319                 this.layout();
58320             }
58321         }
58322     },
58323
58324     bufferRows : function(markup, target, index){
58325         var before = null, trows = target.rows, tbody = target.tBodies[0];
58326         if(index < trows.length){
58327             before = trows[index];
58328         }
58329         var b = document.createElement("div");
58330         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
58331         var rows = b.firstChild.rows;
58332         for(var i = 0, len = rows.length; i < len; i++){
58333             if(before){
58334                 tbody.insertBefore(rows[0], before);
58335             }else{
58336                 tbody.appendChild(rows[0]);
58337             }
58338         }
58339         b.innerHTML = "";
58340         b = null;
58341     },
58342
58343     deleteRows : function(dm, firstRow, lastRow){
58344         if(dm.getRowCount()<1){
58345             this.fireEvent("beforerefresh", this);
58346             this.mainBody.update("");
58347             this.lockedBody.update("");
58348             this.fireEvent("refresh", this);
58349         }else{
58350             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
58351             var bt = this.getBodyTable();
58352             var tbody = bt.firstChild;
58353             var rows = bt.rows;
58354             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
58355                 tbody.removeChild(rows[firstRow]);
58356             }
58357             this.stripeRows(firstRow);
58358             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
58359         }
58360     },
58361
58362     updateRows : function(dataSource, firstRow, lastRow){
58363         var s = this.getScrollState();
58364         this.refresh();
58365         this.restoreScroll(s);
58366     },
58367
58368     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
58369         if(!noRefresh){
58370            this.refresh();
58371         }
58372         this.updateHeaderSortState();
58373     },
58374
58375     getScrollState : function(){
58376         
58377         var sb = this.scroller.dom;
58378         return {left: sb.scrollLeft, top: sb.scrollTop};
58379     },
58380
58381     stripeRows : function(startRow){
58382         if(!this.grid.stripeRows || this.ds.getCount() < 1){
58383             return;
58384         }
58385         startRow = startRow || 0;
58386         var rows = this.getBodyTable().rows;
58387         var lrows = this.getLockedTable().rows;
58388         var cls = ' x-grid-row-alt ';
58389         for(var i = startRow, len = rows.length; i < len; i++){
58390             var row = rows[i], lrow = lrows[i];
58391             var isAlt = ((i+1) % 2 == 0);
58392             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
58393             if(isAlt == hasAlt){
58394                 continue;
58395             }
58396             if(isAlt){
58397                 row.className += " x-grid-row-alt";
58398             }else{
58399                 row.className = row.className.replace("x-grid-row-alt", "");
58400             }
58401             if(lrow){
58402                 lrow.className = row.className;
58403             }
58404         }
58405     },
58406
58407     restoreScroll : function(state){
58408         //Roo.log('GridView.restoreScroll');
58409         var sb = this.scroller.dom;
58410         sb.scrollLeft = state.left;
58411         sb.scrollTop = state.top;
58412         this.syncScroll();
58413     },
58414
58415     syncScroll : function(){
58416         //Roo.log('GridView.syncScroll');
58417         var sb = this.scroller.dom;
58418         var sh = this.mainHd.dom;
58419         var bs = this.mainBody.dom;
58420         var lv = this.lockedBody.dom;
58421         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
58422         lv.scrollTop = bs.scrollTop = sb.scrollTop;
58423     },
58424
58425     handleScroll : function(e){
58426         this.syncScroll();
58427         var sb = this.scroller.dom;
58428         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
58429         e.stopEvent();
58430     },
58431
58432     handleWheel : function(e){
58433         var d = e.getWheelDelta();
58434         this.scroller.dom.scrollTop -= d*22;
58435         // set this here to prevent jumpy scrolling on large tables
58436         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
58437         e.stopEvent();
58438     },
58439
58440     renderRows : function(startRow, endRow){
58441         // pull in all the crap needed to render rows
58442         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
58443         var colCount = cm.getColumnCount();
58444
58445         if(ds.getCount() < 1){
58446             return ["", ""];
58447         }
58448
58449         // build a map for all the columns
58450         var cs = [];
58451         for(var i = 0; i < colCount; i++){
58452             var name = cm.getDataIndex(i);
58453             cs[i] = {
58454                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
58455                 renderer : cm.getRenderer(i),
58456                 id : cm.getColumnId(i),
58457                 locked : cm.isLocked(i),
58458                 has_editor : cm.isCellEditable(i)
58459             };
58460         }
58461
58462         startRow = startRow || 0;
58463         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
58464
58465         // records to render
58466         var rs = ds.getRange(startRow, endRow);
58467
58468         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
58469     },
58470
58471     // As much as I hate to duplicate code, this was branched because FireFox really hates
58472     // [].join("") on strings. The performance difference was substantial enough to
58473     // branch this function
58474     doRender : Roo.isGecko ?
58475             function(cs, rs, ds, startRow, colCount, stripe){
58476                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58477                 // buffers
58478                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58479                 
58480                 var hasListener = this.grid.hasListener('rowclass');
58481                 var rowcfg = {};
58482                 for(var j = 0, len = rs.length; j < len; j++){
58483                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
58484                     for(var i = 0; i < colCount; i++){
58485                         c = cs[i];
58486                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58487                         p.id = c.id;
58488                         p.css = p.attr = "";
58489                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58490                         if(p.value == undefined || p.value === "") {
58491                             p.value = "&#160;";
58492                         }
58493                         if(c.has_editor){
58494                             p.css += ' x-grid-editable-cell';
58495                         }
58496                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
58497                             p.css +=  ' x-grid-dirty-cell';
58498                         }
58499                         var markup = ct.apply(p);
58500                         if(!c.locked){
58501                             cb+= markup;
58502                         }else{
58503                             lcb+= markup;
58504                         }
58505                     }
58506                     var alt = [];
58507                     if(stripe && ((rowIndex+1) % 2 == 0)){
58508                         alt.push("x-grid-row-alt")
58509                     }
58510                     if(r.dirty){
58511                         alt.push(  " x-grid-dirty-row");
58512                     }
58513                     rp.cells = lcb;
58514                     if(this.getRowClass){
58515                         alt.push(this.getRowClass(r, rowIndex));
58516                     }
58517                     if (hasListener) {
58518                         rowcfg = {
58519                              
58520                             record: r,
58521                             rowIndex : rowIndex,
58522                             rowClass : ''
58523                         };
58524                         this.grid.fireEvent('rowclass', this, rowcfg);
58525                         alt.push(rowcfg.rowClass);
58526                     }
58527                     rp.alt = alt.join(" ");
58528                     lbuf+= rt.apply(rp);
58529                     rp.cells = cb;
58530                     buf+=  rt.apply(rp);
58531                 }
58532                 return [lbuf, buf];
58533             } :
58534             function(cs, rs, ds, startRow, colCount, stripe){
58535                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58536                 // buffers
58537                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58538                 var hasListener = this.grid.hasListener('rowclass');
58539  
58540                 var rowcfg = {};
58541                 for(var j = 0, len = rs.length; j < len; j++){
58542                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
58543                     for(var i = 0; i < colCount; i++){
58544                         c = cs[i];
58545                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58546                         p.id = c.id;
58547                         p.css = p.attr = "";
58548                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58549                         if(p.value == undefined || p.value === "") {
58550                             p.value = "&#160;";
58551                         }
58552                         //Roo.log(c);
58553                          if(c.has_editor){
58554                             p.css += ' x-grid-editable-cell';
58555                         }
58556                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
58557                             p.css += ' x-grid-dirty-cell' 
58558                         }
58559                         
58560                         var markup = ct.apply(p);
58561                         if(!c.locked){
58562                             cb[cb.length] = markup;
58563                         }else{
58564                             lcb[lcb.length] = markup;
58565                         }
58566                     }
58567                     var alt = [];
58568                     if(stripe && ((rowIndex+1) % 2 == 0)){
58569                         alt.push( "x-grid-row-alt");
58570                     }
58571                     if(r.dirty){
58572                         alt.push(" x-grid-dirty-row");
58573                     }
58574                     rp.cells = lcb;
58575                     if(this.getRowClass){
58576                         alt.push( this.getRowClass(r, rowIndex));
58577                     }
58578                     if (hasListener) {
58579                         rowcfg = {
58580                              
58581                             record: r,
58582                             rowIndex : rowIndex,
58583                             rowClass : ''
58584                         };
58585                         this.grid.fireEvent('rowclass', this, rowcfg);
58586                         alt.push(rowcfg.rowClass);
58587                     }
58588                     
58589                     rp.alt = alt.join(" ");
58590                     rp.cells = lcb.join("");
58591                     lbuf[lbuf.length] = rt.apply(rp);
58592                     rp.cells = cb.join("");
58593                     buf[buf.length] =  rt.apply(rp);
58594                 }
58595                 return [lbuf.join(""), buf.join("")];
58596             },
58597
58598     renderBody : function(){
58599         var markup = this.renderRows();
58600         var bt = this.templates.body;
58601         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
58602     },
58603
58604     /**
58605      * Refreshes the grid
58606      * @param {Boolean} headersToo
58607      */
58608     refresh : function(headersToo){
58609         this.fireEvent("beforerefresh", this);
58610         this.grid.stopEditing();
58611         var result = this.renderBody();
58612         this.lockedBody.update(result[0]);
58613         this.mainBody.update(result[1]);
58614         if(headersToo === true){
58615             this.updateHeaders();
58616             this.updateColumns();
58617             this.updateSplitters();
58618             this.updateHeaderSortState();
58619         }
58620         this.syncRowHeights();
58621         this.layout();
58622         this.fireEvent("refresh", this);
58623     },
58624
58625     handleColumnMove : function(cm, oldIndex, newIndex){
58626         this.indexMap = null;
58627         var s = this.getScrollState();
58628         this.refresh(true);
58629         this.restoreScroll(s);
58630         this.afterMove(newIndex);
58631     },
58632
58633     afterMove : function(colIndex){
58634         if(this.enableMoveAnim && Roo.enableFx){
58635             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
58636         }
58637         // if multisort - fix sortOrder, and reload..
58638         if (this.grid.dataSource.multiSort) {
58639             // the we can call sort again..
58640             var dm = this.grid.dataSource;
58641             var cm = this.grid.colModel;
58642             var so = [];
58643             for(var i = 0; i < cm.config.length; i++ ) {
58644                 
58645                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
58646                     continue; // dont' bother, it's not in sort list or being set.
58647                 }
58648                 
58649                 so.push(cm.config[i].dataIndex);
58650             };
58651             dm.sortOrder = so;
58652             dm.load(dm.lastOptions);
58653             
58654             
58655         }
58656         
58657     },
58658
58659     updateCell : function(dm, rowIndex, dataIndex){
58660         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
58661         if(typeof colIndex == "undefined"){ // not present in grid
58662             return;
58663         }
58664         var cm = this.grid.colModel;
58665         var cell = this.getCell(rowIndex, colIndex);
58666         var cellText = this.getCellText(rowIndex, colIndex);
58667
58668         var p = {
58669             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
58670             id : cm.getColumnId(colIndex),
58671             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
58672         };
58673         var renderer = cm.getRenderer(colIndex);
58674         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
58675         if(typeof val == "undefined" || val === "") {
58676             val = "&#160;";
58677         }
58678         cellText.innerHTML = val;
58679         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
58680         this.syncRowHeights(rowIndex, rowIndex);
58681     },
58682
58683     calcColumnWidth : function(colIndex, maxRowsToMeasure){
58684         var maxWidth = 0;
58685         if(this.grid.autoSizeHeaders){
58686             var h = this.getHeaderCellMeasure(colIndex);
58687             maxWidth = Math.max(maxWidth, h.scrollWidth);
58688         }
58689         var tb, index;
58690         if(this.cm.isLocked(colIndex)){
58691             tb = this.getLockedTable();
58692             index = colIndex;
58693         }else{
58694             tb = this.getBodyTable();
58695             index = colIndex - this.cm.getLockedCount();
58696         }
58697         if(tb && tb.rows){
58698             var rows = tb.rows;
58699             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
58700             for(var i = 0; i < stopIndex; i++){
58701                 var cell = rows[i].childNodes[index].firstChild;
58702                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
58703             }
58704         }
58705         return maxWidth + /*margin for error in IE*/ 5;
58706     },
58707     /**
58708      * Autofit a column to its content.
58709      * @param {Number} colIndex
58710      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
58711      */
58712      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
58713          if(this.cm.isHidden(colIndex)){
58714              return; // can't calc a hidden column
58715          }
58716         if(forceMinSize){
58717             var cid = this.cm.getColumnId(colIndex);
58718             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
58719            if(this.grid.autoSizeHeaders){
58720                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
58721            }
58722         }
58723         var newWidth = this.calcColumnWidth(colIndex);
58724         this.cm.setColumnWidth(colIndex,
58725             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
58726         if(!suppressEvent){
58727             this.grid.fireEvent("columnresize", colIndex, newWidth);
58728         }
58729     },
58730
58731     /**
58732      * Autofits all columns to their content and then expands to fit any extra space in the grid
58733      */
58734      autoSizeColumns : function(){
58735         var cm = this.grid.colModel;
58736         var colCount = cm.getColumnCount();
58737         for(var i = 0; i < colCount; i++){
58738             this.autoSizeColumn(i, true, true);
58739         }
58740         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
58741             this.fitColumns();
58742         }else{
58743             this.updateColumns();
58744             this.layout();
58745         }
58746     },
58747
58748     /**
58749      * Autofits all columns to the grid's width proportionate with their current size
58750      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
58751      */
58752     fitColumns : function(reserveScrollSpace){
58753         var cm = this.grid.colModel;
58754         var colCount = cm.getColumnCount();
58755         var cols = [];
58756         var width = 0;
58757         var i, w;
58758         for (i = 0; i < colCount; i++){
58759             if(!cm.isHidden(i) && !cm.isFixed(i)){
58760                 w = cm.getColumnWidth(i);
58761                 cols.push(i);
58762                 cols.push(w);
58763                 width += w;
58764             }
58765         }
58766         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
58767         if(reserveScrollSpace){
58768             avail -= 17;
58769         }
58770         var frac = (avail - cm.getTotalWidth())/width;
58771         while (cols.length){
58772             w = cols.pop();
58773             i = cols.pop();
58774             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
58775         }
58776         this.updateColumns();
58777         this.layout();
58778     },
58779
58780     onRowSelect : function(rowIndex){
58781         var row = this.getRowComposite(rowIndex);
58782         row.addClass("x-grid-row-selected");
58783     },
58784
58785     onRowDeselect : function(rowIndex){
58786         var row = this.getRowComposite(rowIndex);
58787         row.removeClass("x-grid-row-selected");
58788     },
58789
58790     onCellSelect : function(row, col){
58791         var cell = this.getCell(row, col);
58792         if(cell){
58793             Roo.fly(cell).addClass("x-grid-cell-selected");
58794         }
58795     },
58796
58797     onCellDeselect : function(row, col){
58798         var cell = this.getCell(row, col);
58799         if(cell){
58800             Roo.fly(cell).removeClass("x-grid-cell-selected");
58801         }
58802     },
58803
58804     updateHeaderSortState : function(){
58805         
58806         // sort state can be single { field: xxx, direction : yyy}
58807         // or   { xxx=>ASC , yyy : DESC ..... }
58808         
58809         var mstate = {};
58810         if (!this.ds.multiSort) { 
58811             var state = this.ds.getSortState();
58812             if(!state){
58813                 return;
58814             }
58815             mstate[state.field] = state.direction;
58816             // FIXME... - this is not used here.. but might be elsewhere..
58817             this.sortState = state;
58818             
58819         } else {
58820             mstate = this.ds.sortToggle;
58821         }
58822         //remove existing sort classes..
58823         
58824         var sc = this.sortClasses;
58825         var hds = this.el.select(this.headerSelector).removeClass(sc);
58826         
58827         for(var f in mstate) {
58828         
58829             var sortColumn = this.cm.findColumnIndex(f);
58830             
58831             if(sortColumn != -1){
58832                 var sortDir = mstate[f];        
58833                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
58834             }
58835         }
58836         
58837          
58838         
58839     },
58840
58841
58842     handleHeaderClick : function(g, index,e){
58843         
58844         Roo.log("header click");
58845         
58846         if (Roo.isTouch) {
58847             // touch events on header are handled by context
58848             this.handleHdCtx(g,index,e);
58849             return;
58850         }
58851         
58852         
58853         if(this.headersDisabled){
58854             return;
58855         }
58856         var dm = g.dataSource, cm = g.colModel;
58857         if(!cm.isSortable(index)){
58858             return;
58859         }
58860         g.stopEditing();
58861         
58862         if (dm.multiSort) {
58863             // update the sortOrder
58864             var so = [];
58865             for(var i = 0; i < cm.config.length; i++ ) {
58866                 
58867                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
58868                     continue; // dont' bother, it's not in sort list or being set.
58869                 }
58870                 
58871                 so.push(cm.config[i].dataIndex);
58872             };
58873             dm.sortOrder = so;
58874         }
58875         
58876         
58877         dm.sort(cm.getDataIndex(index));
58878     },
58879
58880
58881     destroy : function(){
58882         if(this.colMenu){
58883             this.colMenu.removeAll();
58884             Roo.menu.MenuMgr.unregister(this.colMenu);
58885             this.colMenu.getEl().remove();
58886             delete this.colMenu;
58887         }
58888         if(this.hmenu){
58889             this.hmenu.removeAll();
58890             Roo.menu.MenuMgr.unregister(this.hmenu);
58891             this.hmenu.getEl().remove();
58892             delete this.hmenu;
58893         }
58894         if(this.grid.enableColumnMove){
58895             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58896             if(dds){
58897                 for(var dd in dds){
58898                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
58899                         var elid = dds[dd].dragElId;
58900                         dds[dd].unreg();
58901                         Roo.get(elid).remove();
58902                     } else if(dds[dd].config.isTarget){
58903                         dds[dd].proxyTop.remove();
58904                         dds[dd].proxyBottom.remove();
58905                         dds[dd].unreg();
58906                     }
58907                     if(Roo.dd.DDM.locationCache[dd]){
58908                         delete Roo.dd.DDM.locationCache[dd];
58909                     }
58910                 }
58911                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
58912             }
58913         }
58914         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
58915         this.bind(null, null);
58916         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
58917     },
58918
58919     handleLockChange : function(){
58920         this.refresh(true);
58921     },
58922
58923     onDenyColumnLock : function(){
58924
58925     },
58926
58927     onDenyColumnHide : function(){
58928
58929     },
58930
58931     handleHdMenuClick : function(item){
58932         var index = this.hdCtxIndex;
58933         var cm = this.cm, ds = this.ds;
58934         switch(item.id){
58935             case "asc":
58936                 ds.sort(cm.getDataIndex(index), "ASC");
58937                 break;
58938             case "desc":
58939                 ds.sort(cm.getDataIndex(index), "DESC");
58940                 break;
58941             case "lock":
58942                 var lc = cm.getLockedCount();
58943                 if(cm.getColumnCount(true) <= lc+1){
58944                     this.onDenyColumnLock();
58945                     return;
58946                 }
58947                 if(lc != index){
58948                     cm.setLocked(index, true, true);
58949                     cm.moveColumn(index, lc);
58950                     this.grid.fireEvent("columnmove", index, lc);
58951                 }else{
58952                     cm.setLocked(index, true);
58953                 }
58954             break;
58955             case "unlock":
58956                 var lc = cm.getLockedCount();
58957                 if((lc-1) != index){
58958                     cm.setLocked(index, false, true);
58959                     cm.moveColumn(index, lc-1);
58960                     this.grid.fireEvent("columnmove", index, lc-1);
58961                 }else{
58962                     cm.setLocked(index, false);
58963                 }
58964             break;
58965             case 'wider': // used to expand cols on touch..
58966             case 'narrow':
58967                 var cw = cm.getColumnWidth(index);
58968                 cw += (item.id == 'wider' ? 1 : -1) * 50;
58969                 cw = Math.max(0, cw);
58970                 cw = Math.min(cw,4000);
58971                 cm.setColumnWidth(index, cw);
58972                 break;
58973                 
58974             default:
58975                 index = cm.getIndexById(item.id.substr(4));
58976                 if(index != -1){
58977                     if(item.checked && cm.getColumnCount(true) <= 1){
58978                         this.onDenyColumnHide();
58979                         return false;
58980                     }
58981                     cm.setHidden(index, item.checked);
58982                 }
58983         }
58984         return true;
58985     },
58986
58987     beforeColMenuShow : function(){
58988         var cm = this.cm,  colCount = cm.getColumnCount();
58989         this.colMenu.removeAll();
58990         for(var i = 0; i < colCount; i++){
58991             this.colMenu.add(new Roo.menu.CheckItem({
58992                 id: "col-"+cm.getColumnId(i),
58993                 text: cm.getColumnHeader(i),
58994                 checked: !cm.isHidden(i),
58995                 hideOnClick:false
58996             }));
58997         }
58998     },
58999
59000     handleHdCtx : function(g, index, e){
59001         e.stopEvent();
59002         var hd = this.getHeaderCell(index);
59003         this.hdCtxIndex = index;
59004         var ms = this.hmenu.items, cm = this.cm;
59005         ms.get("asc").setDisabled(!cm.isSortable(index));
59006         ms.get("desc").setDisabled(!cm.isSortable(index));
59007         if(this.grid.enableColLock !== false){
59008             ms.get("lock").setDisabled(cm.isLocked(index));
59009             ms.get("unlock").setDisabled(!cm.isLocked(index));
59010         }
59011         this.hmenu.show(hd, "tl-bl");
59012     },
59013
59014     handleHdOver : function(e){
59015         var hd = this.findHeaderCell(e.getTarget());
59016         if(hd && !this.headersDisabled){
59017             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
59018                this.fly(hd).addClass("x-grid-hd-over");
59019             }
59020         }
59021     },
59022
59023     handleHdOut : function(e){
59024         var hd = this.findHeaderCell(e.getTarget());
59025         if(hd){
59026             this.fly(hd).removeClass("x-grid-hd-over");
59027         }
59028     },
59029
59030     handleSplitDblClick : function(e, t){
59031         var i = this.getCellIndex(t);
59032         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
59033             this.autoSizeColumn(i, true);
59034             this.layout();
59035         }
59036     },
59037
59038     render : function(){
59039
59040         var cm = this.cm;
59041         var colCount = cm.getColumnCount();
59042
59043         if(this.grid.monitorWindowResize === true){
59044             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
59045         }
59046         var header = this.renderHeaders();
59047         var body = this.templates.body.apply({rows:""});
59048         var html = this.templates.master.apply({
59049             lockedBody: body,
59050             body: body,
59051             lockedHeader: header[0],
59052             header: header[1]
59053         });
59054
59055         //this.updateColumns();
59056
59057         this.grid.getGridEl().dom.innerHTML = html;
59058
59059         this.initElements();
59060         
59061         // a kludge to fix the random scolling effect in webkit
59062         this.el.on("scroll", function() {
59063             this.el.dom.scrollTop=0; // hopefully not recursive..
59064         },this);
59065
59066         this.scroller.on("scroll", this.handleScroll, this);
59067         this.lockedBody.on("mousewheel", this.handleWheel, this);
59068         this.mainBody.on("mousewheel", this.handleWheel, this);
59069
59070         this.mainHd.on("mouseover", this.handleHdOver, this);
59071         this.mainHd.on("mouseout", this.handleHdOut, this);
59072         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
59073                 {delegate: "."+this.splitClass});
59074
59075         this.lockedHd.on("mouseover", this.handleHdOver, this);
59076         this.lockedHd.on("mouseout", this.handleHdOut, this);
59077         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
59078                 {delegate: "."+this.splitClass});
59079
59080         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
59081             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59082         }
59083
59084         this.updateSplitters();
59085
59086         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
59087             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59088             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59089         }
59090
59091         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
59092             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
59093             this.hmenu.add(
59094                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
59095                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
59096             );
59097             if(this.grid.enableColLock !== false){
59098                 this.hmenu.add('-',
59099                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
59100                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
59101                 );
59102             }
59103             if (Roo.isTouch) {
59104                  this.hmenu.add('-',
59105                     {id:"wider", text: this.columnsWiderText},
59106                     {id:"narrow", text: this.columnsNarrowText }
59107                 );
59108                 
59109                  
59110             }
59111             
59112             if(this.grid.enableColumnHide !== false){
59113
59114                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
59115                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
59116                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
59117
59118                 this.hmenu.add('-',
59119                     {id:"columns", text: this.columnsText, menu: this.colMenu}
59120                 );
59121             }
59122             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
59123
59124             this.grid.on("headercontextmenu", this.handleHdCtx, this);
59125         }
59126
59127         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
59128             this.dd = new Roo.grid.GridDragZone(this.grid, {
59129                 ddGroup : this.grid.ddGroup || 'GridDD'
59130             });
59131             
59132         }
59133
59134         /*
59135         for(var i = 0; i < colCount; i++){
59136             if(cm.isHidden(i)){
59137                 this.hideColumn(i);
59138             }
59139             if(cm.config[i].align){
59140                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
59141                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
59142             }
59143         }*/
59144         
59145         this.updateHeaderSortState();
59146
59147         this.beforeInitialResize();
59148         this.layout(true);
59149
59150         // two part rendering gives faster view to the user
59151         this.renderPhase2.defer(1, this);
59152     },
59153
59154     renderPhase2 : function(){
59155         // render the rows now
59156         this.refresh();
59157         if(this.grid.autoSizeColumns){
59158             this.autoSizeColumns();
59159         }
59160     },
59161
59162     beforeInitialResize : function(){
59163
59164     },
59165
59166     onColumnSplitterMoved : function(i, w){
59167         this.userResized = true;
59168         var cm = this.grid.colModel;
59169         cm.setColumnWidth(i, w, true);
59170         var cid = cm.getColumnId(i);
59171         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59172         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59173         this.updateSplitters();
59174         this.layout();
59175         this.grid.fireEvent("columnresize", i, w);
59176     },
59177
59178     syncRowHeights : function(startIndex, endIndex){
59179         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
59180             startIndex = startIndex || 0;
59181             var mrows = this.getBodyTable().rows;
59182             var lrows = this.getLockedTable().rows;
59183             var len = mrows.length-1;
59184             endIndex = Math.min(endIndex || len, len);
59185             for(var i = startIndex; i <= endIndex; i++){
59186                 var m = mrows[i], l = lrows[i];
59187                 var h = Math.max(m.offsetHeight, l.offsetHeight);
59188                 m.style.height = l.style.height = h + "px";
59189             }
59190         }
59191     },
59192
59193     layout : function(initialRender, is2ndPass)
59194     {
59195         var g = this.grid;
59196         var auto = g.autoHeight;
59197         var scrollOffset = 16;
59198         var c = g.getGridEl(), cm = this.cm,
59199                 expandCol = g.autoExpandColumn,
59200                 gv = this;
59201         //c.beginMeasure();
59202
59203         if(!c.dom.offsetWidth){ // display:none?
59204             if(initialRender){
59205                 this.lockedWrap.show();
59206                 this.mainWrap.show();
59207             }
59208             return;
59209         }
59210
59211         var hasLock = this.cm.isLocked(0);
59212
59213         var tbh = this.headerPanel.getHeight();
59214         var bbh = this.footerPanel.getHeight();
59215
59216         if(auto){
59217             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
59218             var newHeight = ch + c.getBorderWidth("tb");
59219             if(g.maxHeight){
59220                 newHeight = Math.min(g.maxHeight, newHeight);
59221             }
59222             c.setHeight(newHeight);
59223         }
59224
59225         if(g.autoWidth){
59226             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
59227         }
59228
59229         var s = this.scroller;
59230
59231         var csize = c.getSize(true);
59232
59233         this.el.setSize(csize.width, csize.height);
59234
59235         this.headerPanel.setWidth(csize.width);
59236         this.footerPanel.setWidth(csize.width);
59237
59238         var hdHeight = this.mainHd.getHeight();
59239         var vw = csize.width;
59240         var vh = csize.height - (tbh + bbh);
59241
59242         s.setSize(vw, vh);
59243
59244         var bt = this.getBodyTable();
59245         
59246         if(cm.getLockedCount() == cm.config.length){
59247             bt = this.getLockedTable();
59248         }
59249         
59250         var ltWidth = hasLock ?
59251                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
59252
59253         var scrollHeight = bt.offsetHeight;
59254         var scrollWidth = ltWidth + bt.offsetWidth;
59255         var vscroll = false, hscroll = false;
59256
59257         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
59258
59259         var lw = this.lockedWrap, mw = this.mainWrap;
59260         var lb = this.lockedBody, mb = this.mainBody;
59261
59262         setTimeout(function(){
59263             var t = s.dom.offsetTop;
59264             var w = s.dom.clientWidth,
59265                 h = s.dom.clientHeight;
59266
59267             lw.setTop(t);
59268             lw.setSize(ltWidth, h);
59269
59270             mw.setLeftTop(ltWidth, t);
59271             mw.setSize(w-ltWidth, h);
59272
59273             lb.setHeight(h-hdHeight);
59274             mb.setHeight(h-hdHeight);
59275
59276             if(is2ndPass !== true && !gv.userResized && expandCol){
59277                 // high speed resize without full column calculation
59278                 
59279                 var ci = cm.getIndexById(expandCol);
59280                 if (ci < 0) {
59281                     ci = cm.findColumnIndex(expandCol);
59282                 }
59283                 ci = Math.max(0, ci); // make sure it's got at least the first col.
59284                 var expandId = cm.getColumnId(ci);
59285                 var  tw = cm.getTotalWidth(false);
59286                 var currentWidth = cm.getColumnWidth(ci);
59287                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
59288                 if(currentWidth != cw){
59289                     cm.setColumnWidth(ci, cw, true);
59290                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59291                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59292                     gv.updateSplitters();
59293                     gv.layout(false, true);
59294                 }
59295             }
59296
59297             if(initialRender){
59298                 lw.show();
59299                 mw.show();
59300             }
59301             //c.endMeasure();
59302         }, 10);
59303     },
59304
59305     onWindowResize : function(){
59306         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
59307             return;
59308         }
59309         this.layout();
59310     },
59311
59312     appendFooter : function(parentEl){
59313         return null;
59314     },
59315
59316     sortAscText : "Sort Ascending",
59317     sortDescText : "Sort Descending",
59318     lockText : "Lock Column",
59319     unlockText : "Unlock Column",
59320     columnsText : "Columns",
59321  
59322     columnsWiderText : "Wider",
59323     columnsNarrowText : "Thinner"
59324 });
59325
59326
59327 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
59328     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
59329     this.proxy.el.addClass('x-grid3-col-dd');
59330 };
59331
59332 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
59333     handleMouseDown : function(e){
59334
59335     },
59336
59337     callHandleMouseDown : function(e){
59338         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
59339     }
59340 });
59341 /*
59342  * Based on:
59343  * Ext JS Library 1.1.1
59344  * Copyright(c) 2006-2007, Ext JS, LLC.
59345  *
59346  * Originally Released Under LGPL - original licence link has changed is not relivant.
59347  *
59348  * Fork - LGPL
59349  * <script type="text/javascript">
59350  */
59351  /**
59352  * @extends Roo.dd.DDProxy
59353  * @class Roo.grid.SplitDragZone
59354  * Support for Column Header resizing
59355  * @constructor
59356  * @param {Object} config
59357  */
59358 // private
59359 // This is a support class used internally by the Grid components
59360 Roo.grid.SplitDragZone = function(grid, hd, hd2){
59361     this.grid = grid;
59362     this.view = grid.getView();
59363     this.proxy = this.view.resizeProxy;
59364     Roo.grid.SplitDragZone.superclass.constructor.call(
59365         this,
59366         hd, // ID
59367         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
59368         {  // CONFIG
59369             dragElId : Roo.id(this.proxy.dom),
59370             resizeFrame:false
59371         }
59372     );
59373     
59374     this.setHandleElId(Roo.id(hd));
59375     if (hd2 !== false) {
59376         this.setOuterHandleElId(Roo.id(hd2));
59377     }
59378     
59379     this.scroll = false;
59380 };
59381 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
59382     fly: Roo.Element.fly,
59383
59384     b4StartDrag : function(x, y){
59385         this.view.headersDisabled = true;
59386         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
59387                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
59388         );
59389         this.proxy.setHeight(h);
59390         
59391         // for old system colWidth really stored the actual width?
59392         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
59393         // which in reality did not work.. - it worked only for fixed sizes
59394         // for resizable we need to use actual sizes.
59395         var w = this.cm.getColumnWidth(this.cellIndex);
59396         if (!this.view.mainWrap) {
59397             // bootstrap.
59398             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
59399         }
59400         
59401         
59402         
59403         // this was w-this.grid.minColumnWidth;
59404         // doesnt really make sense? - w = thie curren width or the rendered one?
59405         var minw = Math.max(w-this.grid.minColumnWidth, 0);
59406         this.resetConstraints();
59407         this.setXConstraint(minw, 1000);
59408         this.setYConstraint(0, 0);
59409         this.minX = x - minw;
59410         this.maxX = x + 1000;
59411         this.startPos = x;
59412         if (!this.view.mainWrap) { // this is Bootstrap code..
59413             this.getDragEl().style.display='block';
59414         }
59415         
59416         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
59417     },
59418
59419
59420     handleMouseDown : function(e){
59421         ev = Roo.EventObject.setEvent(e);
59422         var t = this.fly(ev.getTarget());
59423         if(t.hasClass("x-grid-split")){
59424             this.cellIndex = this.view.getCellIndex(t.dom);
59425             this.split = t.dom;
59426             this.cm = this.grid.colModel;
59427             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
59428                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
59429             }
59430         }
59431     },
59432
59433     endDrag : function(e){
59434         this.view.headersDisabled = false;
59435         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
59436         var diff = endX - this.startPos;
59437         // 
59438         var w = this.cm.getColumnWidth(this.cellIndex);
59439         if (!this.view.mainWrap) {
59440             w = 0;
59441         }
59442         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
59443     },
59444
59445     autoOffset : function(){
59446         this.setDelta(0,0);
59447     }
59448 });/*
59449  * Based on:
59450  * Ext JS Library 1.1.1
59451  * Copyright(c) 2006-2007, Ext JS, LLC.
59452  *
59453  * Originally Released Under LGPL - original licence link has changed is not relivant.
59454  *
59455  * Fork - LGPL
59456  * <script type="text/javascript">
59457  */
59458  
59459 // private
59460 // This is a support class used internally by the Grid components
59461 Roo.grid.GridDragZone = function(grid, config){
59462     this.view = grid.getView();
59463     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
59464     if(this.view.lockedBody){
59465         this.setHandleElId(Roo.id(this.view.mainBody.dom));
59466         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
59467     }
59468     this.scroll = false;
59469     this.grid = grid;
59470     this.ddel = document.createElement('div');
59471     this.ddel.className = 'x-grid-dd-wrap';
59472 };
59473
59474 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
59475     ddGroup : "GridDD",
59476
59477     getDragData : function(e){
59478         var t = Roo.lib.Event.getTarget(e);
59479         var rowIndex = this.view.findRowIndex(t);
59480         var sm = this.grid.selModel;
59481             
59482         //Roo.log(rowIndex);
59483         
59484         if (sm.getSelectedCell) {
59485             // cell selection..
59486             if (!sm.getSelectedCell()) {
59487                 return false;
59488             }
59489             if (rowIndex != sm.getSelectedCell()[0]) {
59490                 return false;
59491             }
59492         
59493         }
59494         if (sm.getSelections && sm.getSelections().length < 1) {
59495             return false;
59496         }
59497         
59498         
59499         // before it used to all dragging of unseleted... - now we dont do that.
59500         if(rowIndex !== false){
59501             
59502             // if editorgrid.. 
59503             
59504             
59505             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
59506                
59507             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
59508               //  
59509             //}
59510             if (e.hasModifier()){
59511                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
59512             }
59513             
59514             Roo.log("getDragData");
59515             
59516             return {
59517                 grid: this.grid,
59518                 ddel: this.ddel,
59519                 rowIndex: rowIndex,
59520                 selections: sm.getSelections ? sm.getSelections() : (
59521                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
59522             };
59523         }
59524         return false;
59525     },
59526     
59527     
59528     onInitDrag : function(e){
59529         var data = this.dragData;
59530         this.ddel.innerHTML = this.grid.getDragDropText();
59531         this.proxy.update(this.ddel);
59532         // fire start drag?
59533     },
59534
59535     afterRepair : function(){
59536         this.dragging = false;
59537     },
59538
59539     getRepairXY : function(e, data){
59540         return false;
59541     },
59542
59543     onEndDrag : function(data, e){
59544         // fire end drag?
59545     },
59546
59547     onValidDrop : function(dd, e, id){
59548         // fire drag drop?
59549         this.hideProxy();
59550     },
59551
59552     beforeInvalidDrop : function(e, id){
59553
59554     }
59555 });/*
59556  * Based on:
59557  * Ext JS Library 1.1.1
59558  * Copyright(c) 2006-2007, Ext JS, LLC.
59559  *
59560  * Originally Released Under LGPL - original licence link has changed is not relivant.
59561  *
59562  * Fork - LGPL
59563  * <script type="text/javascript">
59564  */
59565  
59566
59567 /**
59568  * @class Roo.grid.ColumnModel
59569  * @extends Roo.util.Observable
59570  * This is the default implementation of a ColumnModel used by the Grid. It defines
59571  * the columns in the grid.
59572  * <br>Usage:<br>
59573  <pre><code>
59574  var colModel = new Roo.grid.ColumnModel([
59575         {header: "Ticker", width: 60, sortable: true, locked: true},
59576         {header: "Company Name", width: 150, sortable: true},
59577         {header: "Market Cap.", width: 100, sortable: true},
59578         {header: "$ Sales", width: 100, sortable: true, renderer: money},
59579         {header: "Employees", width: 100, sortable: true, resizable: false}
59580  ]);
59581  </code></pre>
59582  * <p>
59583  
59584  * The config options listed for this class are options which may appear in each
59585  * individual column definition.
59586  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
59587  * @constructor
59588  * @param {Object} config An Array of column config objects. See this class's
59589  * config objects for details.
59590 */
59591 Roo.grid.ColumnModel = function(config){
59592         /**
59593      * The config passed into the constructor
59594      */
59595     this.config = []; //config;
59596     this.lookup = {};
59597
59598     // if no id, create one
59599     // if the column does not have a dataIndex mapping,
59600     // map it to the order it is in the config
59601     for(var i = 0, len = config.length; i < len; i++){
59602         this.addColumn(config[i]);
59603         
59604     }
59605
59606     /**
59607      * The width of columns which have no width specified (defaults to 100)
59608      * @type Number
59609      */
59610     this.defaultWidth = 100;
59611
59612     /**
59613      * Default sortable of columns which have no sortable specified (defaults to false)
59614      * @type Boolean
59615      */
59616     this.defaultSortable = false;
59617
59618     this.addEvents({
59619         /**
59620              * @event widthchange
59621              * Fires when the width of a column changes.
59622              * @param {ColumnModel} this
59623              * @param {Number} columnIndex The column index
59624              * @param {Number} newWidth The new width
59625              */
59626             "widthchange": true,
59627         /**
59628              * @event headerchange
59629              * Fires when the text of a header changes.
59630              * @param {ColumnModel} this
59631              * @param {Number} columnIndex The column index
59632              * @param {Number} newText The new header text
59633              */
59634             "headerchange": true,
59635         /**
59636              * @event hiddenchange
59637              * Fires when a column is hidden or "unhidden".
59638              * @param {ColumnModel} this
59639              * @param {Number} columnIndex The column index
59640              * @param {Boolean} hidden true if hidden, false otherwise
59641              */
59642             "hiddenchange": true,
59643             /**
59644          * @event columnmoved
59645          * Fires when a column is moved.
59646          * @param {ColumnModel} this
59647          * @param {Number} oldIndex
59648          * @param {Number} newIndex
59649          */
59650         "columnmoved" : true,
59651         /**
59652          * @event columlockchange
59653          * Fires when a column's locked state is changed
59654          * @param {ColumnModel} this
59655          * @param {Number} colIndex
59656          * @param {Boolean} locked true if locked
59657          */
59658         "columnlockchange" : true
59659     });
59660     Roo.grid.ColumnModel.superclass.constructor.call(this);
59661 };
59662 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
59663     /**
59664      * @cfg {String} header The header text to display in the Grid view.
59665      */
59666         /**
59667      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
59668      */
59669         /**
59670      * @cfg {String} smHeader Header at Bootsrap Small width
59671      */
59672         /**
59673      * @cfg {String} mdHeader Header at Bootsrap Medium width
59674      */
59675         /**
59676      * @cfg {String} lgHeader Header at Bootsrap Large width
59677      */
59678         /**
59679      * @cfg {String} xlHeader Header at Bootsrap extra Large width
59680      */
59681     /**
59682      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
59683      * {@link Roo.data.Record} definition from which to draw the column's value. If not
59684      * specified, the column's index is used as an index into the Record's data Array.
59685      */
59686     /**
59687      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
59688      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
59689      */
59690     /**
59691      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
59692      * Defaults to the value of the {@link #defaultSortable} property.
59693      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
59694      */
59695     /**
59696      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
59697      */
59698     /**
59699      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
59700      */
59701     /**
59702      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
59703      */
59704     /**
59705      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
59706      */
59707     /**
59708      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
59709      * given the cell's data value. See {@link #setRenderer}. If not specified, the
59710      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
59711      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
59712      */
59713        /**
59714      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
59715      */
59716     /**
59717      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
59718      */
59719     /**
59720      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
59721      */
59722     /**
59723      * @cfg {String} cursor (Optional)
59724      */
59725     /**
59726      * @cfg {String} tooltip (Optional)
59727      */
59728     /**
59729      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
59730      */
59731     /**
59732      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
59733      */
59734     /**
59735      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
59736      */
59737     /**
59738      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
59739      */
59740         /**
59741      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
59742      */
59743     /**
59744      * Returns the id of the column at the specified index.
59745      * @param {Number} index The column index
59746      * @return {String} the id
59747      */
59748     getColumnId : function(index){
59749         return this.config[index].id;
59750     },
59751
59752     /**
59753      * Returns the column for a specified id.
59754      * @param {String} id The column id
59755      * @return {Object} the column
59756      */
59757     getColumnById : function(id){
59758         return this.lookup[id];
59759     },
59760
59761     
59762     /**
59763      * Returns the column Object for a specified dataIndex.
59764      * @param {String} dataIndex The column dataIndex
59765      * @return {Object|Boolean} the column or false if not found
59766      */
59767     getColumnByDataIndex: function(dataIndex){
59768         var index = this.findColumnIndex(dataIndex);
59769         return index > -1 ? this.config[index] : false;
59770     },
59771     
59772     /**
59773      * Returns the index for a specified column id.
59774      * @param {String} id The column id
59775      * @return {Number} the index, or -1 if not found
59776      */
59777     getIndexById : function(id){
59778         for(var i = 0, len = this.config.length; i < len; i++){
59779             if(this.config[i].id == id){
59780                 return i;
59781             }
59782         }
59783         return -1;
59784     },
59785     
59786     /**
59787      * Returns the index for a specified column dataIndex.
59788      * @param {String} dataIndex The column dataIndex
59789      * @return {Number} the index, or -1 if not found
59790      */
59791     
59792     findColumnIndex : function(dataIndex){
59793         for(var i = 0, len = this.config.length; i < len; i++){
59794             if(this.config[i].dataIndex == dataIndex){
59795                 return i;
59796             }
59797         }
59798         return -1;
59799     },
59800     
59801     
59802     moveColumn : function(oldIndex, newIndex){
59803         var c = this.config[oldIndex];
59804         this.config.splice(oldIndex, 1);
59805         this.config.splice(newIndex, 0, c);
59806         this.dataMap = null;
59807         this.fireEvent("columnmoved", this, oldIndex, newIndex);
59808     },
59809
59810     isLocked : function(colIndex){
59811         return this.config[colIndex].locked === true;
59812     },
59813
59814     setLocked : function(colIndex, value, suppressEvent){
59815         if(this.isLocked(colIndex) == value){
59816             return;
59817         }
59818         this.config[colIndex].locked = value;
59819         if(!suppressEvent){
59820             this.fireEvent("columnlockchange", this, colIndex, value);
59821         }
59822     },
59823
59824     getTotalLockedWidth : function(){
59825         var totalWidth = 0;
59826         for(var i = 0; i < this.config.length; i++){
59827             if(this.isLocked(i) && !this.isHidden(i)){
59828                 this.totalWidth += this.getColumnWidth(i);
59829             }
59830         }
59831         return totalWidth;
59832     },
59833
59834     getLockedCount : function(){
59835         for(var i = 0, len = this.config.length; i < len; i++){
59836             if(!this.isLocked(i)){
59837                 return i;
59838             }
59839         }
59840         
59841         return this.config.length;
59842     },
59843
59844     /**
59845      * Returns the number of columns.
59846      * @return {Number}
59847      */
59848     getColumnCount : function(visibleOnly){
59849         if(visibleOnly === true){
59850             var c = 0;
59851             for(var i = 0, len = this.config.length; i < len; i++){
59852                 if(!this.isHidden(i)){
59853                     c++;
59854                 }
59855             }
59856             return c;
59857         }
59858         return this.config.length;
59859     },
59860
59861     /**
59862      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
59863      * @param {Function} fn
59864      * @param {Object} scope (optional)
59865      * @return {Array} result
59866      */
59867     getColumnsBy : function(fn, scope){
59868         var r = [];
59869         for(var i = 0, len = this.config.length; i < len; i++){
59870             var c = this.config[i];
59871             if(fn.call(scope||this, c, i) === true){
59872                 r[r.length] = c;
59873             }
59874         }
59875         return r;
59876     },
59877
59878     /**
59879      * Returns true if the specified column is sortable.
59880      * @param {Number} col The column index
59881      * @return {Boolean}
59882      */
59883     isSortable : function(col){
59884         if(typeof this.config[col].sortable == "undefined"){
59885             return this.defaultSortable;
59886         }
59887         return this.config[col].sortable;
59888     },
59889
59890     /**
59891      * Returns the rendering (formatting) function defined for the column.
59892      * @param {Number} col The column index.
59893      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
59894      */
59895     getRenderer : function(col){
59896         if(!this.config[col].renderer){
59897             return Roo.grid.ColumnModel.defaultRenderer;
59898         }
59899         return this.config[col].renderer;
59900     },
59901
59902     /**
59903      * Sets the rendering (formatting) function for a column.
59904      * @param {Number} col The column index
59905      * @param {Function} fn The function to use to process the cell's raw data
59906      * to return HTML markup for the grid view. The render function is called with
59907      * the following parameters:<ul>
59908      * <li>Data value.</li>
59909      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
59910      * <li>css A CSS style string to apply to the table cell.</li>
59911      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
59912      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
59913      * <li>Row index</li>
59914      * <li>Column index</li>
59915      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
59916      */
59917     setRenderer : function(col, fn){
59918         this.config[col].renderer = fn;
59919     },
59920
59921     /**
59922      * Returns the width for the specified column.
59923      * @param {Number} col The column index
59924      * @param (optional) {String} gridSize bootstrap width size.
59925      * @return {Number}
59926      */
59927     getColumnWidth : function(col, gridSize)
59928         {
59929                 var cfg = this.config[col];
59930                 
59931                 if (typeof(gridSize) == 'undefined') {
59932                         return cfg.width * 1 || this.defaultWidth;
59933                 }
59934                 if (gridSize === false) { // if we set it..
59935                         return cfg.width || false;
59936                 }
59937                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
59938                 
59939                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
59940                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
59941                                 continue;
59942                         }
59943                         return cfg[ sizes[i] ];
59944                 }
59945                 return 1;
59946                 
59947     },
59948
59949     /**
59950      * Sets the width for a column.
59951      * @param {Number} col The column index
59952      * @param {Number} width The new width
59953      */
59954     setColumnWidth : function(col, width, suppressEvent){
59955         this.config[col].width = width;
59956         this.totalWidth = null;
59957         if(!suppressEvent){
59958              this.fireEvent("widthchange", this, col, width);
59959         }
59960     },
59961
59962     /**
59963      * Returns the total width of all columns.
59964      * @param {Boolean} includeHidden True to include hidden column widths
59965      * @return {Number}
59966      */
59967     getTotalWidth : function(includeHidden){
59968         if(!this.totalWidth){
59969             this.totalWidth = 0;
59970             for(var i = 0, len = this.config.length; i < len; i++){
59971                 if(includeHidden || !this.isHidden(i)){
59972                     this.totalWidth += this.getColumnWidth(i);
59973                 }
59974             }
59975         }
59976         return this.totalWidth;
59977     },
59978
59979     /**
59980      * Returns the header for the specified column.
59981      * @param {Number} col The column index
59982      * @return {String}
59983      */
59984     getColumnHeader : function(col){
59985         return this.config[col].header;
59986     },
59987
59988     /**
59989      * Sets the header for a column.
59990      * @param {Number} col The column index
59991      * @param {String} header The new header
59992      */
59993     setColumnHeader : function(col, header){
59994         this.config[col].header = header;
59995         this.fireEvent("headerchange", this, col, header);
59996     },
59997
59998     /**
59999      * Returns the tooltip for the specified column.
60000      * @param {Number} col The column index
60001      * @return {String}
60002      */
60003     getColumnTooltip : function(col){
60004             return this.config[col].tooltip;
60005     },
60006     /**
60007      * Sets the tooltip for a column.
60008      * @param {Number} col The column index
60009      * @param {String} tooltip The new tooltip
60010      */
60011     setColumnTooltip : function(col, tooltip){
60012             this.config[col].tooltip = tooltip;
60013     },
60014
60015     /**
60016      * Returns the dataIndex for the specified column.
60017      * @param {Number} col The column index
60018      * @return {Number}
60019      */
60020     getDataIndex : function(col){
60021         return this.config[col].dataIndex;
60022     },
60023
60024     /**
60025      * Sets the dataIndex for a column.
60026      * @param {Number} col The column index
60027      * @param {Number} dataIndex The new dataIndex
60028      */
60029     setDataIndex : function(col, dataIndex){
60030         this.config[col].dataIndex = dataIndex;
60031     },
60032
60033     
60034     
60035     /**
60036      * Returns true if the cell is editable.
60037      * @param {Number} colIndex The column index
60038      * @param {Number} rowIndex The row index - this is nto actually used..?
60039      * @return {Boolean}
60040      */
60041     isCellEditable : function(colIndex, rowIndex){
60042         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
60043     },
60044
60045     /**
60046      * Returns the editor defined for the cell/column.
60047      * return false or null to disable editing.
60048      * @param {Number} colIndex The column index
60049      * @param {Number} rowIndex The row index
60050      * @return {Object}
60051      */
60052     getCellEditor : function(colIndex, rowIndex){
60053         return this.config[colIndex].editor;
60054     },
60055
60056     /**
60057      * Sets if a column is editable.
60058      * @param {Number} col The column index
60059      * @param {Boolean} editable True if the column is editable
60060      */
60061     setEditable : function(col, editable){
60062         this.config[col].editable = editable;
60063     },
60064
60065
60066     /**
60067      * Returns true if the column is hidden.
60068      * @param {Number} colIndex The column index
60069      * @return {Boolean}
60070      */
60071     isHidden : function(colIndex){
60072         return this.config[colIndex].hidden;
60073     },
60074
60075
60076     /**
60077      * Returns true if the column width cannot be changed
60078      */
60079     isFixed : function(colIndex){
60080         return this.config[colIndex].fixed;
60081     },
60082
60083     /**
60084      * Returns true if the column can be resized
60085      * @return {Boolean}
60086      */
60087     isResizable : function(colIndex){
60088         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
60089     },
60090     /**
60091      * Sets if a column is hidden.
60092      * @param {Number} colIndex The column index
60093      * @param {Boolean} hidden True if the column is hidden
60094      */
60095     setHidden : function(colIndex, hidden){
60096         this.config[colIndex].hidden = hidden;
60097         this.totalWidth = null;
60098         this.fireEvent("hiddenchange", this, colIndex, hidden);
60099     },
60100
60101     /**
60102      * Sets the editor for a column.
60103      * @param {Number} col The column index
60104      * @param {Object} editor The editor object
60105      */
60106     setEditor : function(col, editor){
60107         this.config[col].editor = editor;
60108     },
60109     /**
60110      * Add a column (experimental...) - defaults to adding to the end..
60111      * @param {Object} config 
60112     */
60113     addColumn : function(c)
60114     {
60115     
60116         var i = this.config.length;
60117         this.config[i] = c;
60118         
60119         if(typeof c.dataIndex == "undefined"){
60120             c.dataIndex = i;
60121         }
60122         if(typeof c.renderer == "string"){
60123             c.renderer = Roo.util.Format[c.renderer];
60124         }
60125         if(typeof c.id == "undefined"){
60126             c.id = Roo.id();
60127         }
60128         if(c.editor && c.editor.xtype){
60129             c.editor  = Roo.factory(c.editor, Roo.grid);
60130         }
60131         if(c.editor && c.editor.isFormField){
60132             c.editor = new Roo.grid.GridEditor(c.editor);
60133         }
60134         this.lookup[c.id] = c;
60135     }
60136     
60137 });
60138
60139 Roo.grid.ColumnModel.defaultRenderer = function(value)
60140 {
60141     if(typeof value == "object") {
60142         return value;
60143     }
60144         if(typeof value == "string" && value.length < 1){
60145             return "&#160;";
60146         }
60147     
60148         return String.format("{0}", value);
60149 };
60150
60151 // Alias for backwards compatibility
60152 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
60153 /*
60154  * Based on:
60155  * Ext JS Library 1.1.1
60156  * Copyright(c) 2006-2007, Ext JS, LLC.
60157  *
60158  * Originally Released Under LGPL - original licence link has changed is not relivant.
60159  *
60160  * Fork - LGPL
60161  * <script type="text/javascript">
60162  */
60163
60164 /**
60165  * @class Roo.grid.AbstractSelectionModel
60166  * @extends Roo.util.Observable
60167  * @abstract
60168  * Abstract base class for grid SelectionModels.  It provides the interface that should be
60169  * implemented by descendant classes.  This class should not be directly instantiated.
60170  * @constructor
60171  */
60172 Roo.grid.AbstractSelectionModel = function(){
60173     this.locked = false;
60174     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
60175 };
60176
60177 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
60178     /** @ignore Called by the grid automatically. Do not call directly. */
60179     init : function(grid){
60180         this.grid = grid;
60181         this.initEvents();
60182     },
60183
60184     /**
60185      * Locks the selections.
60186      */
60187     lock : function(){
60188         this.locked = true;
60189     },
60190
60191     /**
60192      * Unlocks the selections.
60193      */
60194     unlock : function(){
60195         this.locked = false;
60196     },
60197
60198     /**
60199      * Returns true if the selections are locked.
60200      * @return {Boolean}
60201      */
60202     isLocked : function(){
60203         return this.locked;
60204     }
60205 });/*
60206  * Based on:
60207  * Ext JS Library 1.1.1
60208  * Copyright(c) 2006-2007, Ext JS, LLC.
60209  *
60210  * Originally Released Under LGPL - original licence link has changed is not relivant.
60211  *
60212  * Fork - LGPL
60213  * <script type="text/javascript">
60214  */
60215 /**
60216  * @extends Roo.grid.AbstractSelectionModel
60217  * @class Roo.grid.RowSelectionModel
60218  * The default SelectionModel used by {@link Roo.grid.Grid}.
60219  * It supports multiple selections and keyboard selection/navigation. 
60220  * @constructor
60221  * @param {Object} config
60222  */
60223 Roo.grid.RowSelectionModel = function(config){
60224     Roo.apply(this, config);
60225     this.selections = new Roo.util.MixedCollection(false, function(o){
60226         return o.id;
60227     });
60228
60229     this.last = false;
60230     this.lastActive = false;
60231
60232     this.addEvents({
60233         /**
60234         * @event selectionchange
60235         * Fires when the selection changes
60236         * @param {SelectionModel} this
60237         */
60238        "selectionchange" : true,
60239        /**
60240         * @event afterselectionchange
60241         * Fires after the selection changes (eg. by key press or clicking)
60242         * @param {SelectionModel} this
60243         */
60244        "afterselectionchange" : true,
60245        /**
60246         * @event beforerowselect
60247         * Fires when a row is selected being selected, return false to cancel.
60248         * @param {SelectionModel} this
60249         * @param {Number} rowIndex The selected index
60250         * @param {Boolean} keepExisting False if other selections will be cleared
60251         */
60252        "beforerowselect" : true,
60253        /**
60254         * @event rowselect
60255         * Fires when a row is selected.
60256         * @param {SelectionModel} this
60257         * @param {Number} rowIndex The selected index
60258         * @param {Roo.data.Record} r The record
60259         */
60260        "rowselect" : true,
60261        /**
60262         * @event rowdeselect
60263         * Fires when a row is deselected.
60264         * @param {SelectionModel} this
60265         * @param {Number} rowIndex The selected index
60266         */
60267         "rowdeselect" : true
60268     });
60269     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
60270     this.locked = false;
60271 };
60272
60273 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
60274     /**
60275      * @cfg {Boolean} singleSelect
60276      * True to allow selection of only one row at a time (defaults to false)
60277      */
60278     singleSelect : false,
60279
60280     // private
60281     initEvents : function(){
60282
60283         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
60284             this.grid.on("mousedown", this.handleMouseDown, this);
60285         }else{ // allow click to work like normal
60286             this.grid.on("rowclick", this.handleDragableRowClick, this);
60287         }
60288         // bootstrap does not have a view..
60289         var view = this.grid.view ? this.grid.view : this.grid;
60290         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
60291             "up" : function(e){
60292                 if(!e.shiftKey){
60293                     this.selectPrevious(e.shiftKey);
60294                 }else if(this.last !== false && this.lastActive !== false){
60295                     var last = this.last;
60296                     this.selectRange(this.last,  this.lastActive-1);
60297                     view.focusRow(this.lastActive);
60298                     if(last !== false){
60299                         this.last = last;
60300                     }
60301                 }else{
60302                     this.selectFirstRow();
60303                 }
60304                 this.fireEvent("afterselectionchange", this);
60305             },
60306             "down" : function(e){
60307                 if(!e.shiftKey){
60308                     this.selectNext(e.shiftKey);
60309                 }else if(this.last !== false && this.lastActive !== false){
60310                     var last = this.last;
60311                     this.selectRange(this.last,  this.lastActive+1);
60312                     view.focusRow(this.lastActive);
60313                     if(last !== false){
60314                         this.last = last;
60315                     }
60316                 }else{
60317                     this.selectFirstRow();
60318                 }
60319                 this.fireEvent("afterselectionchange", this);
60320             },
60321             scope: this
60322         });
60323
60324          
60325         view.on("refresh", this.onRefresh, this);
60326         view.on("rowupdated", this.onRowUpdated, this);
60327         view.on("rowremoved", this.onRemove, this);
60328     },
60329
60330     // private
60331     onRefresh : function(){
60332         var ds = this.grid.ds, i, v = this.grid.view;
60333         var s = this.selections;
60334         s.each(function(r){
60335             if((i = ds.indexOfId(r.id)) != -1){
60336                 v.onRowSelect(i);
60337                 s.add(ds.getAt(i)); // updating the selection relate data
60338             }else{
60339                 s.remove(r);
60340             }
60341         });
60342     },
60343
60344     // private
60345     onRemove : function(v, index, r){
60346         this.selections.remove(r);
60347     },
60348
60349     // private
60350     onRowUpdated : function(v, index, r){
60351         if(this.isSelected(r)){
60352             v.onRowSelect(index);
60353         }
60354     },
60355
60356     /**
60357      * Select records.
60358      * @param {Array} records The records to select
60359      * @param {Boolean} keepExisting (optional) True to keep existing selections
60360      */
60361     selectRecords : function(records, keepExisting){
60362         if(!keepExisting){
60363             this.clearSelections();
60364         }
60365         var ds = this.grid.ds;
60366         for(var i = 0, len = records.length; i < len; i++){
60367             this.selectRow(ds.indexOf(records[i]), true);
60368         }
60369     },
60370
60371     /**
60372      * Gets the number of selected rows.
60373      * @return {Number}
60374      */
60375     getCount : function(){
60376         return this.selections.length;
60377     },
60378
60379     /**
60380      * Selects the first row in the grid.
60381      */
60382     selectFirstRow : function(){
60383         this.selectRow(0);
60384     },
60385
60386     /**
60387      * Select the last row.
60388      * @param {Boolean} keepExisting (optional) True to keep existing selections
60389      */
60390     selectLastRow : function(keepExisting){
60391         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
60392     },
60393
60394     /**
60395      * Selects the row immediately following the last selected row.
60396      * @param {Boolean} keepExisting (optional) True to keep existing selections
60397      */
60398     selectNext : function(keepExisting){
60399         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
60400             this.selectRow(this.last+1, keepExisting);
60401             var view = this.grid.view ? this.grid.view : this.grid;
60402             view.focusRow(this.last);
60403         }
60404     },
60405
60406     /**
60407      * Selects the row that precedes the last selected row.
60408      * @param {Boolean} keepExisting (optional) True to keep existing selections
60409      */
60410     selectPrevious : function(keepExisting){
60411         if(this.last){
60412             this.selectRow(this.last-1, keepExisting);
60413             var view = this.grid.view ? this.grid.view : this.grid;
60414             view.focusRow(this.last);
60415         }
60416     },
60417
60418     /**
60419      * Returns the selected records
60420      * @return {Array} Array of selected records
60421      */
60422     getSelections : function(){
60423         return [].concat(this.selections.items);
60424     },
60425
60426     /**
60427      * Returns the first selected record.
60428      * @return {Record}
60429      */
60430     getSelected : function(){
60431         return this.selections.itemAt(0);
60432     },
60433
60434
60435     /**
60436      * Clears all selections.
60437      */
60438     clearSelections : function(fast){
60439         if(this.locked) {
60440             return;
60441         }
60442         if(fast !== true){
60443             var ds = this.grid.ds;
60444             var s = this.selections;
60445             s.each(function(r){
60446                 this.deselectRow(ds.indexOfId(r.id));
60447             }, this);
60448             s.clear();
60449         }else{
60450             this.selections.clear();
60451         }
60452         this.last = false;
60453     },
60454
60455
60456     /**
60457      * Selects all rows.
60458      */
60459     selectAll : function(){
60460         if(this.locked) {
60461             return;
60462         }
60463         this.selections.clear();
60464         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
60465             this.selectRow(i, true);
60466         }
60467     },
60468
60469     /**
60470      * Returns True if there is a selection.
60471      * @return {Boolean}
60472      */
60473     hasSelection : function(){
60474         return this.selections.length > 0;
60475     },
60476
60477     /**
60478      * Returns True if the specified row is selected.
60479      * @param {Number/Record} record The record or index of the record to check
60480      * @return {Boolean}
60481      */
60482     isSelected : function(index){
60483         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
60484         return (r && this.selections.key(r.id) ? true : false);
60485     },
60486
60487     /**
60488      * Returns True if the specified record id is selected.
60489      * @param {String} id The id of record to check
60490      * @return {Boolean}
60491      */
60492     isIdSelected : function(id){
60493         return (this.selections.key(id) ? true : false);
60494     },
60495
60496     // private
60497     handleMouseDown : function(e, t)
60498     {
60499         var view = this.grid.view ? this.grid.view : this.grid;
60500         var rowIndex;
60501         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
60502             return;
60503         };
60504         if(e.shiftKey && this.last !== false){
60505             var last = this.last;
60506             this.selectRange(last, rowIndex, e.ctrlKey);
60507             this.last = last; // reset the last
60508             view.focusRow(rowIndex);
60509         }else{
60510             var isSelected = this.isSelected(rowIndex);
60511             if(e.button !== 0 && isSelected){
60512                 view.focusRow(rowIndex);
60513             }else if(e.ctrlKey && isSelected){
60514                 this.deselectRow(rowIndex);
60515             }else if(!isSelected){
60516                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
60517                 view.focusRow(rowIndex);
60518             }
60519         }
60520         this.fireEvent("afterselectionchange", this);
60521     },
60522     // private
60523     handleDragableRowClick :  function(grid, rowIndex, e) 
60524     {
60525         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
60526             this.selectRow(rowIndex, false);
60527             var view = this.grid.view ? this.grid.view : this.grid;
60528             view.focusRow(rowIndex);
60529              this.fireEvent("afterselectionchange", this);
60530         }
60531     },
60532     
60533     /**
60534      * Selects multiple rows.
60535      * @param {Array} rows Array of the indexes of the row to select
60536      * @param {Boolean} keepExisting (optional) True to keep existing selections
60537      */
60538     selectRows : function(rows, keepExisting){
60539         if(!keepExisting){
60540             this.clearSelections();
60541         }
60542         for(var i = 0, len = rows.length; i < len; i++){
60543             this.selectRow(rows[i], true);
60544         }
60545     },
60546
60547     /**
60548      * Selects a range of rows. All rows in between startRow and endRow are also selected.
60549      * @param {Number} startRow The index of the first row in the range
60550      * @param {Number} endRow The index of the last row in the range
60551      * @param {Boolean} keepExisting (optional) True to retain existing selections
60552      */
60553     selectRange : function(startRow, endRow, keepExisting){
60554         if(this.locked) {
60555             return;
60556         }
60557         if(!keepExisting){
60558             this.clearSelections();
60559         }
60560         if(startRow <= endRow){
60561             for(var i = startRow; i <= endRow; i++){
60562                 this.selectRow(i, true);
60563             }
60564         }else{
60565             for(var i = startRow; i >= endRow; i--){
60566                 this.selectRow(i, true);
60567             }
60568         }
60569     },
60570
60571     /**
60572      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
60573      * @param {Number} startRow The index of the first row in the range
60574      * @param {Number} endRow The index of the last row in the range
60575      */
60576     deselectRange : function(startRow, endRow, preventViewNotify){
60577         if(this.locked) {
60578             return;
60579         }
60580         for(var i = startRow; i <= endRow; i++){
60581             this.deselectRow(i, preventViewNotify);
60582         }
60583     },
60584
60585     /**
60586      * Selects a row.
60587      * @param {Number} row The index of the row to select
60588      * @param {Boolean} keepExisting (optional) True to keep existing selections
60589      */
60590     selectRow : function(index, keepExisting, preventViewNotify){
60591         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
60592             return;
60593         }
60594         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
60595             if(!keepExisting || this.singleSelect){
60596                 this.clearSelections();
60597             }
60598             var r = this.grid.ds.getAt(index);
60599             this.selections.add(r);
60600             this.last = this.lastActive = index;
60601             if(!preventViewNotify){
60602                 var view = this.grid.view ? this.grid.view : this.grid;
60603                 view.onRowSelect(index);
60604             }
60605             this.fireEvent("rowselect", this, index, r);
60606             this.fireEvent("selectionchange", this);
60607         }
60608     },
60609
60610     /**
60611      * Deselects a row.
60612      * @param {Number} row The index of the row to deselect
60613      */
60614     deselectRow : function(index, preventViewNotify){
60615         if(this.locked) {
60616             return;
60617         }
60618         if(this.last == index){
60619             this.last = false;
60620         }
60621         if(this.lastActive == index){
60622             this.lastActive = false;
60623         }
60624         var r = this.grid.ds.getAt(index);
60625         this.selections.remove(r);
60626         if(!preventViewNotify){
60627             var view = this.grid.view ? this.grid.view : this.grid;
60628             view.onRowDeselect(index);
60629         }
60630         this.fireEvent("rowdeselect", this, index);
60631         this.fireEvent("selectionchange", this);
60632     },
60633
60634     // private
60635     restoreLast : function(){
60636         if(this._last){
60637             this.last = this._last;
60638         }
60639     },
60640
60641     // private
60642     acceptsNav : function(row, col, cm){
60643         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60644     },
60645
60646     // private
60647     onEditorKey : function(field, e){
60648         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
60649         if(k == e.TAB){
60650             e.stopEvent();
60651             ed.completeEdit();
60652             if(e.shiftKey){
60653                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60654             }else{
60655                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60656             }
60657         }else if(k == e.ENTER && !e.ctrlKey){
60658             e.stopEvent();
60659             ed.completeEdit();
60660             if(e.shiftKey){
60661                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
60662             }else{
60663                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
60664             }
60665         }else if(k == e.ESC){
60666             ed.cancelEdit();
60667         }
60668         if(newCell){
60669             g.startEditing(newCell[0], newCell[1]);
60670         }
60671     }
60672 });/*
60673  * Based on:
60674  * Ext JS Library 1.1.1
60675  * Copyright(c) 2006-2007, Ext JS, LLC.
60676  *
60677  * Originally Released Under LGPL - original licence link has changed is not relivant.
60678  *
60679  * Fork - LGPL
60680  * <script type="text/javascript">
60681  */
60682 /**
60683  * @class Roo.grid.CellSelectionModel
60684  * @extends Roo.grid.AbstractSelectionModel
60685  * This class provides the basic implementation for cell selection in a grid.
60686  * @constructor
60687  * @param {Object} config The object containing the configuration of this model.
60688  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
60689  */
60690 Roo.grid.CellSelectionModel = function(config){
60691     Roo.apply(this, config);
60692
60693     this.selection = null;
60694
60695     this.addEvents({
60696         /**
60697              * @event beforerowselect
60698              * Fires before a cell is selected.
60699              * @param {SelectionModel} this
60700              * @param {Number} rowIndex The selected row index
60701              * @param {Number} colIndex The selected cell index
60702              */
60703             "beforecellselect" : true,
60704         /**
60705              * @event cellselect
60706              * Fires when a cell is selected.
60707              * @param {SelectionModel} this
60708              * @param {Number} rowIndex The selected row index
60709              * @param {Number} colIndex The selected cell index
60710              */
60711             "cellselect" : true,
60712         /**
60713              * @event selectionchange
60714              * Fires when the active selection changes.
60715              * @param {SelectionModel} this
60716              * @param {Object} selection null for no selection or an object (o) with two properties
60717                 <ul>
60718                 <li>o.record: the record object for the row the selection is in</li>
60719                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
60720                 </ul>
60721              */
60722             "selectionchange" : true,
60723         /**
60724              * @event tabend
60725              * Fires when the tab (or enter) was pressed on the last editable cell
60726              * You can use this to trigger add new row.
60727              * @param {SelectionModel} this
60728              */
60729             "tabend" : true,
60730          /**
60731              * @event beforeeditnext
60732              * Fires before the next editable sell is made active
60733              * You can use this to skip to another cell or fire the tabend
60734              *    if you set cell to false
60735              * @param {Object} eventdata object : { cell : [ row, col ] } 
60736              */
60737             "beforeeditnext" : true
60738     });
60739     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
60740 };
60741
60742 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
60743     
60744     enter_is_tab: false,
60745
60746     /** @ignore */
60747     initEvents : function(){
60748         this.grid.on("mousedown", this.handleMouseDown, this);
60749         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
60750         var view = this.grid.view;
60751         view.on("refresh", this.onViewChange, this);
60752         view.on("rowupdated", this.onRowUpdated, this);
60753         view.on("beforerowremoved", this.clearSelections, this);
60754         view.on("beforerowsinserted", this.clearSelections, this);
60755         if(this.grid.isEditor){
60756             this.grid.on("beforeedit", this.beforeEdit,  this);
60757         }
60758     },
60759
60760         //private
60761     beforeEdit : function(e){
60762         this.select(e.row, e.column, false, true, e.record);
60763     },
60764
60765         //private
60766     onRowUpdated : function(v, index, r){
60767         if(this.selection && this.selection.record == r){
60768             v.onCellSelect(index, this.selection.cell[1]);
60769         }
60770     },
60771
60772         //private
60773     onViewChange : function(){
60774         this.clearSelections(true);
60775     },
60776
60777         /**
60778          * Returns the currently selected cell,.
60779          * @return {Array} The selected cell (row, column) or null if none selected.
60780          */
60781     getSelectedCell : function(){
60782         return this.selection ? this.selection.cell : null;
60783     },
60784
60785     /**
60786      * Clears all selections.
60787      * @param {Boolean} true to prevent the gridview from being notified about the change.
60788      */
60789     clearSelections : function(preventNotify){
60790         var s = this.selection;
60791         if(s){
60792             if(preventNotify !== true){
60793                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
60794             }
60795             this.selection = null;
60796             this.fireEvent("selectionchange", this, null);
60797         }
60798     },
60799
60800     /**
60801      * Returns true if there is a selection.
60802      * @return {Boolean}
60803      */
60804     hasSelection : function(){
60805         return this.selection ? true : false;
60806     },
60807
60808     /** @ignore */
60809     handleMouseDown : function(e, t){
60810         var v = this.grid.getView();
60811         if(this.isLocked()){
60812             return;
60813         };
60814         var row = v.findRowIndex(t);
60815         var cell = v.findCellIndex(t);
60816         if(row !== false && cell !== false){
60817             this.select(row, cell);
60818         }
60819     },
60820
60821     /**
60822      * Selects a cell.
60823      * @param {Number} rowIndex
60824      * @param {Number} collIndex
60825      */
60826     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
60827         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
60828             this.clearSelections();
60829             r = r || this.grid.dataSource.getAt(rowIndex);
60830             this.selection = {
60831                 record : r,
60832                 cell : [rowIndex, colIndex]
60833             };
60834             if(!preventViewNotify){
60835                 var v = this.grid.getView();
60836                 v.onCellSelect(rowIndex, colIndex);
60837                 if(preventFocus !== true){
60838                     v.focusCell(rowIndex, colIndex);
60839                 }
60840             }
60841             this.fireEvent("cellselect", this, rowIndex, colIndex);
60842             this.fireEvent("selectionchange", this, this.selection);
60843         }
60844     },
60845
60846         //private
60847     isSelectable : function(rowIndex, colIndex, cm){
60848         return !cm.isHidden(colIndex);
60849     },
60850
60851     /** @ignore */
60852     handleKeyDown : function(e){
60853         //Roo.log('Cell Sel Model handleKeyDown');
60854         if(!e.isNavKeyPress()){
60855             return;
60856         }
60857         var g = this.grid, s = this.selection;
60858         if(!s){
60859             e.stopEvent();
60860             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
60861             if(cell){
60862                 this.select(cell[0], cell[1]);
60863             }
60864             return;
60865         }
60866         var sm = this;
60867         var walk = function(row, col, step){
60868             return g.walkCells(row, col, step, sm.isSelectable,  sm);
60869         };
60870         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
60871         var newCell;
60872
60873       
60874
60875         switch(k){
60876             case e.TAB:
60877                 // handled by onEditorKey
60878                 if (g.isEditor && g.editing) {
60879                     return;
60880                 }
60881                 if(e.shiftKey) {
60882                     newCell = walk(r, c-1, -1);
60883                 } else {
60884                     newCell = walk(r, c+1, 1);
60885                 }
60886                 break;
60887             
60888             case e.DOWN:
60889                newCell = walk(r+1, c, 1);
60890                 break;
60891             
60892             case e.UP:
60893                 newCell = walk(r-1, c, -1);
60894                 break;
60895             
60896             case e.RIGHT:
60897                 newCell = walk(r, c+1, 1);
60898                 break;
60899             
60900             case e.LEFT:
60901                 newCell = walk(r, c-1, -1);
60902                 break;
60903             
60904             case e.ENTER:
60905                 
60906                 if(g.isEditor && !g.editing){
60907                    g.startEditing(r, c);
60908                    e.stopEvent();
60909                    return;
60910                 }
60911                 
60912                 
60913              break;
60914         };
60915         if(newCell){
60916             this.select(newCell[0], newCell[1]);
60917             e.stopEvent();
60918             
60919         }
60920     },
60921
60922     acceptsNav : function(row, col, cm){
60923         return !cm.isHidden(col) && cm.isCellEditable(col, row);
60924     },
60925     /**
60926      * Selects a cell.
60927      * @param {Number} field (not used) - as it's normally used as a listener
60928      * @param {Number} e - event - fake it by using
60929      *
60930      * var e = Roo.EventObjectImpl.prototype;
60931      * e.keyCode = e.TAB
60932      *
60933      * 
60934      */
60935     onEditorKey : function(field, e){
60936         
60937         var k = e.getKey(),
60938             newCell,
60939             g = this.grid,
60940             ed = g.activeEditor,
60941             forward = false;
60942         ///Roo.log('onEditorKey' + k);
60943         
60944         
60945         if (this.enter_is_tab && k == e.ENTER) {
60946             k = e.TAB;
60947         }
60948         
60949         if(k == e.TAB){
60950             if(e.shiftKey){
60951                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
60952             }else{
60953                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60954                 forward = true;
60955             }
60956             
60957             e.stopEvent();
60958             
60959         } else if(k == e.ENTER &&  !e.ctrlKey){
60960             ed.completeEdit();
60961             e.stopEvent();
60962             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
60963         
60964                 } else if(k == e.ESC){
60965             ed.cancelEdit();
60966         }
60967                 
60968         if (newCell) {
60969             var ecall = { cell : newCell, forward : forward };
60970             this.fireEvent('beforeeditnext', ecall );
60971             newCell = ecall.cell;
60972                         forward = ecall.forward;
60973         }
60974                 
60975         if(newCell){
60976             //Roo.log('next cell after edit');
60977             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
60978         } else if (forward) {
60979             // tabbed past last
60980             this.fireEvent.defer(100, this, ['tabend',this]);
60981         }
60982     }
60983 });/*
60984  * Based on:
60985  * Ext JS Library 1.1.1
60986  * Copyright(c) 2006-2007, Ext JS, LLC.
60987  *
60988  * Originally Released Under LGPL - original licence link has changed is not relivant.
60989  *
60990  * Fork - LGPL
60991  * <script type="text/javascript">
60992  */
60993  
60994 /**
60995  * @class Roo.grid.EditorGrid
60996  * @extends Roo.grid.Grid
60997  * Class for creating and editable grid.
60998  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
60999  * The container MUST have some type of size defined for the grid to fill. The container will be 
61000  * automatically set to position relative if it isn't already.
61001  * @param {Object} dataSource The data model to bind to
61002  * @param {Object} colModel The column model with info about this grid's columns
61003  */
61004 Roo.grid.EditorGrid = function(container, config){
61005     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
61006     this.getGridEl().addClass("xedit-grid");
61007
61008     if(!this.selModel){
61009         this.selModel = new Roo.grid.CellSelectionModel();
61010     }
61011
61012     this.activeEditor = null;
61013
61014         this.addEvents({
61015             /**
61016              * @event beforeedit
61017              * Fires before cell editing is triggered. The edit event object has the following properties <br />
61018              * <ul style="padding:5px;padding-left:16px;">
61019              * <li>grid - This grid</li>
61020              * <li>record - The record being edited</li>
61021              * <li>field - The field name being edited</li>
61022              * <li>value - The value for the field being edited.</li>
61023              * <li>row - The grid row index</li>
61024              * <li>column - The grid column index</li>
61025              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
61026              * </ul>
61027              * @param {Object} e An edit event (see above for description)
61028              */
61029             "beforeedit" : true,
61030             /**
61031              * @event afteredit
61032              * Fires after a cell is edited. <br />
61033              * <ul style="padding:5px;padding-left:16px;">
61034              * <li>grid - This grid</li>
61035              * <li>record - The record being edited</li>
61036              * <li>field - The field name being edited</li>
61037              * <li>value - The value being set</li>
61038              * <li>originalValue - The original value for the field, before the edit.</li>
61039              * <li>row - The grid row index</li>
61040              * <li>column - The grid column index</li>
61041              * </ul>
61042              * @param {Object} e An edit event (see above for description)
61043              */
61044             "afteredit" : true,
61045             /**
61046              * @event validateedit
61047              * Fires after a cell is edited, but before the value is set in the record. 
61048          * You can use this to modify the value being set in the field, Return false
61049              * to cancel the change. The edit event object has the following properties <br />
61050              * <ul style="padding:5px;padding-left:16px;">
61051          * <li>editor - This editor</li>
61052              * <li>grid - This grid</li>
61053              * <li>record - The record being edited</li>
61054              * <li>field - The field name being edited</li>
61055              * <li>value - The value being set</li>
61056              * <li>originalValue - The original value for the field, before the edit.</li>
61057              * <li>row - The grid row index</li>
61058              * <li>column - The grid column index</li>
61059              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
61060              * </ul>
61061              * @param {Object} e An edit event (see above for description)
61062              */
61063             "validateedit" : true
61064         });
61065     this.on("bodyscroll", this.stopEditing,  this);
61066     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
61067 };
61068
61069 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
61070     /**
61071      * @cfg {Number} clicksToEdit
61072      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
61073      */
61074     clicksToEdit: 2,
61075
61076     // private
61077     isEditor : true,
61078     // private
61079     trackMouseOver: false, // causes very odd FF errors
61080
61081     onCellDblClick : function(g, row, col){
61082         this.startEditing(row, col);
61083     },
61084
61085     onEditComplete : function(ed, value, startValue){
61086         this.editing = false;
61087         this.activeEditor = null;
61088         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
61089         var r = ed.record;
61090         var field = this.colModel.getDataIndex(ed.col);
61091         var e = {
61092             grid: this,
61093             record: r,
61094             field: field,
61095             originalValue: startValue,
61096             value: value,
61097             row: ed.row,
61098             column: ed.col,
61099             cancel:false,
61100             editor: ed
61101         };
61102         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
61103         cell.show();
61104           
61105         if(String(value) !== String(startValue)){
61106             
61107             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
61108                 r.set(field, e.value);
61109                 // if we are dealing with a combo box..
61110                 // then we also set the 'name' colum to be the displayField
61111                 if (ed.field.displayField && ed.field.name) {
61112                     r.set(ed.field.name, ed.field.el.dom.value);
61113                 }
61114                 
61115                 delete e.cancel; //?? why!!!
61116                 this.fireEvent("afteredit", e);
61117             }
61118         } else {
61119             this.fireEvent("afteredit", e); // always fire it!
61120         }
61121         this.view.focusCell(ed.row, ed.col);
61122     },
61123
61124     /**
61125      * Starts editing the specified for the specified row/column
61126      * @param {Number} rowIndex
61127      * @param {Number} colIndex
61128      */
61129     startEditing : function(row, col){
61130         this.stopEditing();
61131         if(this.colModel.isCellEditable(col, row)){
61132             this.view.ensureVisible(row, col, true);
61133           
61134             var r = this.dataSource.getAt(row);
61135             var field = this.colModel.getDataIndex(col);
61136             var cell = Roo.get(this.view.getCell(row,col));
61137             var e = {
61138                 grid: this,
61139                 record: r,
61140                 field: field,
61141                 value: r.data[field],
61142                 row: row,
61143                 column: col,
61144                 cancel:false 
61145             };
61146             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
61147                 this.editing = true;
61148                 var ed = this.colModel.getCellEditor(col, row);
61149                 
61150                 if (!ed) {
61151                     return;
61152                 }
61153                 if(!ed.rendered){
61154                     ed.render(ed.parentEl || document.body);
61155                 }
61156                 ed.field.reset();
61157                
61158                 cell.hide();
61159                 
61160                 (function(){ // complex but required for focus issues in safari, ie and opera
61161                     ed.row = row;
61162                     ed.col = col;
61163                     ed.record = r;
61164                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
61165                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
61166                     this.activeEditor = ed;
61167                     var v = r.data[field];
61168                     ed.startEdit(this.view.getCell(row, col), v);
61169                     // combo's with 'displayField and name set
61170                     if (ed.field.displayField && ed.field.name) {
61171                         ed.field.el.dom.value = r.data[ed.field.name];
61172                     }
61173                     
61174                     
61175                 }).defer(50, this);
61176             }
61177         }
61178     },
61179         
61180     /**
61181      * Stops any active editing
61182      */
61183     stopEditing : function(){
61184         if(this.activeEditor){
61185             this.activeEditor.completeEdit();
61186         }
61187         this.activeEditor = null;
61188     },
61189         
61190          /**
61191      * Called to get grid's drag proxy text, by default returns this.ddText.
61192      * @return {String}
61193      */
61194     getDragDropText : function(){
61195         var count = this.selModel.getSelectedCell() ? 1 : 0;
61196         return String.format(this.ddText, count, count == 1 ? '' : 's');
61197     }
61198         
61199 });/*
61200  * Based on:
61201  * Ext JS Library 1.1.1
61202  * Copyright(c) 2006-2007, Ext JS, LLC.
61203  *
61204  * Originally Released Under LGPL - original licence link has changed is not relivant.
61205  *
61206  * Fork - LGPL
61207  * <script type="text/javascript">
61208  */
61209
61210 // private - not really -- you end up using it !
61211 // This is a support class used internally by the Grid components
61212
61213 /**
61214  * @class Roo.grid.GridEditor
61215  * @extends Roo.Editor
61216  * Class for creating and editable grid elements.
61217  * @param {Object} config any settings (must include field)
61218  */
61219 Roo.grid.GridEditor = function(field, config){
61220     if (!config && field.field) {
61221         config = field;
61222         field = Roo.factory(config.field, Roo.form);
61223     }
61224     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
61225     field.monitorTab = false;
61226 };
61227
61228 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
61229     
61230     /**
61231      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
61232      */
61233     
61234     alignment: "tl-tl",
61235     autoSize: "width",
61236     hideEl : false,
61237     cls: "x-small-editor x-grid-editor",
61238     shim:false,
61239     shadow:"frame"
61240 });/*
61241  * Based on:
61242  * Ext JS Library 1.1.1
61243  * Copyright(c) 2006-2007, Ext JS, LLC.
61244  *
61245  * Originally Released Under LGPL - original licence link has changed is not relivant.
61246  *
61247  * Fork - LGPL
61248  * <script type="text/javascript">
61249  */
61250   
61251
61252   
61253 Roo.grid.PropertyRecord = Roo.data.Record.create([
61254     {name:'name',type:'string'},  'value'
61255 ]);
61256
61257
61258 Roo.grid.PropertyStore = function(grid, source){
61259     this.grid = grid;
61260     this.store = new Roo.data.Store({
61261         recordType : Roo.grid.PropertyRecord
61262     });
61263     this.store.on('update', this.onUpdate,  this);
61264     if(source){
61265         this.setSource(source);
61266     }
61267     Roo.grid.PropertyStore.superclass.constructor.call(this);
61268 };
61269
61270
61271
61272 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
61273     setSource : function(o){
61274         this.source = o;
61275         this.store.removeAll();
61276         var data = [];
61277         for(var k in o){
61278             if(this.isEditableValue(o[k])){
61279                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
61280             }
61281         }
61282         this.store.loadRecords({records: data}, {}, true);
61283     },
61284
61285     onUpdate : function(ds, record, type){
61286         if(type == Roo.data.Record.EDIT){
61287             var v = record.data['value'];
61288             var oldValue = record.modified['value'];
61289             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
61290                 this.source[record.id] = v;
61291                 record.commit();
61292                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
61293             }else{
61294                 record.reject();
61295             }
61296         }
61297     },
61298
61299     getProperty : function(row){
61300        return this.store.getAt(row);
61301     },
61302
61303     isEditableValue: function(val){
61304         if(val && val instanceof Date){
61305             return true;
61306         }else if(typeof val == 'object' || typeof val == 'function'){
61307             return false;
61308         }
61309         return true;
61310     },
61311
61312     setValue : function(prop, value){
61313         this.source[prop] = value;
61314         this.store.getById(prop).set('value', value);
61315     },
61316
61317     getSource : function(){
61318         return this.source;
61319     }
61320 });
61321
61322 Roo.grid.PropertyColumnModel = function(grid, store){
61323     this.grid = grid;
61324     var g = Roo.grid;
61325     g.PropertyColumnModel.superclass.constructor.call(this, [
61326         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
61327         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
61328     ]);
61329     this.store = store;
61330     this.bselect = Roo.DomHelper.append(document.body, {
61331         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
61332             {tag: 'option', value: 'true', html: 'true'},
61333             {tag: 'option', value: 'false', html: 'false'}
61334         ]
61335     });
61336     Roo.id(this.bselect);
61337     var f = Roo.form;
61338     this.editors = {
61339         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
61340         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
61341         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
61342         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
61343         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
61344     };
61345     this.renderCellDelegate = this.renderCell.createDelegate(this);
61346     this.renderPropDelegate = this.renderProp.createDelegate(this);
61347 };
61348
61349 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
61350     
61351     
61352     nameText : 'Name',
61353     valueText : 'Value',
61354     
61355     dateFormat : 'm/j/Y',
61356     
61357     
61358     renderDate : function(dateVal){
61359         return dateVal.dateFormat(this.dateFormat);
61360     },
61361
61362     renderBool : function(bVal){
61363         return bVal ? 'true' : 'false';
61364     },
61365
61366     isCellEditable : function(colIndex, rowIndex){
61367         return colIndex == 1;
61368     },
61369
61370     getRenderer : function(col){
61371         return col == 1 ?
61372             this.renderCellDelegate : this.renderPropDelegate;
61373     },
61374
61375     renderProp : function(v){
61376         return this.getPropertyName(v);
61377     },
61378
61379     renderCell : function(val){
61380         var rv = val;
61381         if(val instanceof Date){
61382             rv = this.renderDate(val);
61383         }else if(typeof val == 'boolean'){
61384             rv = this.renderBool(val);
61385         }
61386         return Roo.util.Format.htmlEncode(rv);
61387     },
61388
61389     getPropertyName : function(name){
61390         var pn = this.grid.propertyNames;
61391         return pn && pn[name] ? pn[name] : name;
61392     },
61393
61394     getCellEditor : function(colIndex, rowIndex){
61395         var p = this.store.getProperty(rowIndex);
61396         var n = p.data['name'], val = p.data['value'];
61397         
61398         if(typeof(this.grid.customEditors[n]) == 'string'){
61399             return this.editors[this.grid.customEditors[n]];
61400         }
61401         if(typeof(this.grid.customEditors[n]) != 'undefined'){
61402             return this.grid.customEditors[n];
61403         }
61404         if(val instanceof Date){
61405             return this.editors['date'];
61406         }else if(typeof val == 'number'){
61407             return this.editors['number'];
61408         }else if(typeof val == 'boolean'){
61409             return this.editors['boolean'];
61410         }else{
61411             return this.editors['string'];
61412         }
61413     }
61414 });
61415
61416 /**
61417  * @class Roo.grid.PropertyGrid
61418  * @extends Roo.grid.EditorGrid
61419  * This class represents the  interface of a component based property grid control.
61420  * <br><br>Usage:<pre><code>
61421  var grid = new Roo.grid.PropertyGrid("my-container-id", {
61422       
61423  });
61424  // set any options
61425  grid.render();
61426  * </code></pre>
61427   
61428  * @constructor
61429  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61430  * The container MUST have some type of size defined for the grid to fill. The container will be
61431  * automatically set to position relative if it isn't already.
61432  * @param {Object} config A config object that sets properties on this grid.
61433  */
61434 Roo.grid.PropertyGrid = function(container, config){
61435     config = config || {};
61436     var store = new Roo.grid.PropertyStore(this);
61437     this.store = store;
61438     var cm = new Roo.grid.PropertyColumnModel(this, store);
61439     store.store.sort('name', 'ASC');
61440     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
61441         ds: store.store,
61442         cm: cm,
61443         enableColLock:false,
61444         enableColumnMove:false,
61445         stripeRows:false,
61446         trackMouseOver: false,
61447         clicksToEdit:1
61448     }, config));
61449     this.getGridEl().addClass('x-props-grid');
61450     this.lastEditRow = null;
61451     this.on('columnresize', this.onColumnResize, this);
61452     this.addEvents({
61453          /**
61454              * @event beforepropertychange
61455              * Fires before a property changes (return false to stop?)
61456              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61457              * @param {String} id Record Id
61458              * @param {String} newval New Value
61459          * @param {String} oldval Old Value
61460              */
61461         "beforepropertychange": true,
61462         /**
61463              * @event propertychange
61464              * Fires after a property changes
61465              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61466              * @param {String} id Record Id
61467              * @param {String} newval New Value
61468          * @param {String} oldval Old Value
61469              */
61470         "propertychange": true
61471     });
61472     this.customEditors = this.customEditors || {};
61473 };
61474 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
61475     
61476      /**
61477      * @cfg {Object} customEditors map of colnames=> custom editors.
61478      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
61479      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
61480      * false disables editing of the field.
61481          */
61482     
61483       /**
61484      * @cfg {Object} propertyNames map of property Names to their displayed value
61485          */
61486     
61487     render : function(){
61488         Roo.grid.PropertyGrid.superclass.render.call(this);
61489         this.autoSize.defer(100, this);
61490     },
61491
61492     autoSize : function(){
61493         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
61494         if(this.view){
61495             this.view.fitColumns();
61496         }
61497     },
61498
61499     onColumnResize : function(){
61500         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
61501         this.autoSize();
61502     },
61503     /**
61504      * Sets the data for the Grid
61505      * accepts a Key => Value object of all the elements avaiable.
61506      * @param {Object} data  to appear in grid.
61507      */
61508     setSource : function(source){
61509         this.store.setSource(source);
61510         //this.autoSize();
61511     },
61512     /**
61513      * Gets all the data from the grid.
61514      * @return {Object} data  data stored in grid
61515      */
61516     getSource : function(){
61517         return this.store.getSource();
61518     }
61519 });/*
61520   
61521  * Licence LGPL
61522  
61523  */
61524  
61525 /**
61526  * @class Roo.grid.Calendar
61527  * @extends Roo.grid.Grid
61528  * This class extends the Grid to provide a calendar widget
61529  * <br><br>Usage:<pre><code>
61530  var grid = new Roo.grid.Calendar("my-container-id", {
61531      ds: myDataStore,
61532      cm: myColModel,
61533      selModel: mySelectionModel,
61534      autoSizeColumns: true,
61535      monitorWindowResize: false,
61536      trackMouseOver: true
61537      eventstore : real data store..
61538  });
61539  // set any options
61540  grid.render();
61541   
61542   * @constructor
61543  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61544  * The container MUST have some type of size defined for the grid to fill. The container will be
61545  * automatically set to position relative if it isn't already.
61546  * @param {Object} config A config object that sets properties on this grid.
61547  */
61548 Roo.grid.Calendar = function(container, config){
61549         // initialize the container
61550         this.container = Roo.get(container);
61551         this.container.update("");
61552         this.container.setStyle("overflow", "hidden");
61553     this.container.addClass('x-grid-container');
61554
61555     this.id = this.container.id;
61556
61557     Roo.apply(this, config);
61558     // check and correct shorthanded configs
61559     
61560     var rows = [];
61561     var d =1;
61562     for (var r = 0;r < 6;r++) {
61563         
61564         rows[r]=[];
61565         for (var c =0;c < 7;c++) {
61566             rows[r][c]= '';
61567         }
61568     }
61569     if (this.eventStore) {
61570         this.eventStore= Roo.factory(this.eventStore, Roo.data);
61571         this.eventStore.on('load',this.onLoad, this);
61572         this.eventStore.on('beforeload',this.clearEvents, this);
61573          
61574     }
61575     
61576     this.dataSource = new Roo.data.Store({
61577             proxy: new Roo.data.MemoryProxy(rows),
61578             reader: new Roo.data.ArrayReader({}, [
61579                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
61580     });
61581
61582     this.dataSource.load();
61583     this.ds = this.dataSource;
61584     this.ds.xmodule = this.xmodule || false;
61585     
61586     
61587     var cellRender = function(v,x,r)
61588     {
61589         return String.format(
61590             '<div class="fc-day  fc-widget-content"><div>' +
61591                 '<div class="fc-event-container"></div>' +
61592                 '<div class="fc-day-number">{0}</div>'+
61593                 
61594                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
61595             '</div></div>', v);
61596     
61597     }
61598     
61599     
61600     this.colModel = new Roo.grid.ColumnModel( [
61601         {
61602             xtype: 'ColumnModel',
61603             xns: Roo.grid,
61604             dataIndex : 'weekday0',
61605             header : 'Sunday',
61606             renderer : cellRender
61607         },
61608         {
61609             xtype: 'ColumnModel',
61610             xns: Roo.grid,
61611             dataIndex : 'weekday1',
61612             header : 'Monday',
61613             renderer : cellRender
61614         },
61615         {
61616             xtype: 'ColumnModel',
61617             xns: Roo.grid,
61618             dataIndex : 'weekday2',
61619             header : 'Tuesday',
61620             renderer : cellRender
61621         },
61622         {
61623             xtype: 'ColumnModel',
61624             xns: Roo.grid,
61625             dataIndex : 'weekday3',
61626             header : 'Wednesday',
61627             renderer : cellRender
61628         },
61629         {
61630             xtype: 'ColumnModel',
61631             xns: Roo.grid,
61632             dataIndex : 'weekday4',
61633             header : 'Thursday',
61634             renderer : cellRender
61635         },
61636         {
61637             xtype: 'ColumnModel',
61638             xns: Roo.grid,
61639             dataIndex : 'weekday5',
61640             header : 'Friday',
61641             renderer : cellRender
61642         },
61643         {
61644             xtype: 'ColumnModel',
61645             xns: Roo.grid,
61646             dataIndex : 'weekday6',
61647             header : 'Saturday',
61648             renderer : cellRender
61649         }
61650     ]);
61651     this.cm = this.colModel;
61652     this.cm.xmodule = this.xmodule || false;
61653  
61654         
61655           
61656     //this.selModel = new Roo.grid.CellSelectionModel();
61657     //this.sm = this.selModel;
61658     //this.selModel.init(this);
61659     
61660     
61661     if(this.width){
61662         this.container.setWidth(this.width);
61663     }
61664
61665     if(this.height){
61666         this.container.setHeight(this.height);
61667     }
61668     /** @private */
61669         this.addEvents({
61670         // raw events
61671         /**
61672          * @event click
61673          * The raw click event for the entire grid.
61674          * @param {Roo.EventObject} e
61675          */
61676         "click" : true,
61677         /**
61678          * @event dblclick
61679          * The raw dblclick event for the entire grid.
61680          * @param {Roo.EventObject} e
61681          */
61682         "dblclick" : true,
61683         /**
61684          * @event contextmenu
61685          * The raw contextmenu event for the entire grid.
61686          * @param {Roo.EventObject} e
61687          */
61688         "contextmenu" : true,
61689         /**
61690          * @event mousedown
61691          * The raw mousedown event for the entire grid.
61692          * @param {Roo.EventObject} e
61693          */
61694         "mousedown" : true,
61695         /**
61696          * @event mouseup
61697          * The raw mouseup event for the entire grid.
61698          * @param {Roo.EventObject} e
61699          */
61700         "mouseup" : true,
61701         /**
61702          * @event mouseover
61703          * The raw mouseover event for the entire grid.
61704          * @param {Roo.EventObject} e
61705          */
61706         "mouseover" : true,
61707         /**
61708          * @event mouseout
61709          * The raw mouseout event for the entire grid.
61710          * @param {Roo.EventObject} e
61711          */
61712         "mouseout" : true,
61713         /**
61714          * @event keypress
61715          * The raw keypress event for the entire grid.
61716          * @param {Roo.EventObject} e
61717          */
61718         "keypress" : true,
61719         /**
61720          * @event keydown
61721          * The raw keydown event for the entire grid.
61722          * @param {Roo.EventObject} e
61723          */
61724         "keydown" : true,
61725
61726         // custom events
61727
61728         /**
61729          * @event cellclick
61730          * Fires when a cell is clicked
61731          * @param {Grid} this
61732          * @param {Number} rowIndex
61733          * @param {Number} columnIndex
61734          * @param {Roo.EventObject} e
61735          */
61736         "cellclick" : true,
61737         /**
61738          * @event celldblclick
61739          * Fires when a cell is double clicked
61740          * @param {Grid} this
61741          * @param {Number} rowIndex
61742          * @param {Number} columnIndex
61743          * @param {Roo.EventObject} e
61744          */
61745         "celldblclick" : true,
61746         /**
61747          * @event rowclick
61748          * Fires when a row is clicked
61749          * @param {Grid} this
61750          * @param {Number} rowIndex
61751          * @param {Roo.EventObject} e
61752          */
61753         "rowclick" : true,
61754         /**
61755          * @event rowdblclick
61756          * Fires when a row is double clicked
61757          * @param {Grid} this
61758          * @param {Number} rowIndex
61759          * @param {Roo.EventObject} e
61760          */
61761         "rowdblclick" : true,
61762         /**
61763          * @event headerclick
61764          * Fires when a header is clicked
61765          * @param {Grid} this
61766          * @param {Number} columnIndex
61767          * @param {Roo.EventObject} e
61768          */
61769         "headerclick" : true,
61770         /**
61771          * @event headerdblclick
61772          * Fires when a header cell is double clicked
61773          * @param {Grid} this
61774          * @param {Number} columnIndex
61775          * @param {Roo.EventObject} e
61776          */
61777         "headerdblclick" : true,
61778         /**
61779          * @event rowcontextmenu
61780          * Fires when a row is right clicked
61781          * @param {Grid} this
61782          * @param {Number} rowIndex
61783          * @param {Roo.EventObject} e
61784          */
61785         "rowcontextmenu" : true,
61786         /**
61787          * @event cellcontextmenu
61788          * Fires when a cell is right clicked
61789          * @param {Grid} this
61790          * @param {Number} rowIndex
61791          * @param {Number} cellIndex
61792          * @param {Roo.EventObject} e
61793          */
61794          "cellcontextmenu" : true,
61795         /**
61796          * @event headercontextmenu
61797          * Fires when a header is right clicked
61798          * @param {Grid} this
61799          * @param {Number} columnIndex
61800          * @param {Roo.EventObject} e
61801          */
61802         "headercontextmenu" : true,
61803         /**
61804          * @event bodyscroll
61805          * Fires when the body element is scrolled
61806          * @param {Number} scrollLeft
61807          * @param {Number} scrollTop
61808          */
61809         "bodyscroll" : true,
61810         /**
61811          * @event columnresize
61812          * Fires when the user resizes a column
61813          * @param {Number} columnIndex
61814          * @param {Number} newSize
61815          */
61816         "columnresize" : true,
61817         /**
61818          * @event columnmove
61819          * Fires when the user moves a column
61820          * @param {Number} oldIndex
61821          * @param {Number} newIndex
61822          */
61823         "columnmove" : true,
61824         /**
61825          * @event startdrag
61826          * Fires when row(s) start being dragged
61827          * @param {Grid} this
61828          * @param {Roo.GridDD} dd The drag drop object
61829          * @param {event} e The raw browser event
61830          */
61831         "startdrag" : true,
61832         /**
61833          * @event enddrag
61834          * Fires when a drag operation is complete
61835          * @param {Grid} this
61836          * @param {Roo.GridDD} dd The drag drop object
61837          * @param {event} e The raw browser event
61838          */
61839         "enddrag" : true,
61840         /**
61841          * @event dragdrop
61842          * Fires when dragged row(s) are dropped on a valid DD target
61843          * @param {Grid} this
61844          * @param {Roo.GridDD} dd The drag drop object
61845          * @param {String} targetId The target drag drop object
61846          * @param {event} e The raw browser event
61847          */
61848         "dragdrop" : true,
61849         /**
61850          * @event dragover
61851          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
61852          * @param {Grid} this
61853          * @param {Roo.GridDD} dd The drag drop object
61854          * @param {String} targetId The target drag drop object
61855          * @param {event} e The raw browser event
61856          */
61857         "dragover" : true,
61858         /**
61859          * @event dragenter
61860          *  Fires when the dragged row(s) first cross another DD target while being dragged
61861          * @param {Grid} this
61862          * @param {Roo.GridDD} dd The drag drop object
61863          * @param {String} targetId The target drag drop object
61864          * @param {event} e The raw browser event
61865          */
61866         "dragenter" : true,
61867         /**
61868          * @event dragout
61869          * Fires when the dragged row(s) leave another DD target while being dragged
61870          * @param {Grid} this
61871          * @param {Roo.GridDD} dd The drag drop object
61872          * @param {String} targetId The target drag drop object
61873          * @param {event} e The raw browser event
61874          */
61875         "dragout" : true,
61876         /**
61877          * @event rowclass
61878          * Fires when a row is rendered, so you can change add a style to it.
61879          * @param {GridView} gridview   The grid view
61880          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
61881          */
61882         'rowclass' : true,
61883
61884         /**
61885          * @event render
61886          * Fires when the grid is rendered
61887          * @param {Grid} grid
61888          */
61889         'render' : true,
61890             /**
61891              * @event select
61892              * Fires when a date is selected
61893              * @param {DatePicker} this
61894              * @param {Date} date The selected date
61895              */
61896         'select': true,
61897         /**
61898              * @event monthchange
61899              * Fires when the displayed month changes 
61900              * @param {DatePicker} this
61901              * @param {Date} date The selected month
61902              */
61903         'monthchange': true,
61904         /**
61905              * @event evententer
61906              * Fires when mouse over an event
61907              * @param {Calendar} this
61908              * @param {event} Event
61909              */
61910         'evententer': true,
61911         /**
61912              * @event eventleave
61913              * Fires when the mouse leaves an
61914              * @param {Calendar} this
61915              * @param {event}
61916              */
61917         'eventleave': true,
61918         /**
61919              * @event eventclick
61920              * Fires when the mouse click an
61921              * @param {Calendar} this
61922              * @param {event}
61923              */
61924         'eventclick': true,
61925         /**
61926              * @event eventrender
61927              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
61928              * @param {Calendar} this
61929              * @param {data} data to be modified
61930              */
61931         'eventrender': true
61932         
61933     });
61934
61935     Roo.grid.Grid.superclass.constructor.call(this);
61936     this.on('render', function() {
61937         this.view.el.addClass('x-grid-cal'); 
61938         
61939         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
61940
61941     },this);
61942     
61943     if (!Roo.grid.Calendar.style) {
61944         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
61945             
61946             
61947             '.x-grid-cal .x-grid-col' :  {
61948                 height: 'auto !important',
61949                 'vertical-align': 'top'
61950             },
61951             '.x-grid-cal  .fc-event-hori' : {
61952                 height: '14px'
61953             }
61954              
61955             
61956         }, Roo.id());
61957     }
61958
61959     
61960     
61961 };
61962 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
61963     /**
61964      * @cfg {Store} eventStore The store that loads events.
61965      */
61966     eventStore : 25,
61967
61968      
61969     activeDate : false,
61970     startDay : 0,
61971     autoWidth : true,
61972     monitorWindowResize : false,
61973
61974     
61975     resizeColumns : function() {
61976         var col = (this.view.el.getWidth() / 7) - 3;
61977         // loop through cols, and setWidth
61978         for(var i =0 ; i < 7 ; i++){
61979             this.cm.setColumnWidth(i, col);
61980         }
61981     },
61982      setDate :function(date) {
61983         
61984         Roo.log('setDate?');
61985         
61986         this.resizeColumns();
61987         var vd = this.activeDate;
61988         this.activeDate = date;
61989 //        if(vd && this.el){
61990 //            var t = date.getTime();
61991 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
61992 //                Roo.log('using add remove');
61993 //                
61994 //                this.fireEvent('monthchange', this, date);
61995 //                
61996 //                this.cells.removeClass("fc-state-highlight");
61997 //                this.cells.each(function(c){
61998 //                   if(c.dateValue == t){
61999 //                       c.addClass("fc-state-highlight");
62000 //                       setTimeout(function(){
62001 //                            try{c.dom.firstChild.focus();}catch(e){}
62002 //                       }, 50);
62003 //                       return false;
62004 //                   }
62005 //                   return true;
62006 //                });
62007 //                return;
62008 //            }
62009 //        }
62010         
62011         var days = date.getDaysInMonth();
62012         
62013         var firstOfMonth = date.getFirstDateOfMonth();
62014         var startingPos = firstOfMonth.getDay()-this.startDay;
62015         
62016         if(startingPos < this.startDay){
62017             startingPos += 7;
62018         }
62019         
62020         var pm = date.add(Date.MONTH, -1);
62021         var prevStart = pm.getDaysInMonth()-startingPos;
62022 //        
62023         
62024         
62025         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62026         
62027         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
62028         //this.cells.addClassOnOver('fc-state-hover');
62029         
62030         var cells = this.cells.elements;
62031         var textEls = this.textNodes;
62032         
62033         //Roo.each(cells, function(cell){
62034         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
62035         //});
62036         
62037         days += startingPos;
62038
62039         // convert everything to numbers so it's fast
62040         var day = 86400000;
62041         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
62042         //Roo.log(d);
62043         //Roo.log(pm);
62044         //Roo.log(prevStart);
62045         
62046         var today = new Date().clearTime().getTime();
62047         var sel = date.clearTime().getTime();
62048         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
62049         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
62050         var ddMatch = this.disabledDatesRE;
62051         var ddText = this.disabledDatesText;
62052         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
62053         var ddaysText = this.disabledDaysText;
62054         var format = this.format;
62055         
62056         var setCellClass = function(cal, cell){
62057             
62058             //Roo.log('set Cell Class');
62059             cell.title = "";
62060             var t = d.getTime();
62061             
62062             //Roo.log(d);
62063             
62064             
62065             cell.dateValue = t;
62066             if(t == today){
62067                 cell.className += " fc-today";
62068                 cell.className += " fc-state-highlight";
62069                 cell.title = cal.todayText;
62070             }
62071             if(t == sel){
62072                 // disable highlight in other month..
62073                 cell.className += " fc-state-highlight";
62074                 
62075             }
62076             // disabling
62077             if(t < min) {
62078                 //cell.className = " fc-state-disabled";
62079                 cell.title = cal.minText;
62080                 return;
62081             }
62082             if(t > max) {
62083                 //cell.className = " fc-state-disabled";
62084                 cell.title = cal.maxText;
62085                 return;
62086             }
62087             if(ddays){
62088                 if(ddays.indexOf(d.getDay()) != -1){
62089                     // cell.title = ddaysText;
62090                    // cell.className = " fc-state-disabled";
62091                 }
62092             }
62093             if(ddMatch && format){
62094                 var fvalue = d.dateFormat(format);
62095                 if(ddMatch.test(fvalue)){
62096                     cell.title = ddText.replace("%0", fvalue);
62097                    cell.className = " fc-state-disabled";
62098                 }
62099             }
62100             
62101             if (!cell.initialClassName) {
62102                 cell.initialClassName = cell.dom.className;
62103             }
62104             
62105             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
62106         };
62107
62108         var i = 0;
62109         
62110         for(; i < startingPos; i++) {
62111             cells[i].dayName =  (++prevStart);
62112             Roo.log(textEls[i]);
62113             d.setDate(d.getDate()+1);
62114             
62115             //cells[i].className = "fc-past fc-other-month";
62116             setCellClass(this, cells[i]);
62117         }
62118         
62119         var intDay = 0;
62120         
62121         for(; i < days; i++){
62122             intDay = i - startingPos + 1;
62123             cells[i].dayName =  (intDay);
62124             d.setDate(d.getDate()+1);
62125             
62126             cells[i].className = ''; // "x-date-active";
62127             setCellClass(this, cells[i]);
62128         }
62129         var extraDays = 0;
62130         
62131         for(; i < 42; i++) {
62132             //textEls[i].innerHTML = (++extraDays);
62133             
62134             d.setDate(d.getDate()+1);
62135             cells[i].dayName = (++extraDays);
62136             cells[i].className = "fc-future fc-other-month";
62137             setCellClass(this, cells[i]);
62138         }
62139         
62140         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
62141         
62142         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
62143         
62144         // this will cause all the cells to mis
62145         var rows= [];
62146         var i =0;
62147         for (var r = 0;r < 6;r++) {
62148             for (var c =0;c < 7;c++) {
62149                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
62150             }    
62151         }
62152         
62153         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62154         for(i=0;i<cells.length;i++) {
62155             
62156             this.cells.elements[i].dayName = cells[i].dayName ;
62157             this.cells.elements[i].className = cells[i].className;
62158             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
62159             this.cells.elements[i].title = cells[i].title ;
62160             this.cells.elements[i].dateValue = cells[i].dateValue ;
62161         }
62162         
62163         
62164         
62165         
62166         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
62167         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
62168         
62169         ////if(totalRows != 6){
62170             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
62171            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
62172        // }
62173         
62174         this.fireEvent('monthchange', this, date);
62175         
62176         
62177     },
62178  /**
62179      * Returns the grid's SelectionModel.
62180      * @return {SelectionModel}
62181      */
62182     getSelectionModel : function(){
62183         if(!this.selModel){
62184             this.selModel = new Roo.grid.CellSelectionModel();
62185         }
62186         return this.selModel;
62187     },
62188
62189     load: function() {
62190         this.eventStore.load()
62191         
62192         
62193         
62194     },
62195     
62196     findCell : function(dt) {
62197         dt = dt.clearTime().getTime();
62198         var ret = false;
62199         this.cells.each(function(c){
62200             //Roo.log("check " +c.dateValue + '?=' + dt);
62201             if(c.dateValue == dt){
62202                 ret = c;
62203                 return false;
62204             }
62205             return true;
62206         });
62207         
62208         return ret;
62209     },
62210     
62211     findCells : function(rec) {
62212         var s = rec.data.start_dt.clone().clearTime().getTime();
62213        // Roo.log(s);
62214         var e= rec.data.end_dt.clone().clearTime().getTime();
62215        // Roo.log(e);
62216         var ret = [];
62217         this.cells.each(function(c){
62218              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
62219             
62220             if(c.dateValue > e){
62221                 return ;
62222             }
62223             if(c.dateValue < s){
62224                 return ;
62225             }
62226             ret.push(c);
62227         });
62228         
62229         return ret;    
62230     },
62231     
62232     findBestRow: function(cells)
62233     {
62234         var ret = 0;
62235         
62236         for (var i =0 ; i < cells.length;i++) {
62237             ret  = Math.max(cells[i].rows || 0,ret);
62238         }
62239         return ret;
62240         
62241     },
62242     
62243     
62244     addItem : function(rec)
62245     {
62246         // look for vertical location slot in
62247         var cells = this.findCells(rec);
62248         
62249         rec.row = this.findBestRow(cells);
62250         
62251         // work out the location.
62252         
62253         var crow = false;
62254         var rows = [];
62255         for(var i =0; i < cells.length; i++) {
62256             if (!crow) {
62257                 crow = {
62258                     start : cells[i],
62259                     end :  cells[i]
62260                 };
62261                 continue;
62262             }
62263             if (crow.start.getY() == cells[i].getY()) {
62264                 // on same row.
62265                 crow.end = cells[i];
62266                 continue;
62267             }
62268             // different row.
62269             rows.push(crow);
62270             crow = {
62271                 start: cells[i],
62272                 end : cells[i]
62273             };
62274             
62275         }
62276         
62277         rows.push(crow);
62278         rec.els = [];
62279         rec.rows = rows;
62280         rec.cells = cells;
62281         for (var i = 0; i < cells.length;i++) {
62282             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
62283             
62284         }
62285         
62286         
62287     },
62288     
62289     clearEvents: function() {
62290         
62291         if (!this.eventStore.getCount()) {
62292             return;
62293         }
62294         // reset number of rows in cells.
62295         Roo.each(this.cells.elements, function(c){
62296             c.rows = 0;
62297         });
62298         
62299         this.eventStore.each(function(e) {
62300             this.clearEvent(e);
62301         },this);
62302         
62303     },
62304     
62305     clearEvent : function(ev)
62306     {
62307         if (ev.els) {
62308             Roo.each(ev.els, function(el) {
62309                 el.un('mouseenter' ,this.onEventEnter, this);
62310                 el.un('mouseleave' ,this.onEventLeave, this);
62311                 el.remove();
62312             },this);
62313             ev.els = [];
62314         }
62315     },
62316     
62317     
62318     renderEvent : function(ev,ctr) {
62319         if (!ctr) {
62320              ctr = this.view.el.select('.fc-event-container',true).first();
62321         }
62322         
62323          
62324         this.clearEvent(ev);
62325             //code
62326        
62327         
62328         
62329         ev.els = [];
62330         var cells = ev.cells;
62331         var rows = ev.rows;
62332         this.fireEvent('eventrender', this, ev);
62333         
62334         for(var i =0; i < rows.length; i++) {
62335             
62336             cls = '';
62337             if (i == 0) {
62338                 cls += ' fc-event-start';
62339             }
62340             if ((i+1) == rows.length) {
62341                 cls += ' fc-event-end';
62342             }
62343             
62344             //Roo.log(ev.data);
62345             // how many rows should it span..
62346             var cg = this.eventTmpl.append(ctr,Roo.apply({
62347                 fccls : cls
62348                 
62349             }, ev.data) , true);
62350             
62351             
62352             cg.on('mouseenter' ,this.onEventEnter, this, ev);
62353             cg.on('mouseleave' ,this.onEventLeave, this, ev);
62354             cg.on('click', this.onEventClick, this, ev);
62355             
62356             ev.els.push(cg);
62357             
62358             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
62359             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
62360             //Roo.log(cg);
62361              
62362             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
62363             cg.setWidth(ebox.right - sbox.x -2);
62364         }
62365     },
62366     
62367     renderEvents: function()
62368     {   
62369         // first make sure there is enough space..
62370         
62371         if (!this.eventTmpl) {
62372             this.eventTmpl = new Roo.Template(
62373                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
62374                     '<div class="fc-event-inner">' +
62375                         '<span class="fc-event-time">{time}</span>' +
62376                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
62377                     '</div>' +
62378                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
62379                 '</div>'
62380             );
62381                 
62382         }
62383                
62384         
62385         
62386         this.cells.each(function(c) {
62387             //Roo.log(c.select('.fc-day-content div',true).first());
62388             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
62389         });
62390         
62391         var ctr = this.view.el.select('.fc-event-container',true).first();
62392         
62393         var cls;
62394         this.eventStore.each(function(ev){
62395             
62396             this.renderEvent(ev);
62397              
62398              
62399         }, this);
62400         this.view.layout();
62401         
62402     },
62403     
62404     onEventEnter: function (e, el,event,d) {
62405         this.fireEvent('evententer', this, el, event);
62406     },
62407     
62408     onEventLeave: function (e, el,event,d) {
62409         this.fireEvent('eventleave', this, el, event);
62410     },
62411     
62412     onEventClick: function (e, el,event,d) {
62413         this.fireEvent('eventclick', this, el, event);
62414     },
62415     
62416     onMonthChange: function () {
62417         this.store.load();
62418     },
62419     
62420     onLoad: function () {
62421         
62422         //Roo.log('calendar onload');
62423 //         
62424         if(this.eventStore.getCount() > 0){
62425             
62426            
62427             
62428             this.eventStore.each(function(d){
62429                 
62430                 
62431                 // FIXME..
62432                 var add =   d.data;
62433                 if (typeof(add.end_dt) == 'undefined')  {
62434                     Roo.log("Missing End time in calendar data: ");
62435                     Roo.log(d);
62436                     return;
62437                 }
62438                 if (typeof(add.start_dt) == 'undefined')  {
62439                     Roo.log("Missing Start time in calendar data: ");
62440                     Roo.log(d);
62441                     return;
62442                 }
62443                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
62444                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
62445                 add.id = add.id || d.id;
62446                 add.title = add.title || '??';
62447                 
62448                 this.addItem(d);
62449                 
62450              
62451             },this);
62452         }
62453         
62454         this.renderEvents();
62455     }
62456     
62457
62458 });
62459 /*
62460  grid : {
62461                 xtype: 'Grid',
62462                 xns: Roo.grid,
62463                 listeners : {
62464                     render : function ()
62465                     {
62466                         _this.grid = this;
62467                         
62468                         if (!this.view.el.hasClass('course-timesheet')) {
62469                             this.view.el.addClass('course-timesheet');
62470                         }
62471                         if (this.tsStyle) {
62472                             this.ds.load({});
62473                             return; 
62474                         }
62475                         Roo.log('width');
62476                         Roo.log(_this.grid.view.el.getWidth());
62477                         
62478                         
62479                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
62480                             '.course-timesheet .x-grid-row' : {
62481                                 height: '80px'
62482                             },
62483                             '.x-grid-row td' : {
62484                                 'vertical-align' : 0
62485                             },
62486                             '.course-edit-link' : {
62487                                 'color' : 'blue',
62488                                 'text-overflow' : 'ellipsis',
62489                                 'overflow' : 'hidden',
62490                                 'white-space' : 'nowrap',
62491                                 'cursor' : 'pointer'
62492                             },
62493                             '.sub-link' : {
62494                                 'color' : 'green'
62495                             },
62496                             '.de-act-sup-link' : {
62497                                 'color' : 'purple',
62498                                 'text-decoration' : 'line-through'
62499                             },
62500                             '.de-act-link' : {
62501                                 'color' : 'red',
62502                                 'text-decoration' : 'line-through'
62503                             },
62504                             '.course-timesheet .course-highlight' : {
62505                                 'border-top-style': 'dashed !important',
62506                                 'border-bottom-bottom': 'dashed !important'
62507                             },
62508                             '.course-timesheet .course-item' : {
62509                                 'font-family'   : 'tahoma, arial, helvetica',
62510                                 'font-size'     : '11px',
62511                                 'overflow'      : 'hidden',
62512                                 'padding-left'  : '10px',
62513                                 'padding-right' : '10px',
62514                                 'padding-top' : '10px' 
62515                             }
62516                             
62517                         }, Roo.id());
62518                                 this.ds.load({});
62519                     }
62520                 },
62521                 autoWidth : true,
62522                 monitorWindowResize : false,
62523                 cellrenderer : function(v,x,r)
62524                 {
62525                     return v;
62526                 },
62527                 sm : {
62528                     xtype: 'CellSelectionModel',
62529                     xns: Roo.grid
62530                 },
62531                 dataSource : {
62532                     xtype: 'Store',
62533                     xns: Roo.data,
62534                     listeners : {
62535                         beforeload : function (_self, options)
62536                         {
62537                             options.params = options.params || {};
62538                             options.params._month = _this.monthField.getValue();
62539                             options.params.limit = 9999;
62540                             options.params['sort'] = 'when_dt';    
62541                             options.params['dir'] = 'ASC';    
62542                             this.proxy.loadResponse = this.loadResponse;
62543                             Roo.log("load?");
62544                             //this.addColumns();
62545                         },
62546                         load : function (_self, records, options)
62547                         {
62548                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
62549                                 // if you click on the translation.. you can edit it...
62550                                 var el = Roo.get(this);
62551                                 var id = el.dom.getAttribute('data-id');
62552                                 var d = el.dom.getAttribute('data-date');
62553                                 var t = el.dom.getAttribute('data-time');
62554                                 //var id = this.child('span').dom.textContent;
62555                                 
62556                                 //Roo.log(this);
62557                                 Pman.Dialog.CourseCalendar.show({
62558                                     id : id,
62559                                     when_d : d,
62560                                     when_t : t,
62561                                     productitem_active : id ? 1 : 0
62562                                 }, function() {
62563                                     _this.grid.ds.load({});
62564                                 });
62565                            
62566                            });
62567                            
62568                            _this.panel.fireEvent('resize', [ '', '' ]);
62569                         }
62570                     },
62571                     loadResponse : function(o, success, response){
62572                             // this is overridden on before load..
62573                             
62574                             Roo.log("our code?");       
62575                             //Roo.log(success);
62576                             //Roo.log(response)
62577                             delete this.activeRequest;
62578                             if(!success){
62579                                 this.fireEvent("loadexception", this, o, response);
62580                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62581                                 return;
62582                             }
62583                             var result;
62584                             try {
62585                                 result = o.reader.read(response);
62586                             }catch(e){
62587                                 Roo.log("load exception?");
62588                                 this.fireEvent("loadexception", this, o, response, e);
62589                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
62590                                 return;
62591                             }
62592                             Roo.log("ready...");        
62593                             // loop through result.records;
62594                             // and set this.tdate[date] = [] << array of records..
62595                             _this.tdata  = {};
62596                             Roo.each(result.records, function(r){
62597                                 //Roo.log(r.data);
62598                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
62599                                     _this.tdata[r.data.when_dt.format('j')] = [];
62600                                 }
62601                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
62602                             });
62603                             
62604                             //Roo.log(_this.tdata);
62605                             
62606                             result.records = [];
62607                             result.totalRecords = 6;
62608                     
62609                             // let's generate some duumy records for the rows.
62610                             //var st = _this.dateField.getValue();
62611                             
62612                             // work out monday..
62613                             //st = st.add(Date.DAY, -1 * st.format('w'));
62614                             
62615                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62616                             
62617                             var firstOfMonth = date.getFirstDayOfMonth();
62618                             var days = date.getDaysInMonth();
62619                             var d = 1;
62620                             var firstAdded = false;
62621                             for (var i = 0; i < result.totalRecords ; i++) {
62622                                 //var d= st.add(Date.DAY, i);
62623                                 var row = {};
62624                                 var added = 0;
62625                                 for(var w = 0 ; w < 7 ; w++){
62626                                     if(!firstAdded && firstOfMonth != w){
62627                                         continue;
62628                                     }
62629                                     if(d > days){
62630                                         continue;
62631                                     }
62632                                     firstAdded = true;
62633                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
62634                                     row['weekday'+w] = String.format(
62635                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
62636                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
62637                                                     d,
62638                                                     date.format('Y-m-')+dd
62639                                                 );
62640                                     added++;
62641                                     if(typeof(_this.tdata[d]) != 'undefined'){
62642                                         Roo.each(_this.tdata[d], function(r){
62643                                             var is_sub = '';
62644                                             var deactive = '';
62645                                             var id = r.id;
62646                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
62647                                             if(r.parent_id*1>0){
62648                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
62649                                                 id = r.parent_id;
62650                                             }
62651                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
62652                                                 deactive = 'de-act-link';
62653                                             }
62654                                             
62655                                             row['weekday'+w] += String.format(
62656                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
62657                                                     id, //0
62658                                                     r.product_id_name, //1
62659                                                     r.when_dt.format('h:ia'), //2
62660                                                     is_sub, //3
62661                                                     deactive, //4
62662                                                     desc // 5
62663                                             );
62664                                         });
62665                                     }
62666                                     d++;
62667                                 }
62668                                 
62669                                 // only do this if something added..
62670                                 if(added > 0){ 
62671                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
62672                                 }
62673                                 
62674                                 
62675                                 // push it twice. (second one with an hour..
62676                                 
62677                             }
62678                             //Roo.log(result);
62679                             this.fireEvent("load", this, o, o.request.arg);
62680                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
62681                         },
62682                     sortInfo : {field: 'when_dt', direction : 'ASC' },
62683                     proxy : {
62684                         xtype: 'HttpProxy',
62685                         xns: Roo.data,
62686                         method : 'GET',
62687                         url : baseURL + '/Roo/Shop_course.php'
62688                     },
62689                     reader : {
62690                         xtype: 'JsonReader',
62691                         xns: Roo.data,
62692                         id : 'id',
62693                         fields : [
62694                             {
62695                                 'name': 'id',
62696                                 'type': 'int'
62697                             },
62698                             {
62699                                 'name': 'when_dt',
62700                                 'type': 'string'
62701                             },
62702                             {
62703                                 'name': 'end_dt',
62704                                 'type': 'string'
62705                             },
62706                             {
62707                                 'name': 'parent_id',
62708                                 'type': 'int'
62709                             },
62710                             {
62711                                 'name': 'product_id',
62712                                 'type': 'int'
62713                             },
62714                             {
62715                                 'name': 'productitem_id',
62716                                 'type': 'int'
62717                             },
62718                             {
62719                                 'name': 'guid',
62720                                 'type': 'int'
62721                             }
62722                         ]
62723                     }
62724                 },
62725                 toolbar : {
62726                     xtype: 'Toolbar',
62727                     xns: Roo,
62728                     items : [
62729                         {
62730                             xtype: 'Button',
62731                             xns: Roo.Toolbar,
62732                             listeners : {
62733                                 click : function (_self, e)
62734                                 {
62735                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62736                                     sd.setMonth(sd.getMonth()-1);
62737                                     _this.monthField.setValue(sd.format('Y-m-d'));
62738                                     _this.grid.ds.load({});
62739                                 }
62740                             },
62741                             text : "Back"
62742                         },
62743                         {
62744                             xtype: 'Separator',
62745                             xns: Roo.Toolbar
62746                         },
62747                         {
62748                             xtype: 'MonthField',
62749                             xns: Roo.form,
62750                             listeners : {
62751                                 render : function (_self)
62752                                 {
62753                                     _this.monthField = _self;
62754                                    // _this.monthField.set  today
62755                                 },
62756                                 select : function (combo, date)
62757                                 {
62758                                     _this.grid.ds.load({});
62759                                 }
62760                             },
62761                             value : (function() { return new Date(); })()
62762                         },
62763                         {
62764                             xtype: 'Separator',
62765                             xns: Roo.Toolbar
62766                         },
62767                         {
62768                             xtype: 'TextItem',
62769                             xns: Roo.Toolbar,
62770                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
62771                         },
62772                         {
62773                             xtype: 'Fill',
62774                             xns: Roo.Toolbar
62775                         },
62776                         {
62777                             xtype: 'Button',
62778                             xns: Roo.Toolbar,
62779                             listeners : {
62780                                 click : function (_self, e)
62781                                 {
62782                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
62783                                     sd.setMonth(sd.getMonth()+1);
62784                                     _this.monthField.setValue(sd.format('Y-m-d'));
62785                                     _this.grid.ds.load({});
62786                                 }
62787                             },
62788                             text : "Next"
62789                         }
62790                     ]
62791                 },
62792                  
62793             }
62794         };
62795         
62796         *//*
62797  * Based on:
62798  * Ext JS Library 1.1.1
62799  * Copyright(c) 2006-2007, Ext JS, LLC.
62800  *
62801  * Originally Released Under LGPL - original licence link has changed is not relivant.
62802  *
62803  * Fork - LGPL
62804  * <script type="text/javascript">
62805  */
62806  
62807 /**
62808  * @class Roo.LoadMask
62809  * A simple utility class for generically masking elements while loading data.  If the element being masked has
62810  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
62811  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
62812  * element's UpdateManager load indicator and will be destroyed after the initial load.
62813  * @constructor
62814  * Create a new LoadMask
62815  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
62816  * @param {Object} config The config object
62817  */
62818 Roo.LoadMask = function(el, config){
62819     this.el = Roo.get(el);
62820     Roo.apply(this, config);
62821     if(this.store){
62822         this.store.on('beforeload', this.onBeforeLoad, this);
62823         this.store.on('load', this.onLoad, this);
62824         this.store.on('loadexception', this.onLoadException, this);
62825         this.removeMask = false;
62826     }else{
62827         var um = this.el.getUpdateManager();
62828         um.showLoadIndicator = false; // disable the default indicator
62829         um.on('beforeupdate', this.onBeforeLoad, this);
62830         um.on('update', this.onLoad, this);
62831         um.on('failure', this.onLoad, this);
62832         this.removeMask = true;
62833     }
62834 };
62835
62836 Roo.LoadMask.prototype = {
62837     /**
62838      * @cfg {Boolean} removeMask
62839      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
62840      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
62841      */
62842     removeMask : false,
62843     /**
62844      * @cfg {String} msg
62845      * The text to display in a centered loading message box (defaults to 'Loading...')
62846      */
62847     msg : 'Loading...',
62848     /**
62849      * @cfg {String} msgCls
62850      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
62851      */
62852     msgCls : 'x-mask-loading',
62853
62854     /**
62855      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
62856      * @type Boolean
62857      */
62858     disabled: false,
62859
62860     /**
62861      * Disables the mask to prevent it from being displayed
62862      */
62863     disable : function(){
62864        this.disabled = true;
62865     },
62866
62867     /**
62868      * Enables the mask so that it can be displayed
62869      */
62870     enable : function(){
62871         this.disabled = false;
62872     },
62873     
62874     onLoadException : function()
62875     {
62876         Roo.log(arguments);
62877         
62878         if (typeof(arguments[3]) != 'undefined') {
62879             Roo.MessageBox.alert("Error loading",arguments[3]);
62880         } 
62881         /*
62882         try {
62883             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
62884                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
62885             }   
62886         } catch(e) {
62887             
62888         }
62889         */
62890     
62891         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62892     },
62893     // private
62894     onLoad : function()
62895     {
62896         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
62897     },
62898
62899     // private
62900     onBeforeLoad : function(){
62901         if(!this.disabled){
62902             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
62903         }
62904     },
62905
62906     // private
62907     destroy : function(){
62908         if(this.store){
62909             this.store.un('beforeload', this.onBeforeLoad, this);
62910             this.store.un('load', this.onLoad, this);
62911             this.store.un('loadexception', this.onLoadException, this);
62912         }else{
62913             var um = this.el.getUpdateManager();
62914             um.un('beforeupdate', this.onBeforeLoad, this);
62915             um.un('update', this.onLoad, this);
62916             um.un('failure', this.onLoad, this);
62917         }
62918     }
62919 };/*
62920  * Based on:
62921  * Ext JS Library 1.1.1
62922  * Copyright(c) 2006-2007, Ext JS, LLC.
62923  *
62924  * Originally Released Under LGPL - original licence link has changed is not relivant.
62925  *
62926  * Fork - LGPL
62927  * <script type="text/javascript">
62928  */
62929
62930
62931 /**
62932  * @class Roo.XTemplate
62933  * @extends Roo.Template
62934  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
62935 <pre><code>
62936 var t = new Roo.XTemplate(
62937         '&lt;select name="{name}"&gt;',
62938                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
62939         '&lt;/select&gt;'
62940 );
62941  
62942 // then append, applying the master template values
62943  </code></pre>
62944  *
62945  * Supported features:
62946  *
62947  *  Tags:
62948
62949 <pre><code>
62950       {a_variable} - output encoded.
62951       {a_variable.format:("Y-m-d")} - call a method on the variable
62952       {a_variable:raw} - unencoded output
62953       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
62954       {a_variable:this.method_on_template(...)} - call a method on the template object.
62955  
62956 </code></pre>
62957  *  The tpl tag:
62958 <pre><code>
62959         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
62960         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
62961         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
62962         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
62963   
62964         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
62965         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
62966 </code></pre>
62967  *      
62968  */
62969 Roo.XTemplate = function()
62970 {
62971     Roo.XTemplate.superclass.constructor.apply(this, arguments);
62972     if (this.html) {
62973         this.compile();
62974     }
62975 };
62976
62977
62978 Roo.extend(Roo.XTemplate, Roo.Template, {
62979
62980     /**
62981      * The various sub templates
62982      */
62983     tpls : false,
62984     /**
62985      *
62986      * basic tag replacing syntax
62987      * WORD:WORD()
62988      *
62989      * // you can fake an object call by doing this
62990      *  x.t:(test,tesT) 
62991      * 
62992      */
62993     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
62994
62995     /**
62996      * compile the template
62997      *
62998      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
62999      *
63000      */
63001     compile: function()
63002     {
63003         var s = this.html;
63004      
63005         s = ['<tpl>', s, '</tpl>'].join('');
63006     
63007         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
63008             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
63009             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
63010             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
63011             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
63012             m,
63013             id     = 0,
63014             tpls   = [];
63015     
63016         while(true == !!(m = s.match(re))){
63017             var forMatch   = m[0].match(nameRe),
63018                 ifMatch   = m[0].match(ifRe),
63019                 execMatch   = m[0].match(execRe),
63020                 namedMatch   = m[0].match(namedRe),
63021                 
63022                 exp  = null, 
63023                 fn   = null,
63024                 exec = null,
63025                 name = forMatch && forMatch[1] ? forMatch[1] : '';
63026                 
63027             if (ifMatch) {
63028                 // if - puts fn into test..
63029                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
63030                 if(exp){
63031                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
63032                 }
63033             }
63034             
63035             if (execMatch) {
63036                 // exec - calls a function... returns empty if true is  returned.
63037                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
63038                 if(exp){
63039                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
63040                 }
63041             }
63042             
63043             
63044             if (name) {
63045                 // for = 
63046                 switch(name){
63047                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
63048                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
63049                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
63050                 }
63051             }
63052             var uid = namedMatch ? namedMatch[1] : id;
63053             
63054             
63055             tpls.push({
63056                 id:     namedMatch ? namedMatch[1] : id,
63057                 target: name,
63058                 exec:   exec,
63059                 test:   fn,
63060                 body:   m[1] || ''
63061             });
63062             if (namedMatch) {
63063                 s = s.replace(m[0], '');
63064             } else { 
63065                 s = s.replace(m[0], '{xtpl'+ id + '}');
63066             }
63067             ++id;
63068         }
63069         this.tpls = [];
63070         for(var i = tpls.length-1; i >= 0; --i){
63071             this.compileTpl(tpls[i]);
63072             this.tpls[tpls[i].id] = tpls[i];
63073         }
63074         this.master = tpls[tpls.length-1];
63075         return this;
63076     },
63077     /**
63078      * same as applyTemplate, except it's done to one of the subTemplates
63079      * when using named templates, you can do:
63080      *
63081      * var str = pl.applySubTemplate('your-name', values);
63082      *
63083      * 
63084      * @param {Number} id of the template
63085      * @param {Object} values to apply to template
63086      * @param {Object} parent (normaly the instance of this object)
63087      */
63088     applySubTemplate : function(id, values, parent)
63089     {
63090         
63091         
63092         var t = this.tpls[id];
63093         
63094         
63095         try { 
63096             if(t.test && !t.test.call(this, values, parent)){
63097                 return '';
63098             }
63099         } catch(e) {
63100             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
63101             Roo.log(e.toString());
63102             Roo.log(t.test);
63103             return ''
63104         }
63105         try { 
63106             
63107             if(t.exec && t.exec.call(this, values, parent)){
63108                 return '';
63109             }
63110         } catch(e) {
63111             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
63112             Roo.log(e.toString());
63113             Roo.log(t.exec);
63114             return ''
63115         }
63116         try {
63117             var vs = t.target ? t.target.call(this, values, parent) : values;
63118             parent = t.target ? values : parent;
63119             if(t.target && vs instanceof Array){
63120                 var buf = [];
63121                 for(var i = 0, len = vs.length; i < len; i++){
63122                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
63123                 }
63124                 return buf.join('');
63125             }
63126             return t.compiled.call(this, vs, parent);
63127         } catch (e) {
63128             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
63129             Roo.log(e.toString());
63130             Roo.log(t.compiled);
63131             return '';
63132         }
63133     },
63134
63135     compileTpl : function(tpl)
63136     {
63137         var fm = Roo.util.Format;
63138         var useF = this.disableFormats !== true;
63139         var sep = Roo.isGecko ? "+" : ",";
63140         var undef = function(str) {
63141             Roo.log("Property not found :"  + str);
63142             return '';
63143         };
63144         
63145         var fn = function(m, name, format, args)
63146         {
63147             //Roo.log(arguments);
63148             args = args ? args.replace(/\\'/g,"'") : args;
63149             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
63150             if (typeof(format) == 'undefined') {
63151                 format= 'htmlEncode';
63152             }
63153             if (format == 'raw' ) {
63154                 format = false;
63155             }
63156             
63157             if(name.substr(0, 4) == 'xtpl'){
63158                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
63159             }
63160             
63161             // build an array of options to determine if value is undefined..
63162             
63163             // basically get 'xxxx.yyyy' then do
63164             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
63165             //    (function () { Roo.log("Property not found"); return ''; })() :
63166             //    ......
63167             
63168             var udef_ar = [];
63169             var lookfor = '';
63170             Roo.each(name.split('.'), function(st) {
63171                 lookfor += (lookfor.length ? '.': '') + st;
63172                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
63173             });
63174             
63175             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
63176             
63177             
63178             if(format && useF){
63179                 
63180                 args = args ? ',' + args : "";
63181                  
63182                 if(format.substr(0, 5) != "this."){
63183                     format = "fm." + format + '(';
63184                 }else{
63185                     format = 'this.call("'+ format.substr(5) + '", ';
63186                     args = ", values";
63187                 }
63188                 
63189                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
63190             }
63191              
63192             if (args.length) {
63193                 // called with xxyx.yuu:(test,test)
63194                 // change to ()
63195                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
63196             }
63197             // raw.. - :raw modifier..
63198             return "'"+ sep + udef_st  + name + ")"+sep+"'";
63199             
63200         };
63201         var body;
63202         // branched to use + in gecko and [].join() in others
63203         if(Roo.isGecko){
63204             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
63205                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
63206                     "';};};";
63207         }else{
63208             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
63209             body.push(tpl.body.replace(/(\r\n|\n)/g,
63210                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
63211             body.push("'].join('');};};");
63212             body = body.join('');
63213         }
63214         
63215         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
63216        
63217         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
63218         eval(body);
63219         
63220         return this;
63221     },
63222
63223     applyTemplate : function(values){
63224         return this.master.compiled.call(this, values, {});
63225         //var s = this.subs;
63226     },
63227
63228     apply : function(){
63229         return this.applyTemplate.apply(this, arguments);
63230     }
63231
63232  });
63233
63234 Roo.XTemplate.from = function(el){
63235     el = Roo.getDom(el);
63236     return new Roo.XTemplate(el.value || el.innerHTML);
63237 };