roojs-ui.js
[roojs1] / roojs-debug.js
1 /*
2  * Based on:
3  * Ext JS Library 1.1.1
4  * Copyright(c) 2006-2007, Ext JS, LLC.
5  *
6  * Originally Released Under LGPL - original licence link has changed is not relivant.
7  *
8  * Fork - LGPL
9  * <script type="text/javascript">
10  */
11  
12
13
14
15
16 // for old browsers
17 window["undefined"] = window["undefined"];
18
19 /**
20  * @class Roo
21  * Roo core utilities and functions.
22  * @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     // this is a bit like the react update code...
5150     // 
5151     
5152     var updateNode = function(from, to)
5153     {
5154         // should we handle non-standard elements?
5155         
5156         if (from.nodeType != to.nodeType) {
5157             from.parentNode.replaceChild(to, from);
5158         }
5159         
5160         if (from.nodeType == 3) {
5161             // assume it's text?!
5162             if (from.data == to.data) {
5163                 return;
5164             }
5165             from.data = to.data;
5166             return;
5167         }
5168         
5169         // assume 'to' doesnt have '1/3 nodetypes!
5170         if (from.nodeType !=1 || from.tagName != to.tagName) {
5171             from.parentNode.replaceChild(to, from);
5172             return;
5173         }
5174         // compare attributes
5175         var ar = Array.from(from.attributes);
5176         for(var i = 0; i< ar.length;i++) {
5177             if (to.hasAttribute(ar[i].name)) {
5178                 continue;
5179             }
5180             from.removeAttribute(ar[i].name);
5181         }
5182         ar = to.attributes;
5183         for(var i = 0; i< ar.length;i++) {
5184             if (from.getAttribute(ar[i].name) == to.getAttribute(ar[i].name)) {
5185                 continue;
5186             }
5187             from.setAttribute(ar[i].name, to.getAttribute(ar[i].name));
5188         }
5189         // children
5190         var far = Array.from(from.childNodes);
5191         var tar = Array.from(to.childNodes);
5192         // if the lengths are different.. then it's probably a editable content change, rather than
5193         // a change of the block definition..
5194         if (from.innerHTML == to.innerHTML) {
5195             return;
5196         }
5197         if (far.length != tar.length) {
5198             from.innerHTML = to.innerHTML;
5199             return;
5200         }
5201         
5202         for(var i = 0; i < far.length; i++) {
5203             updateNode(far[i], tar[i]);
5204         }
5205         
5206         
5207     };
5208     
5209     
5210
5211     return {
5212         /** True to force the use of DOM instead of html fragments @type Boolean */
5213         useDom : false,
5214     
5215         /**
5216          * Returns the markup for the passed Element(s) config
5217          * @param {Object} o The Dom object spec (and children)
5218          * @return {String}
5219          */
5220         markup : function(o){
5221             return createHtml(o);
5222         },
5223     
5224         /**
5225          * Applies a style specification to an element
5226          * @param {String/HTMLElement} el The element to apply styles to
5227          * @param {String/Object/Function} styles A style specification string eg "width:100px", or object in the form {width:"100px"}, or
5228          * a function which returns such a specification.
5229          */
5230         applyStyles : function(el, styles){
5231             if(styles){
5232                el = Roo.fly(el);
5233                if(typeof styles == "string"){
5234                    var re = /\s?([a-z\-]*)\:\s?([^;]*);?/gi;
5235                    var matches;
5236                    while ((matches = re.exec(styles)) != null){
5237                        el.setStyle(matches[1], matches[2]);
5238                    }
5239                }else if (typeof styles == "object"){
5240                    for (var style in styles){
5241                       el.setStyle(style, styles[style]);
5242                    }
5243                }else if (typeof styles == "function"){
5244                     Roo.DomHelper.applyStyles(el, styles.call());
5245                }
5246             }
5247         },
5248     
5249         /**
5250          * Inserts an HTML fragment into the Dom
5251          * @param {String} where Where to insert the html in relation to el - beforeBegin, afterBegin, beforeEnd, afterEnd.
5252          * @param {HTMLElement} el The context element
5253          * @param {String} html The HTML fragmenet
5254          * @return {HTMLElement} The new node
5255          */
5256         insertHtml : function(where, el, html){
5257             where = where.toLowerCase();
5258             if(el.insertAdjacentHTML){
5259                 if(tableRe.test(el.tagName)){
5260                     var rs;
5261                     if(rs = insertIntoTable(el.tagName.toLowerCase(), where, el, html)){
5262                         return rs;
5263                     }
5264                 }
5265                 switch(where){
5266                     case "beforebegin":
5267                         el.insertAdjacentHTML('BeforeBegin', html);
5268                         return el.previousSibling;
5269                     case "afterbegin":
5270                         el.insertAdjacentHTML('AfterBegin', html);
5271                         return el.firstChild;
5272                     case "beforeend":
5273                         el.insertAdjacentHTML('BeforeEnd', html);
5274                         return el.lastChild;
5275                     case "afterend":
5276                         el.insertAdjacentHTML('AfterEnd', html);
5277                         return el.nextSibling;
5278                 }
5279                 throw 'Illegal insertion point -> "' + where + '"';
5280             }
5281             var range = el.ownerDocument.createRange();
5282             var frag;
5283             switch(where){
5284                  case "beforebegin":
5285                     range.setStartBefore(el);
5286                     frag = range.createContextualFragment(html);
5287                     el.parentNode.insertBefore(frag, el);
5288                     return el.previousSibling;
5289                  case "afterbegin":
5290                     if(el.firstChild){
5291                         range.setStartBefore(el.firstChild);
5292                         frag = range.createContextualFragment(html);
5293                         el.insertBefore(frag, el.firstChild);
5294                         return el.firstChild;
5295                     }else{
5296                         el.innerHTML = html;
5297                         return el.firstChild;
5298                     }
5299                 case "beforeend":
5300                     if(el.lastChild){
5301                         range.setStartAfter(el.lastChild);
5302                         frag = range.createContextualFragment(html);
5303                         el.appendChild(frag);
5304                         return el.lastChild;
5305                     }else{
5306                         el.innerHTML = html;
5307                         return el.lastChild;
5308                     }
5309                 case "afterend":
5310                     range.setStartAfter(el);
5311                     frag = range.createContextualFragment(html);
5312                     el.parentNode.insertBefore(frag, el.nextSibling);
5313                     return el.nextSibling;
5314                 }
5315                 throw 'Illegal insertion point -> "' + where + '"';
5316         },
5317     
5318         /**
5319          * Creates new Dom element(s) and inserts them before el
5320          * @param {String/HTMLElement/Element} el The context element
5321          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5322          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5323          * @return {HTMLElement/Roo.Element} The new node
5324          */
5325         insertBefore : function(el, o, returnElement){
5326             return this.doInsert(el, o, returnElement, "beforeBegin");
5327         },
5328     
5329         /**
5330          * Creates new Dom element(s) and inserts them after el
5331          * @param {String/HTMLElement/Element} el The context element
5332          * @param {Object} o The Dom object spec (and children)
5333          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5334          * @return {HTMLElement/Roo.Element} The new node
5335          */
5336         insertAfter : function(el, o, returnElement){
5337             return this.doInsert(el, o, returnElement, "afterEnd", "nextSibling");
5338         },
5339     
5340         /**
5341          * Creates new Dom element(s) and inserts them as the first child of el
5342          * @param {String/HTMLElement/Element} el The context element
5343          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5344          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5345          * @return {HTMLElement/Roo.Element} The new node
5346          */
5347         insertFirst : function(el, o, returnElement){
5348             return this.doInsert(el, o, returnElement, "afterBegin");
5349         },
5350     
5351         // private
5352         doInsert : function(el, o, returnElement, pos, sibling){
5353             el = Roo.getDom(el);
5354             var newNode;
5355             if(this.useDom || o.ns){
5356                 newNode = createDom(o, null);
5357                 el.parentNode.insertBefore(newNode, sibling ? el[sibling] : el);
5358             }else{
5359                 var html = createHtml(o);
5360                 newNode = this.insertHtml(pos, el, html);
5361             }
5362             return returnElement ? Roo.get(newNode, true) : newNode;
5363         },
5364     
5365         /**
5366          * Creates new Dom element(s) and appends them to el
5367          * @param {String/HTMLElement/Element} el The context element
5368          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5369          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5370          * @return {HTMLElement/Roo.Element} The new node
5371          */
5372         append : function(el, o, returnElement){
5373             el = Roo.getDom(el);
5374             var newNode;
5375             if(this.useDom || o.ns){
5376                 newNode = createDom(o, null);
5377                 el.appendChild(newNode);
5378             }else{
5379                 var html = createHtml(o);
5380                 newNode = this.insertHtml("beforeEnd", el, html);
5381             }
5382             return returnElement ? Roo.get(newNode, true) : newNode;
5383         },
5384     
5385         /**
5386          * Creates new Dom element(s) and overwrites the contents of el with them
5387          * @param {String/HTMLElement/Element} el The context element
5388          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5389          * @param {Boolean} returnElement (optional) true to return a Roo.Element
5390          * @return {HTMLElement/Roo.Element} The new node
5391          */
5392         overwrite : function(el, o, returnElement)
5393         {
5394             el = Roo.getDom(el);
5395             if (o.ns) {
5396               
5397                 while (el.childNodes.length) {
5398                     el.removeChild(el.firstChild);
5399                 }
5400                 createDom(o, el);
5401             } else {
5402                 el.innerHTML = createHtml(o);   
5403             }
5404             
5405             return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5406         },
5407     
5408         /**
5409          * Creates a new Roo.DomHelper.Template from the Dom object spec
5410          * @param {Object} o The Dom object spec (and children)
5411          * @return {Roo.DomHelper.Template} The new template
5412          */
5413         createTemplate : function(o){
5414             var html = createHtml(o);
5415             return new Roo.Template(html);
5416         },
5417          /**
5418          * Updates the first element with the spec from the o (replacing if necessary)
5419          * This iterates through the children, and updates attributes / children etc..
5420          * @param {String/HTMLElement/Element} el The context element
5421          * @param {Object/String} o The Dom object spec (and children) or raw HTML blob
5422          */
5423         
5424         update : function(el, o)
5425         {
5426             updateNode(Roo.getDom(el), createDom(o));
5427             
5428         }
5429         
5430         
5431     };
5432 }();
5433 /*
5434  * Based on:
5435  * Ext JS Library 1.1.1
5436  * Copyright(c) 2006-2007, Ext JS, LLC.
5437  *
5438  * Originally Released Under LGPL - original licence link has changed is not relivant.
5439  *
5440  * Fork - LGPL
5441  * <script type="text/javascript">
5442  */
5443  
5444 /**
5445 * @class Roo.Template
5446 * Represents an HTML fragment template. Templates can be precompiled for greater performance.
5447 * For a list of available format functions, see {@link Roo.util.Format}.<br />
5448 * Usage:
5449 <pre><code>
5450 var t = new Roo.Template({
5451     html :  '&lt;div name="{id}"&gt;' + 
5452         '&lt;span class="{cls}"&gt;{name:trim} {someval:this.myformat}{value:ellipsis(10)}&lt;/span&gt;' +
5453         '&lt;/div&gt;',
5454     myformat: function (value, allValues) {
5455         return 'XX' + value;
5456     }
5457 });
5458 t.append('some-element', {id: 'myid', cls: 'myclass', name: 'foo', value: 'bar'});
5459 </code></pre>
5460 * For more information see this blog post with examples:
5461 *  <a href="http://www.cnitblog.com/seeyeah/archive/2011/12/30/38728.html/">DomHelper
5462      - Create Elements using DOM, HTML fragments and Templates</a>. 
5463 * @constructor
5464 * @param {Object} cfg - Configuration object.
5465 */
5466 Roo.Template = function(cfg){
5467     // BC!
5468     if(cfg instanceof Array){
5469         cfg = cfg.join("");
5470     }else if(arguments.length > 1){
5471         cfg = Array.prototype.join.call(arguments, "");
5472     }
5473     
5474     
5475     if (typeof(cfg) == 'object') {
5476         Roo.apply(this,cfg)
5477     } else {
5478         // bc
5479         this.html = cfg;
5480     }
5481     if (this.url) {
5482         this.load();
5483     }
5484     
5485 };
5486 Roo.Template.prototype = {
5487     
5488     /**
5489      * @cfg {Function} onLoad Called after the template has been loaded and complied (usually from a remove source)
5490      */
5491     onLoad : false,
5492     
5493     
5494     /**
5495      * @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..
5496      *                    it should be fixed so that template is observable...
5497      */
5498     url : false,
5499     /**
5500      * @cfg {String} html  The HTML fragment or an array of fragments to join("") or multiple arguments to join("")
5501      */
5502     html : '',
5503     
5504     
5505     compiled : false,
5506     loaded : false,
5507     /**
5508      * Returns an HTML fragment of this template with the specified values applied.
5509      * @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'})
5510      * @return {String} The HTML fragment
5511      */
5512     
5513    
5514     
5515     applyTemplate : function(values){
5516         //Roo.log(["applyTemplate", values]);
5517         try {
5518            
5519             if(this.compiled){
5520                 return this.compiled(values);
5521             }
5522             var useF = this.disableFormats !== true;
5523             var fm = Roo.util.Format, tpl = this;
5524             var fn = function(m, name, format, args){
5525                 if(format && useF){
5526                     if(format.substr(0, 5) == "this."){
5527                         return tpl.call(format.substr(5), values[name], values);
5528                     }else{
5529                         if(args){
5530                             // quoted values are required for strings in compiled templates, 
5531                             // but for non compiled we need to strip them
5532                             // quoted reversed for jsmin
5533                             var re = /^\s*['"](.*)["']\s*$/;
5534                             args = args.split(',');
5535                             for(var i = 0, len = args.length; i < len; i++){
5536                                 args[i] = args[i].replace(re, "$1");
5537                             }
5538                             args = [values[name]].concat(args);
5539                         }else{
5540                             args = [values[name]];
5541                         }
5542                         return fm[format].apply(fm, args);
5543                     }
5544                 }else{
5545                     return values[name] !== undefined ? values[name] : "";
5546                 }
5547             };
5548             return this.html.replace(this.re, fn);
5549         } catch (e) {
5550             Roo.log(e);
5551             throw e;
5552         }
5553          
5554     },
5555     
5556     loading : false,
5557       
5558     load : function ()
5559     {
5560          
5561         if (this.loading) {
5562             return;
5563         }
5564         var _t = this;
5565         
5566         this.loading = true;
5567         this.compiled = false;
5568         
5569         var cx = new Roo.data.Connection();
5570         cx.request({
5571             url : this.url,
5572             method : 'GET',
5573             success : function (response) {
5574                 _t.loading = false;
5575                 _t.url = false;
5576                 
5577                 _t.set(response.responseText,true);
5578                 _t.loaded = true;
5579                 if (_t.onLoad) {
5580                     _t.onLoad();
5581                 }
5582              },
5583             failure : function(response) {
5584                 Roo.log("Template failed to load from " + _t.url);
5585                 _t.loading = false;
5586             }
5587         });
5588     },
5589
5590     /**
5591      * Sets the HTML used as the template and optionally compiles it.
5592      * @param {String} html
5593      * @param {Boolean} compile (optional) True to compile the template (defaults to undefined)
5594      * @return {Roo.Template} this
5595      */
5596     set : function(html, compile){
5597         this.html = html;
5598         this.compiled = false;
5599         if(compile){
5600             this.compile();
5601         }
5602         return this;
5603     },
5604     
5605     /**
5606      * True to disable format functions (defaults to false)
5607      * @type Boolean
5608      */
5609     disableFormats : false,
5610     
5611     /**
5612     * The regular expression used to match template variables 
5613     * @type RegExp
5614     * @property 
5615     */
5616     re : /\{([\w-]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
5617     
5618     /**
5619      * Compiles the template into an internal function, eliminating the RegEx overhead.
5620      * @return {Roo.Template} this
5621      */
5622     compile : function(){
5623         var fm = Roo.util.Format;
5624         var useF = this.disableFormats !== true;
5625         var sep = Roo.isGecko ? "+" : ",";
5626         var fn = function(m, name, format, args){
5627             if(format && useF){
5628                 args = args ? ',' + args : "";
5629                 if(format.substr(0, 5) != "this."){
5630                     format = "fm." + format + '(';
5631                 }else{
5632                     format = 'this.call("'+ format.substr(5) + '", ';
5633                     args = ", values";
5634                 }
5635             }else{
5636                 args= ''; format = "(values['" + name + "'] == undefined ? '' : ";
5637             }
5638             return "'"+ sep + format + "values['" + name + "']" + args + ")"+sep+"'";
5639         };
5640         var body;
5641         // branched to use + in gecko and [].join() in others
5642         if(Roo.isGecko){
5643             body = "this.compiled = function(values){ return '" +
5644                    this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
5645                     "';};";
5646         }else{
5647             body = ["this.compiled = function(values){ return ['"];
5648             body.push(this.html.replace(/\\/g, '\\\\').replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn));
5649             body.push("'].join('');};");
5650             body = body.join('');
5651         }
5652         /**
5653          * eval:var:values
5654          * eval:var:fm
5655          */
5656         eval(body);
5657         return this;
5658     },
5659     
5660     // private function used to call members
5661     call : function(fnName, value, allValues){
5662         return this[fnName](value, allValues);
5663     },
5664     
5665     /**
5666      * Applies the supplied values to the template and inserts the new node(s) as the first child of el.
5667      * @param {String/HTMLElement/Roo.Element} el The context element
5668      * @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'})
5669      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5670      * @return {HTMLElement/Roo.Element} The new node or Element
5671      */
5672     insertFirst: function(el, values, returnElement){
5673         return this.doInsert('afterBegin', el, values, returnElement);
5674     },
5675
5676     /**
5677      * Applies the supplied values to the template and inserts the new node(s) before el.
5678      * @param {String/HTMLElement/Roo.Element} el The context element
5679      * @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'})
5680      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5681      * @return {HTMLElement/Roo.Element} The new node or Element
5682      */
5683     insertBefore: function(el, values, returnElement){
5684         return this.doInsert('beforeBegin', el, values, returnElement);
5685     },
5686
5687     /**
5688      * Applies the supplied values to the template and inserts the new node(s) after el.
5689      * @param {String/HTMLElement/Roo.Element} el The context element
5690      * @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'})
5691      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5692      * @return {HTMLElement/Roo.Element} The new node or Element
5693      */
5694     insertAfter : function(el, values, returnElement){
5695         return this.doInsert('afterEnd', el, values, returnElement);
5696     },
5697     
5698     /**
5699      * Applies the supplied values to the template and appends the new node(s) to el.
5700      * @param {String/HTMLElement/Roo.Element} el The context element
5701      * @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'})
5702      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5703      * @return {HTMLElement/Roo.Element} The new node or Element
5704      */
5705     append : function(el, values, returnElement){
5706         return this.doInsert('beforeEnd', el, values, returnElement);
5707     },
5708
5709     doInsert : function(where, el, values, returnEl){
5710         el = Roo.getDom(el);
5711         var newNode = Roo.DomHelper.insertHtml(where, el, this.applyTemplate(values));
5712         return returnEl ? Roo.get(newNode, true) : newNode;
5713     },
5714
5715     /**
5716      * Applies the supplied values to the template and overwrites the content of el with the new node(s).
5717      * @param {String/HTMLElement/Roo.Element} el The context element
5718      * @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'})
5719      * @param {Boolean} returnElement (optional) true to return a Roo.Element (defaults to undefined)
5720      * @return {HTMLElement/Roo.Element} The new node or Element
5721      */
5722     overwrite : function(el, values, returnElement){
5723         el = Roo.getDom(el);
5724         el.innerHTML = this.applyTemplate(values);
5725         return returnElement ? Roo.get(el.firstChild, true) : el.firstChild;
5726     }
5727 };
5728 /**
5729  * Alias for {@link #applyTemplate}
5730  * @method
5731  */
5732 Roo.Template.prototype.apply = Roo.Template.prototype.applyTemplate;
5733
5734 // backwards compat
5735 Roo.DomHelper.Template = Roo.Template;
5736
5737 /**
5738  * Creates a template from the passed element's value (<i>display:none</i> textarea, preferred) or innerHTML.
5739  * @param {String/HTMLElement} el A DOM element or its id
5740  * @returns {Roo.Template} The created template
5741  * @static
5742  */
5743 Roo.Template.from = function(el){
5744     el = Roo.getDom(el);
5745     return new Roo.Template(el.value || el.innerHTML);
5746 };/*
5747  * Based on:
5748  * Ext JS Library 1.1.1
5749  * Copyright(c) 2006-2007, Ext JS, LLC.
5750  *
5751  * Originally Released Under LGPL - original licence link has changed is not relivant.
5752  *
5753  * Fork - LGPL
5754  * <script type="text/javascript">
5755  */
5756  
5757
5758 /*
5759  * This is code is also distributed under MIT license for use
5760  * with jQuery and prototype JavaScript libraries.
5761  */
5762 /**
5763  * @class Roo.DomQuery
5764 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).
5765 <p>
5766 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>
5767
5768 <p>
5769 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.
5770 </p>
5771 <h4>Element Selectors:</h4>
5772 <ul class="list">
5773     <li> <b>*</b> any element</li>
5774     <li> <b>E</b> an element with the tag E</li>
5775     <li> <b>E F</b> All descendent elements of E that have the tag F</li>
5776     <li> <b>E > F</b> or <b>E/F</b> all direct children elements of E that have the tag F</li>
5777     <li> <b>E + F</b> all elements with the tag F that are immediately preceded by an element with the tag E</li>
5778     <li> <b>E ~ F</b> all elements with the tag F that are preceded by a sibling element with the tag E</li>
5779 </ul>
5780 <h4>Attribute Selectors:</h4>
5781 <p>The use of @ and quotes are optional. For example, div[@foo='bar'] is also a valid attribute selector.</p>
5782 <ul class="list">
5783     <li> <b>E[foo]</b> has an attribute "foo"</li>
5784     <li> <b>E[foo=bar]</b> has an attribute "foo" that equals "bar"</li>
5785     <li> <b>E[foo^=bar]</b> has an attribute "foo" that starts with "bar"</li>
5786     <li> <b>E[foo$=bar]</b> has an attribute "foo" that ends with "bar"</li>
5787     <li> <b>E[foo*=bar]</b> has an attribute "foo" that contains the substring "bar"</li>
5788     <li> <b>E[foo%=2]</b> has an attribute "foo" that is evenly divisible by 2</li>
5789     <li> <b>E[foo!=bar]</b> has an attribute "foo" that does not equal "bar"</li>
5790 </ul>
5791 <h4>Pseudo Classes:</h4>
5792 <ul class="list">
5793     <li> <b>E:first-child</b> E is the first child of its parent</li>
5794     <li> <b>E:last-child</b> E is the last child of its parent</li>
5795     <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>
5796     <li> <b>E:nth-child(odd)</b> E is an odd child of its parent</li>
5797     <li> <b>E:nth-child(even)</b> E is an even child of its parent</li>
5798     <li> <b>E:only-child</b> E is the only child of its parent</li>
5799     <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>
5800     <li> <b>E:first</b> the first E in the resultset</li>
5801     <li> <b>E:last</b> the last E in the resultset</li>
5802     <li> <b>E:nth(<i>n</i>)</b> the <i>n</i>th E in the resultset (1 based)</li>
5803     <li> <b>E:odd</b> shortcut for :nth-child(odd)</li>
5804     <li> <b>E:even</b> shortcut for :nth-child(even)</li>
5805     <li> <b>E:contains(foo)</b> E's innerHTML contains the substring "foo"</li>
5806     <li> <b>E:nodeValue(foo)</b> E contains a textNode with a nodeValue that equals "foo"</li>
5807     <li> <b>E:not(S)</b> an E element that does not match simple selector S</li>
5808     <li> <b>E:has(S)</b> an E element that has a descendent that matches simple selector S</li>
5809     <li> <b>E:next(S)</b> an E element whose next sibling matches simple selector S</li>
5810     <li> <b>E:prev(S)</b> an E element whose previous sibling matches simple selector S</li>
5811 </ul>
5812 <h4>CSS Value Selectors:</h4>
5813 <ul class="list">
5814     <li> <b>E{display=none}</b> css value "display" that equals "none"</li>
5815     <li> <b>E{display^=none}</b> css value "display" that starts with "none"</li>
5816     <li> <b>E{display$=none}</b> css value "display" that ends with "none"</li>
5817     <li> <b>E{display*=none}</b> css value "display" that contains the substring "none"</li>
5818     <li> <b>E{display%=2}</b> css value "display" that is evenly divisible by 2</li>
5819     <li> <b>E{display!=none}</b> css value "display" that does not equal "none"</li>
5820 </ul>
5821  * @static
5822  */
5823 Roo.DomQuery = function(){
5824     var cache = {}, simpleCache = {}, valueCache = {};
5825     var nonSpace = /\S/;
5826     var trimRe = /^\s+|\s+$/g;
5827     var tplRe = /\{(\d+)\}/g;
5828     var modeRe = /^(\s?[\/>+~]\s?|\s|$)/;
5829     var tagTokenRe = /^(#)?([\w-\*]+)/;
5830     var nthRe = /(\d*)n\+?(\d*)/, nthRe2 = /\D/;
5831
5832     function child(p, index){
5833         var i = 0;
5834         var n = p.firstChild;
5835         while(n){
5836             if(n.nodeType == 1){
5837                if(++i == index){
5838                    return n;
5839                }
5840             }
5841             n = n.nextSibling;
5842         }
5843         return null;
5844     };
5845
5846     function next(n){
5847         while((n = n.nextSibling) && n.nodeType != 1);
5848         return n;
5849     };
5850
5851     function prev(n){
5852         while((n = n.previousSibling) && n.nodeType != 1);
5853         return n;
5854     };
5855
5856     function children(d){
5857         var n = d.firstChild, ni = -1;
5858             while(n){
5859                 var nx = n.nextSibling;
5860                 if(n.nodeType == 3 && !nonSpace.test(n.nodeValue)){
5861                     d.removeChild(n);
5862                 }else{
5863                     n.nodeIndex = ++ni;
5864                 }
5865                 n = nx;
5866             }
5867             return this;
5868         };
5869
5870     function byClassName(c, a, v){
5871         if(!v){
5872             return c;
5873         }
5874         var r = [], ri = -1, cn;
5875         for(var i = 0, ci; ci = c[i]; i++){
5876             
5877             
5878             if((' '+
5879                 ( (ci instanceof SVGElement) ? ci.className.baseVal : ci.className)
5880                  +' ').indexOf(v) != -1){
5881                 r[++ri] = ci;
5882             }
5883         }
5884         return r;
5885     };
5886
5887     function attrValue(n, attr){
5888         if(!n.tagName && typeof n.length != "undefined"){
5889             n = n[0];
5890         }
5891         if(!n){
5892             return null;
5893         }
5894         if(attr == "for"){
5895             return n.htmlFor;
5896         }
5897         if(attr == "class" || attr == "className"){
5898             return (n instanceof SVGElement) ? n.className.baseVal : n.className;
5899         }
5900         return n.getAttribute(attr) || n[attr];
5901
5902     };
5903
5904     function getNodes(ns, mode, tagName){
5905         var result = [], ri = -1, cs;
5906         if(!ns){
5907             return result;
5908         }
5909         tagName = tagName || "*";
5910         if(typeof ns.getElementsByTagName != "undefined"){
5911             ns = [ns];
5912         }
5913         if(!mode){
5914             for(var i = 0, ni; ni = ns[i]; i++){
5915                 cs = ni.getElementsByTagName(tagName);
5916                 for(var j = 0, ci; ci = cs[j]; j++){
5917                     result[++ri] = ci;
5918                 }
5919             }
5920         }else if(mode == "/" || mode == ">"){
5921             var utag = tagName.toUpperCase();
5922             for(var i = 0, ni, cn; ni = ns[i]; i++){
5923                 cn = ni.children || ni.childNodes;
5924                 for(var j = 0, cj; cj = cn[j]; j++){
5925                     if(cj.nodeName == utag || cj.nodeName == tagName  || tagName == '*'){
5926                         result[++ri] = cj;
5927                     }
5928                 }
5929             }
5930         }else if(mode == "+"){
5931             var utag = tagName.toUpperCase();
5932             for(var i = 0, n; n = ns[i]; i++){
5933                 while((n = n.nextSibling) && n.nodeType != 1);
5934                 if(n && (n.nodeName == utag || n.nodeName == tagName || tagName == '*')){
5935                     result[++ri] = n;
5936                 }
5937             }
5938         }else if(mode == "~"){
5939             for(var i = 0, n; n = ns[i]; i++){
5940                 while((n = n.nextSibling) && (n.nodeType != 1 || (tagName == '*' || n.tagName.toLowerCase()!=tagName)));
5941                 if(n){
5942                     result[++ri] = n;
5943                 }
5944             }
5945         }
5946         return result;
5947     };
5948
5949     function concat(a, b){
5950         if(b.slice){
5951             return a.concat(b);
5952         }
5953         for(var i = 0, l = b.length; i < l; i++){
5954             a[a.length] = b[i];
5955         }
5956         return a;
5957     }
5958
5959     function byTag(cs, tagName){
5960         if(cs.tagName || cs == document){
5961             cs = [cs];
5962         }
5963         if(!tagName){
5964             return cs;
5965         }
5966         var r = [], ri = -1;
5967         tagName = tagName.toLowerCase();
5968         for(var i = 0, ci; ci = cs[i]; i++){
5969             if(ci.nodeType == 1 && ci.tagName.toLowerCase()==tagName){
5970                 r[++ri] = ci;
5971             }
5972         }
5973         return r;
5974     };
5975
5976     function byId(cs, attr, id){
5977         if(cs.tagName || cs == document){
5978             cs = [cs];
5979         }
5980         if(!id){
5981             return cs;
5982         }
5983         var r = [], ri = -1;
5984         for(var i = 0,ci; ci = cs[i]; i++){
5985             if(ci && ci.id == id){
5986                 r[++ri] = ci;
5987                 return r;
5988             }
5989         }
5990         return r;
5991     };
5992
5993     function byAttribute(cs, attr, value, op, custom){
5994         var r = [], ri = -1, st = custom=="{";
5995         var f = Roo.DomQuery.operators[op];
5996         for(var i = 0, ci; ci = cs[i]; i++){
5997             var a;
5998             if(st){
5999                 a = Roo.DomQuery.getStyle(ci, attr);
6000             }
6001             else if(attr == "class" || attr == "className"){
6002                 a = (ci instanceof SVGElement) ? ci.className.baseVal : ci.className;
6003             }else if(attr == "for"){
6004                 a = ci.htmlFor;
6005             }else if(attr == "href"){
6006                 a = ci.getAttribute("href", 2);
6007             }else{
6008                 a = ci.getAttribute(attr);
6009             }
6010             if((f && f(a, value)) || (!f && a)){
6011                 r[++ri] = ci;
6012             }
6013         }
6014         return r;
6015     };
6016
6017     function byPseudo(cs, name, value){
6018         return Roo.DomQuery.pseudos[name](cs, value);
6019     };
6020
6021     // This is for IE MSXML which does not support expandos.
6022     // IE runs the same speed using setAttribute, however FF slows way down
6023     // and Safari completely fails so they need to continue to use expandos.
6024     var isIE = window.ActiveXObject ? true : false;
6025
6026     // this eval is stop the compressor from
6027     // renaming the variable to something shorter
6028     
6029     /** eval:var:batch */
6030     var batch = 30803; 
6031
6032     var key = 30803;
6033
6034     function nodupIEXml(cs){
6035         var d = ++key;
6036         cs[0].setAttribute("_nodup", d);
6037         var r = [cs[0]];
6038         for(var i = 1, len = cs.length; i < len; i++){
6039             var c = cs[i];
6040             if(!c.getAttribute("_nodup") != d){
6041                 c.setAttribute("_nodup", d);
6042                 r[r.length] = c;
6043             }
6044         }
6045         for(var i = 0, len = cs.length; i < len; i++){
6046             cs[i].removeAttribute("_nodup");
6047         }
6048         return r;
6049     }
6050
6051     function nodup(cs){
6052         if(!cs){
6053             return [];
6054         }
6055         var len = cs.length, c, i, r = cs, cj, ri = -1;
6056         if(!len || typeof cs.nodeType != "undefined" || len == 1){
6057             return cs;
6058         }
6059         if(isIE && typeof cs[0].selectSingleNode != "undefined"){
6060             return nodupIEXml(cs);
6061         }
6062         var d = ++key;
6063         cs[0]._nodup = d;
6064         for(i = 1; c = cs[i]; i++){
6065             if(c._nodup != d){
6066                 c._nodup = d;
6067             }else{
6068                 r = [];
6069                 for(var j = 0; j < i; j++){
6070                     r[++ri] = cs[j];
6071                 }
6072                 for(j = i+1; cj = cs[j]; j++){
6073                     if(cj._nodup != d){
6074                         cj._nodup = d;
6075                         r[++ri] = cj;
6076                     }
6077                 }
6078                 return r;
6079             }
6080         }
6081         return r;
6082     }
6083
6084     function quickDiffIEXml(c1, c2){
6085         var d = ++key;
6086         for(var i = 0, len = c1.length; i < len; i++){
6087             c1[i].setAttribute("_qdiff", d);
6088         }
6089         var r = [];
6090         for(var i = 0, len = c2.length; i < len; i++){
6091             if(c2[i].getAttribute("_qdiff") != d){
6092                 r[r.length] = c2[i];
6093             }
6094         }
6095         for(var i = 0, len = c1.length; i < len; i++){
6096            c1[i].removeAttribute("_qdiff");
6097         }
6098         return r;
6099     }
6100
6101     function quickDiff(c1, c2){
6102         var len1 = c1.length;
6103         if(!len1){
6104             return c2;
6105         }
6106         if(isIE && c1[0].selectSingleNode){
6107             return quickDiffIEXml(c1, c2);
6108         }
6109         var d = ++key;
6110         for(var i = 0; i < len1; i++){
6111             c1[i]._qdiff = d;
6112         }
6113         var r = [];
6114         for(var i = 0, len = c2.length; i < len; i++){
6115             if(c2[i]._qdiff != d){
6116                 r[r.length] = c2[i];
6117             }
6118         }
6119         return r;
6120     }
6121
6122     function quickId(ns, mode, root, id){
6123         if(ns == root){
6124            var d = root.ownerDocument || root;
6125            return d.getElementById(id);
6126         }
6127         ns = getNodes(ns, mode, "*");
6128         return byId(ns, null, id);
6129     }
6130
6131     return {
6132         getStyle : function(el, name){
6133             return Roo.fly(el).getStyle(name);
6134         },
6135         /**
6136          * Compiles a selector/xpath query into a reusable function. The returned function
6137          * takes one parameter "root" (optional), which is the context node from where the query should start.
6138          * @param {String} selector The selector/xpath query
6139          * @param {String} type (optional) Either "select" (the default) or "simple" for a simple selector match
6140          * @return {Function}
6141          */
6142         compile : function(path, type){
6143             type = type || "select";
6144             
6145             var fn = ["var f = function(root){\n var mode; ++batch; var n = root || document;\n"];
6146             var q = path, mode, lq;
6147             var tk = Roo.DomQuery.matchers;
6148             var tklen = tk.length;
6149             var mm;
6150
6151             // accept leading mode switch
6152             var lmode = q.match(modeRe);
6153             if(lmode && lmode[1]){
6154                 fn[fn.length] = 'mode="'+lmode[1].replace(trimRe, "")+'";';
6155                 q = q.replace(lmode[1], "");
6156             }
6157             // strip leading slashes
6158             while(path.substr(0, 1)=="/"){
6159                 path = path.substr(1);
6160             }
6161
6162             while(q && lq != q){
6163                 lq = q;
6164                 var tm = q.match(tagTokenRe);
6165                 if(type == "select"){
6166                     if(tm){
6167                         if(tm[1] == "#"){
6168                             fn[fn.length] = 'n = quickId(n, mode, root, "'+tm[2]+'");';
6169                         }else{
6170                             fn[fn.length] = 'n = getNodes(n, mode, "'+tm[2]+'");';
6171                         }
6172                         q = q.replace(tm[0], "");
6173                     }else if(q.substr(0, 1) != '@'){
6174                         fn[fn.length] = 'n = getNodes(n, mode, "*");';
6175                     }
6176                 }else{
6177                     if(tm){
6178                         if(tm[1] == "#"){
6179                             fn[fn.length] = 'n = byId(n, null, "'+tm[2]+'");';
6180                         }else{
6181                             fn[fn.length] = 'n = byTag(n, "'+tm[2]+'");';
6182                         }
6183                         q = q.replace(tm[0], "");
6184                     }
6185                 }
6186                 while(!(mm = q.match(modeRe))){
6187                     var matched = false;
6188                     for(var j = 0; j < tklen; j++){
6189                         var t = tk[j];
6190                         var m = q.match(t.re);
6191                         if(m){
6192                             fn[fn.length] = t.select.replace(tplRe, function(x, i){
6193                                                     return m[i];
6194                                                 });
6195                             q = q.replace(m[0], "");
6196                             matched = true;
6197                             break;
6198                         }
6199                     }
6200                     // prevent infinite loop on bad selector
6201                     if(!matched){
6202                         throw 'Error parsing selector, parsing failed at "' + q + '"';
6203                     }
6204                 }
6205                 if(mm[1]){
6206                     fn[fn.length] = 'mode="'+mm[1].replace(trimRe, "")+'";';
6207                     q = q.replace(mm[1], "");
6208                 }
6209             }
6210             fn[fn.length] = "return nodup(n);\n}";
6211             
6212              /** 
6213               * list of variables that need from compression as they are used by eval.
6214              *  eval:var:batch 
6215              *  eval:var:nodup
6216              *  eval:var:byTag
6217              *  eval:var:ById
6218              *  eval:var:getNodes
6219              *  eval:var:quickId
6220              *  eval:var:mode
6221              *  eval:var:root
6222              *  eval:var:n
6223              *  eval:var:byClassName
6224              *  eval:var:byPseudo
6225              *  eval:var:byAttribute
6226              *  eval:var:attrValue
6227              * 
6228              **/ 
6229             eval(fn.join(""));
6230             return f;
6231         },
6232
6233         /**
6234          * Selects a group of elements.
6235          * @param {String} selector The selector/xpath query (can be a comma separated list of selectors)
6236          * @param {Node} root (optional) The start of the query (defaults to document).
6237          * @return {Array}
6238          */
6239         select : function(path, root, type){
6240             if(!root || root == document){
6241                 root = document;
6242             }
6243             if(typeof root == "string"){
6244                 root = document.getElementById(root);
6245             }
6246             var paths = path.split(",");
6247             var results = [];
6248             for(var i = 0, len = paths.length; i < len; i++){
6249                 var p = paths[i].replace(trimRe, "");
6250                 if(!cache[p]){
6251                     cache[p] = Roo.DomQuery.compile(p);
6252                     if(!cache[p]){
6253                         throw p + " is not a valid selector";
6254                     }
6255                 }
6256                 var result = cache[p](root);
6257                 if(result && result != document){
6258                     results = results.concat(result);
6259                 }
6260             }
6261             if(paths.length > 1){
6262                 return nodup(results);
6263             }
6264             return results;
6265         },
6266
6267         /**
6268          * Selects a single element.
6269          * @param {String} selector The selector/xpath query
6270          * @param {Node} root (optional) The start of the query (defaults to document).
6271          * @return {Element}
6272          */
6273         selectNode : function(path, root){
6274             return Roo.DomQuery.select(path, root)[0];
6275         },
6276
6277         /**
6278          * Selects the value of a node, optionally replacing null with the defaultValue.
6279          * @param {String} selector The selector/xpath query
6280          * @param {Node} root (optional) The start of the query (defaults to document).
6281          * @param {String} defaultValue
6282          */
6283         selectValue : function(path, root, defaultValue){
6284             path = path.replace(trimRe, "");
6285             if(!valueCache[path]){
6286                 valueCache[path] = Roo.DomQuery.compile(path, "select");
6287             }
6288             var n = valueCache[path](root);
6289             n = n[0] ? n[0] : n;
6290             var v = (n && n.firstChild ? n.firstChild.nodeValue : null);
6291             return ((v === null||v === undefined||v==='') ? defaultValue : v);
6292         },
6293
6294         /**
6295          * Selects the value of a node, parsing integers and floats.
6296          * @param {String} selector The selector/xpath query
6297          * @param {Node} root (optional) The start of the query (defaults to document).
6298          * @param {Number} defaultValue
6299          * @return {Number}
6300          */
6301         selectNumber : function(path, root, defaultValue){
6302             var v = Roo.DomQuery.selectValue(path, root, defaultValue || 0);
6303             return parseFloat(v);
6304         },
6305
6306         /**
6307          * Returns true if the passed element(s) match the passed simple selector (e.g. div.some-class or span:first-child)
6308          * @param {String/HTMLElement/Array} el An element id, element or array of elements
6309          * @param {String} selector The simple selector to test
6310          * @return {Boolean}
6311          */
6312         is : function(el, ss){
6313             if(typeof el == "string"){
6314                 el = document.getElementById(el);
6315             }
6316             var isArray = (el instanceof Array);
6317             var result = Roo.DomQuery.filter(isArray ? el : [el], ss);
6318             return isArray ? (result.length == el.length) : (result.length > 0);
6319         },
6320
6321         /**
6322          * Filters an array of elements to only include matches of a simple selector (e.g. div.some-class or span:first-child)
6323          * @param {Array} el An array of elements to filter
6324          * @param {String} selector The simple selector to test
6325          * @param {Boolean} nonMatches If true, it returns the elements that DON'T match
6326          * the selector instead of the ones that match
6327          * @return {Array}
6328          */
6329         filter : function(els, ss, nonMatches){
6330             ss = ss.replace(trimRe, "");
6331             if(!simpleCache[ss]){
6332                 simpleCache[ss] = Roo.DomQuery.compile(ss, "simple");
6333             }
6334             var result = simpleCache[ss](els);
6335             return nonMatches ? quickDiff(result, els) : result;
6336         },
6337
6338         /**
6339          * Collection of matching regular expressions and code snippets.
6340          */
6341         matchers : [{
6342                 re: /^\.([\w-]+)/,
6343                 select: 'n = byClassName(n, null, " {1} ");'
6344             }, {
6345                 re: /^\:([\w-]+)(?:\(((?:[^\s>\/]*|.*?))\))?/,
6346                 select: 'n = byPseudo(n, "{1}", "{2}");'
6347             },{
6348                 re: /^(?:([\[\{])(?:@)?([\w-]+)\s?(?:(=|.=)\s?['"]?(.*?)["']?)?[\]\}])/,
6349                 select: 'n = byAttribute(n, "{2}", "{4}", "{3}", "{1}");'
6350             }, {
6351                 re: /^#([\w-]+)/,
6352                 select: 'n = byId(n, null, "{1}");'
6353             },{
6354                 re: /^@([\w-]+)/,
6355                 select: 'return {firstChild:{nodeValue:attrValue(n, "{1}")}};'
6356             }
6357         ],
6358
6359         /**
6360          * Collection of operator comparison functions. The default operators are =, !=, ^=, $=, *=, %=, |= and ~=.
6361          * 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;.
6362          */
6363         operators : {
6364             "=" : function(a, v){
6365                 return a == v;
6366             },
6367             "!=" : function(a, v){
6368                 return a != v;
6369             },
6370             "^=" : function(a, v){
6371                 return a && a.substr(0, v.length) == v;
6372             },
6373             "$=" : function(a, v){
6374                 return a && a.substr(a.length-v.length) == v;
6375             },
6376             "*=" : function(a, v){
6377                 return a && a.indexOf(v) !== -1;
6378             },
6379             "%=" : function(a, v){
6380                 return (a % v) == 0;
6381             },
6382             "|=" : function(a, v){
6383                 return a && (a == v || a.substr(0, v.length+1) == v+'-');
6384             },
6385             "~=" : function(a, v){
6386                 return a && (' '+a+' ').indexOf(' '+v+' ') != -1;
6387             }
6388         },
6389
6390         /**
6391          * Collection of "pseudo class" processors. Each processor is passed the current nodeset (array)
6392          * and the argument (if any) supplied in the selector.
6393          */
6394         pseudos : {
6395             "first-child" : function(c){
6396                 var r = [], ri = -1, n;
6397                 for(var i = 0, ci; ci = n = c[i]; i++){
6398                     while((n = n.previousSibling) && n.nodeType != 1);
6399                     if(!n){
6400                         r[++ri] = ci;
6401                     }
6402                 }
6403                 return r;
6404             },
6405
6406             "last-child" : function(c){
6407                 var r = [], ri = -1, n;
6408                 for(var i = 0, ci; ci = n = c[i]; i++){
6409                     while((n = n.nextSibling) && n.nodeType != 1);
6410                     if(!n){
6411                         r[++ri] = ci;
6412                     }
6413                 }
6414                 return r;
6415             },
6416
6417             "nth-child" : function(c, a) {
6418                 var r = [], ri = -1;
6419                 var m = nthRe.exec(a == "even" && "2n" || a == "odd" && "2n+1" || !nthRe2.test(a) && "n+" + a || a);
6420                 var f = (m[1] || 1) - 0, l = m[2] - 0;
6421                 for(var i = 0, n; n = c[i]; i++){
6422                     var pn = n.parentNode;
6423                     if (batch != pn._batch) {
6424                         var j = 0;
6425                         for(var cn = pn.firstChild; cn; cn = cn.nextSibling){
6426                             if(cn.nodeType == 1){
6427                                cn.nodeIndex = ++j;
6428                             }
6429                         }
6430                         pn._batch = batch;
6431                     }
6432                     if (f == 1) {
6433                         if (l == 0 || n.nodeIndex == l){
6434                             r[++ri] = n;
6435                         }
6436                     } else if ((n.nodeIndex + l) % f == 0){
6437                         r[++ri] = n;
6438                     }
6439                 }
6440
6441                 return r;
6442             },
6443
6444             "only-child" : function(c){
6445                 var r = [], ri = -1;;
6446                 for(var i = 0, ci; ci = c[i]; i++){
6447                     if(!prev(ci) && !next(ci)){
6448                         r[++ri] = ci;
6449                     }
6450                 }
6451                 return r;
6452             },
6453
6454             "empty" : function(c){
6455                 var r = [], ri = -1;
6456                 for(var i = 0, ci; ci = c[i]; i++){
6457                     var cns = ci.childNodes, j = 0, cn, empty = true;
6458                     while(cn = cns[j]){
6459                         ++j;
6460                         if(cn.nodeType == 1 || cn.nodeType == 3){
6461                             empty = false;
6462                             break;
6463                         }
6464                     }
6465                     if(empty){
6466                         r[++ri] = ci;
6467                     }
6468                 }
6469                 return r;
6470             },
6471
6472             "contains" : function(c, v){
6473                 var r = [], ri = -1;
6474                 for(var i = 0, ci; ci = c[i]; i++){
6475                     if((ci.textContent||ci.innerText||'').indexOf(v) != -1){
6476                         r[++ri] = ci;
6477                     }
6478                 }
6479                 return r;
6480             },
6481
6482             "nodeValue" : function(c, v){
6483                 var r = [], ri = -1;
6484                 for(var i = 0, ci; ci = c[i]; i++){
6485                     if(ci.firstChild && ci.firstChild.nodeValue == v){
6486                         r[++ri] = ci;
6487                     }
6488                 }
6489                 return r;
6490             },
6491
6492             "checked" : function(c){
6493                 var r = [], ri = -1;
6494                 for(var i = 0, ci; ci = c[i]; i++){
6495                     if(ci.checked == true){
6496                         r[++ri] = ci;
6497                     }
6498                 }
6499                 return r;
6500             },
6501
6502             "not" : function(c, ss){
6503                 return Roo.DomQuery.filter(c, ss, true);
6504             },
6505
6506             "odd" : function(c){
6507                 return this["nth-child"](c, "odd");
6508             },
6509
6510             "even" : function(c){
6511                 return this["nth-child"](c, "even");
6512             },
6513
6514             "nth" : function(c, a){
6515                 return c[a-1] || [];
6516             },
6517
6518             "first" : function(c){
6519                 return c[0] || [];
6520             },
6521
6522             "last" : function(c){
6523                 return c[c.length-1] || [];
6524             },
6525
6526             "has" : function(c, ss){
6527                 var s = Roo.DomQuery.select;
6528                 var r = [], ri = -1;
6529                 for(var i = 0, ci; ci = c[i]; i++){
6530                     if(s(ss, ci).length > 0){
6531                         r[++ri] = ci;
6532                     }
6533                 }
6534                 return r;
6535             },
6536
6537             "next" : function(c, ss){
6538                 var is = Roo.DomQuery.is;
6539                 var r = [], ri = -1;
6540                 for(var i = 0, ci; ci = c[i]; i++){
6541                     var n = next(ci);
6542                     if(n && is(n, ss)){
6543                         r[++ri] = ci;
6544                     }
6545                 }
6546                 return r;
6547             },
6548
6549             "prev" : function(c, ss){
6550                 var is = Roo.DomQuery.is;
6551                 var r = [], ri = -1;
6552                 for(var i = 0, ci; ci = c[i]; i++){
6553                     var n = prev(ci);
6554                     if(n && is(n, ss)){
6555                         r[++ri] = ci;
6556                     }
6557                 }
6558                 return r;
6559             }
6560         }
6561     };
6562 }();
6563
6564 /**
6565  * Selects an array of DOM nodes by CSS/XPath selector. Shorthand of {@link Roo.DomQuery#select}
6566  * @param {String} path The selector/xpath query
6567  * @param {Node} root (optional) The start of the query (defaults to document).
6568  * @return {Array}
6569  * @member Roo
6570  * @method query
6571  */
6572 Roo.query = Roo.DomQuery.select;
6573 /*
6574  * Based on:
6575  * Ext JS Library 1.1.1
6576  * Copyright(c) 2006-2007, Ext JS, LLC.
6577  *
6578  * Originally Released Under LGPL - original licence link has changed is not relivant.
6579  *
6580  * Fork - LGPL
6581  * <script type="text/javascript">
6582  */
6583
6584 /**
6585  * @class Roo.util.Observable
6586  * Base class that provides a common interface for publishing events. Subclasses are expected to
6587  * to have a property "events" with all the events defined.<br>
6588  * For example:
6589  * <pre><code>
6590  Employee = function(name){
6591     this.name = name;
6592     this.addEvents({
6593         "fired" : true,
6594         "quit" : true
6595     });
6596  }
6597  Roo.extend(Employee, Roo.util.Observable);
6598 </code></pre>
6599  * @param {Object} config properties to use (incuding events / listeners)
6600  */
6601
6602 Roo.util.Observable = function(cfg){
6603     
6604     cfg = cfg|| {};
6605     this.addEvents(cfg.events || {});
6606     if (cfg.events) {
6607         delete cfg.events; // make sure
6608     }
6609      
6610     Roo.apply(this, cfg);
6611     
6612     if(this.listeners){
6613         this.on(this.listeners);
6614         delete this.listeners;
6615     }
6616 };
6617 Roo.util.Observable.prototype = {
6618     /** 
6619  * @cfg {Object} listeners  list of events and functions to call for this object, 
6620  * For example :
6621  * <pre><code>
6622     listeners :  { 
6623        'click' : function(e) {
6624            ..... 
6625         } ,
6626         .... 
6627     } 
6628   </code></pre>
6629  */
6630     
6631     
6632     /**
6633      * Fires the specified event with the passed parameters (minus the event name).
6634      * @param {String} eventName
6635      * @param {Object...} args Variable number of parameters are passed to handlers
6636      * @return {Boolean} returns false if any of the handlers return false otherwise it returns true
6637      */
6638     fireEvent : function(){
6639         var ce = this.events[arguments[0].toLowerCase()];
6640         if(typeof ce == "object"){
6641             return ce.fire.apply(ce, Array.prototype.slice.call(arguments, 1));
6642         }else{
6643             return true;
6644         }
6645     },
6646
6647     // private
6648     filterOptRe : /^(?:scope|delay|buffer|single)$/,
6649
6650     /**
6651      * Appends an event handler to this component
6652      * @param {String}   eventName The type of event to listen for
6653      * @param {Function} handler The method the event invokes
6654      * @param {Object}   scope (optional) The scope in which to execute the handler
6655      * function. The handler function's "this" context.
6656      * @param {Object}   options (optional) An object containing handler configuration
6657      * properties. This may contain any of the following properties:<ul>
6658      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
6659      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
6660      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
6661      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
6662      * by the specified number of milliseconds. If the event fires again within that time, the original
6663      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
6664      * </ul><br>
6665      * <p>
6666      * <b>Combining Options</b><br>
6667      * Using the options argument, it is possible to combine different types of listeners:<br>
6668      * <br>
6669      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)
6670                 <pre><code>
6671                 el.on('click', this.onClick, this, {
6672                         single: true,
6673                 delay: 100,
6674                 forumId: 4
6675                 });
6676                 </code></pre>
6677      * <p>
6678      * <b>Attaching multiple handlers in 1 call</b><br>
6679      * The method also allows for a single argument to be passed which is a config object containing properties
6680      * which specify multiple handlers.
6681      * <pre><code>
6682                 el.on({
6683                         'click': {
6684                         fn: this.onClick,
6685                         scope: this,
6686                         delay: 100
6687                 }, 
6688                 'mouseover': {
6689                         fn: this.onMouseOver,
6690                         scope: this
6691                 },
6692                 'mouseout': {
6693                         fn: this.onMouseOut,
6694                         scope: this
6695                 }
6696                 });
6697                 </code></pre>
6698      * <p>
6699      * Or a shorthand syntax which passes the same scope object to all handlers:
6700         <pre><code>
6701                 el.on({
6702                         'click': this.onClick,
6703                 'mouseover': this.onMouseOver,
6704                 'mouseout': this.onMouseOut,
6705                 scope: this
6706                 });
6707                 </code></pre>
6708      */
6709     addListener : function(eventName, fn, scope, o){
6710         if(typeof eventName == "object"){
6711             o = eventName;
6712             for(var e in o){
6713                 if(this.filterOptRe.test(e)){
6714                     continue;
6715                 }
6716                 if(typeof o[e] == "function"){
6717                     // shared options
6718                     this.addListener(e, o[e], o.scope,  o);
6719                 }else{
6720                     // individual options
6721                     this.addListener(e, o[e].fn, o[e].scope, o[e]);
6722                 }
6723             }
6724             return;
6725         }
6726         o = (!o || typeof o == "boolean") ? {} : o;
6727         eventName = eventName.toLowerCase();
6728         var ce = this.events[eventName] || true;
6729         if(typeof ce == "boolean"){
6730             ce = new Roo.util.Event(this, eventName);
6731             this.events[eventName] = ce;
6732         }
6733         ce.addListener(fn, scope, o);
6734     },
6735
6736     /**
6737      * Removes a listener
6738      * @param {String}   eventName     The type of event to listen for
6739      * @param {Function} handler        The handler to remove
6740      * @param {Object}   scope  (optional) The scope (this object) for the handler
6741      */
6742     removeListener : function(eventName, fn, scope){
6743         var ce = this.events[eventName.toLowerCase()];
6744         if(typeof ce == "object"){
6745             ce.removeListener(fn, scope);
6746         }
6747     },
6748
6749     /**
6750      * Removes all listeners for this object
6751      */
6752     purgeListeners : function(){
6753         for(var evt in this.events){
6754             if(typeof this.events[evt] == "object"){
6755                  this.events[evt].clearListeners();
6756             }
6757         }
6758     },
6759
6760     relayEvents : function(o, events){
6761         var createHandler = function(ename){
6762             return function(){
6763                  
6764                 return this.fireEvent.apply(this, Roo.combine(ename, Array.prototype.slice.call(arguments, 0)));
6765             };
6766         };
6767         for(var i = 0, len = events.length; i < len; i++){
6768             var ename = events[i];
6769             if(!this.events[ename]){
6770                 this.events[ename] = true;
6771             };
6772             o.on(ename, createHandler(ename), this);
6773         }
6774     },
6775
6776     /**
6777      * Used to define events on this Observable
6778      * @param {Object} object The object with the events defined
6779      */
6780     addEvents : function(o){
6781         if(!this.events){
6782             this.events = {};
6783         }
6784         Roo.applyIf(this.events, o);
6785     },
6786
6787     /**
6788      * Checks to see if this object has any listeners for a specified event
6789      * @param {String} eventName The name of the event to check for
6790      * @return {Boolean} True if the event is being listened for, else false
6791      */
6792     hasListener : function(eventName){
6793         var e = this.events[eventName];
6794         return typeof e == "object" && e.listeners.length > 0;
6795     }
6796 };
6797 /**
6798  * Appends an event handler to this element (shorthand for addListener)
6799  * @param {String}   eventName     The type of event to listen for
6800  * @param {Function} handler        The method the event invokes
6801  * @param {Object}   scope (optional) The scope in which to execute the handler
6802  * function. The handler function's "this" context.
6803  * @param {Object}   options  (optional)
6804  * @method
6805  */
6806 Roo.util.Observable.prototype.on = Roo.util.Observable.prototype.addListener;
6807 /**
6808  * Removes a listener (shorthand for removeListener)
6809  * @param {String}   eventName     The type of event to listen for
6810  * @param {Function} handler        The handler to remove
6811  * @param {Object}   scope  (optional) The scope (this object) for the handler
6812  * @method
6813  */
6814 Roo.util.Observable.prototype.un = Roo.util.Observable.prototype.removeListener;
6815
6816 /**
6817  * Starts capture on the specified Observable. All events will be passed
6818  * to the supplied function with the event name + standard signature of the event
6819  * <b>before</b> the event is fired. If the supplied function returns false,
6820  * the event will not fire.
6821  * @param {Observable} o The Observable to capture
6822  * @param {Function} fn The function to call
6823  * @param {Object} scope (optional) The scope (this object) for the fn
6824  * @static
6825  */
6826 Roo.util.Observable.capture = function(o, fn, scope){
6827     o.fireEvent = o.fireEvent.createInterceptor(fn, scope);
6828 };
6829
6830 /**
6831  * Removes <b>all</b> added captures from the Observable.
6832  * @param {Observable} o The Observable to release
6833  * @static
6834  */
6835 Roo.util.Observable.releaseCapture = function(o){
6836     o.fireEvent = Roo.util.Observable.prototype.fireEvent;
6837 };
6838
6839 (function(){
6840
6841     var createBuffered = function(h, o, scope){
6842         var task = new Roo.util.DelayedTask();
6843         return function(){
6844             task.delay(o.buffer, h, scope, Array.prototype.slice.call(arguments, 0));
6845         };
6846     };
6847
6848     var createSingle = function(h, e, fn, scope){
6849         return function(){
6850             e.removeListener(fn, scope);
6851             return h.apply(scope, arguments);
6852         };
6853     };
6854
6855     var createDelayed = function(h, o, scope){
6856         return function(){
6857             var args = Array.prototype.slice.call(arguments, 0);
6858             setTimeout(function(){
6859                 h.apply(scope, args);
6860             }, o.delay || 10);
6861         };
6862     };
6863
6864     Roo.util.Event = function(obj, name){
6865         this.name = name;
6866         this.obj = obj;
6867         this.listeners = [];
6868     };
6869
6870     Roo.util.Event.prototype = {
6871         addListener : function(fn, scope, options){
6872             var o = options || {};
6873             scope = scope || this.obj;
6874             if(!this.isListening(fn, scope)){
6875                 var l = {fn: fn, scope: scope, options: o};
6876                 var h = fn;
6877                 if(o.delay){
6878                     h = createDelayed(h, o, scope);
6879                 }
6880                 if(o.single){
6881                     h = createSingle(h, this, fn, scope);
6882                 }
6883                 if(o.buffer){
6884                     h = createBuffered(h, o, scope);
6885                 }
6886                 l.fireFn = h;
6887                 if(!this.firing){ // if we are currently firing this event, don't disturb the listener loop
6888                     this.listeners.push(l);
6889                 }else{
6890                     this.listeners = this.listeners.slice(0);
6891                     this.listeners.push(l);
6892                 }
6893             }
6894         },
6895
6896         findListener : function(fn, scope){
6897             scope = scope || this.obj;
6898             var ls = this.listeners;
6899             for(var i = 0, len = ls.length; i < len; i++){
6900                 var l = ls[i];
6901                 if(l.fn == fn && l.scope == scope){
6902                     return i;
6903                 }
6904             }
6905             return -1;
6906         },
6907
6908         isListening : function(fn, scope){
6909             return this.findListener(fn, scope) != -1;
6910         },
6911
6912         removeListener : function(fn, scope){
6913             var index;
6914             if((index = this.findListener(fn, scope)) != -1){
6915                 if(!this.firing){
6916                     this.listeners.splice(index, 1);
6917                 }else{
6918                     this.listeners = this.listeners.slice(0);
6919                     this.listeners.splice(index, 1);
6920                 }
6921                 return true;
6922             }
6923             return false;
6924         },
6925
6926         clearListeners : function(){
6927             this.listeners = [];
6928         },
6929
6930         fire : function(){
6931             var ls = this.listeners, scope, len = ls.length;
6932             if(len > 0){
6933                 this.firing = true;
6934                 var args = Array.prototype.slice.call(arguments, 0);                
6935                 for(var i = 0; i < len; i++){
6936                     var l = ls[i];
6937                     if(l.fireFn.apply(l.scope||this.obj||window, args) === false){
6938                         this.firing = false;
6939                         return false;
6940                     }
6941                 }
6942                 this.firing = false;
6943             }
6944             return true;
6945         }
6946     };
6947 })();/*
6948  * RooJS Library 
6949  * Copyright(c) 2007-2017, Roo J Solutions Ltd
6950  *
6951  * Licence LGPL 
6952  *
6953  */
6954  
6955 /**
6956  * @class Roo.Document
6957  * @extends Roo.util.Observable
6958  * This is a convience class to wrap up the main document loading code.. , rather than adding Roo.onReady(......)
6959  * 
6960  * @param {Object} config the methods and properties of the 'base' class for the application.
6961  * 
6962  *  Generic Page handler - implement this to start your app..
6963  * 
6964  * eg.
6965  *  MyProject = new Roo.Document({
6966         events : {
6967             'load' : true // your events..
6968         },
6969         listeners : {
6970             'ready' : function() {
6971                 // fired on Roo.onReady()
6972             }
6973         }
6974  * 
6975  */
6976 Roo.Document = function(cfg) {
6977      
6978     this.addEvents({ 
6979         'ready' : true
6980     });
6981     Roo.util.Observable.call(this,cfg);
6982     
6983     var _this = this;
6984     
6985     Roo.onReady(function() {
6986         _this.fireEvent('ready');
6987     },null,false);
6988     
6989     
6990 }
6991
6992 Roo.extend(Roo.Document, Roo.util.Observable, {});/*
6993  * Based on:
6994  * Ext JS Library 1.1.1
6995  * Copyright(c) 2006-2007, Ext JS, LLC.
6996  *
6997  * Originally Released Under LGPL - original licence link has changed is not relivant.
6998  *
6999  * Fork - LGPL
7000  * <script type="text/javascript">
7001  */
7002
7003 /**
7004  * @class Roo.EventManager
7005  * Registers event handlers that want to receive a normalized EventObject instead of the standard browser event and provides 
7006  * several useful events directly.
7007  * See {@link Roo.EventObject} for more details on normalized event objects.
7008  * @static
7009  */
7010 Roo.EventManager = function(){
7011     var docReadyEvent, docReadyProcId, docReadyState = false;
7012     var resizeEvent, resizeTask, textEvent, textSize;
7013     var E = Roo.lib.Event;
7014     var D = Roo.lib.Dom;
7015
7016     
7017     
7018
7019     var fireDocReady = function(){
7020         if(!docReadyState){
7021             docReadyState = true;
7022             Roo.isReady = true;
7023             if(docReadyProcId){
7024                 clearInterval(docReadyProcId);
7025             }
7026             if(Roo.isGecko || Roo.isOpera) {
7027                 document.removeEventListener("DOMContentLoaded", fireDocReady, false);
7028             }
7029             if(Roo.isIE){
7030                 var defer = document.getElementById("ie-deferred-loader");
7031                 if(defer){
7032                     defer.onreadystatechange = null;
7033                     defer.parentNode.removeChild(defer);
7034                 }
7035             }
7036             if(docReadyEvent){
7037                 docReadyEvent.fire();
7038                 docReadyEvent.clearListeners();
7039             }
7040         }
7041     };
7042     
7043     var initDocReady = function(){
7044         docReadyEvent = new Roo.util.Event();
7045         if(Roo.isGecko || Roo.isOpera) {
7046             document.addEventListener("DOMContentLoaded", fireDocReady, false);
7047         }else if(Roo.isIE){
7048             document.write("<s"+'cript id="ie-deferred-loader" defer="defer" src="/'+'/:"></s'+"cript>");
7049             var defer = document.getElementById("ie-deferred-loader");
7050             defer.onreadystatechange = function(){
7051                 if(this.readyState == "complete"){
7052                     fireDocReady();
7053                 }
7054             };
7055         }else if(Roo.isSafari){ 
7056             docReadyProcId = setInterval(function(){
7057                 var rs = document.readyState;
7058                 if(rs == "complete") {
7059                     fireDocReady();     
7060                  }
7061             }, 10);
7062         }
7063         // no matter what, make sure it fires on load
7064         E.on(window, "load", fireDocReady);
7065     };
7066
7067     var createBuffered = function(h, o){
7068         var task = new Roo.util.DelayedTask(h);
7069         return function(e){
7070             // create new event object impl so new events don't wipe out properties
7071             e = new Roo.EventObjectImpl(e);
7072             task.delay(o.buffer, h, null, [e]);
7073         };
7074     };
7075
7076     var createSingle = function(h, el, ename, fn){
7077         return function(e){
7078             Roo.EventManager.removeListener(el, ename, fn);
7079             h(e);
7080         };
7081     };
7082
7083     var createDelayed = function(h, o){
7084         return function(e){
7085             // create new event object impl so new events don't wipe out properties
7086             e = new Roo.EventObjectImpl(e);
7087             setTimeout(function(){
7088                 h(e);
7089             }, o.delay || 10);
7090         };
7091     };
7092     var transitionEndVal = false;
7093     
7094     var transitionEnd = function()
7095     {
7096         if (transitionEndVal) {
7097             return transitionEndVal;
7098         }
7099         var el = document.createElement('div');
7100
7101         var transEndEventNames = {
7102             WebkitTransition : 'webkitTransitionEnd',
7103             MozTransition    : 'transitionend',
7104             OTransition      : 'oTransitionEnd otransitionend',
7105             transition       : 'transitionend'
7106         };
7107     
7108         for (var name in transEndEventNames) {
7109             if (el.style[name] !== undefined) {
7110                 transitionEndVal = transEndEventNames[name];
7111                 return  transitionEndVal ;
7112             }
7113         }
7114     }
7115     
7116   
7117
7118     var listen = function(element, ename, opt, fn, scope)
7119     {
7120         var o = (!opt || typeof opt == "boolean") ? {} : opt;
7121         fn = fn || o.fn; scope = scope || o.scope;
7122         var el = Roo.getDom(element);
7123         
7124         
7125         if(!el){
7126             throw "Error listening for \"" + ename + '\". Element "' + element + '" doesn\'t exist.';
7127         }
7128         
7129         if (ename == 'transitionend') {
7130             ename = transitionEnd();
7131         }
7132         var h = function(e){
7133             e = Roo.EventObject.setEvent(e);
7134             var t;
7135             if(o.delegate){
7136                 t = e.getTarget(o.delegate, el);
7137                 if(!t){
7138                     return;
7139                 }
7140             }else{
7141                 t = e.target;
7142             }
7143             if(o.stopEvent === true){
7144                 e.stopEvent();
7145             }
7146             if(o.preventDefault === true){
7147                e.preventDefault();
7148             }
7149             if(o.stopPropagation === true){
7150                 e.stopPropagation();
7151             }
7152
7153             if(o.normalized === false){
7154                 e = e.browserEvent;
7155             }
7156
7157             fn.call(scope || el, e, t, o);
7158         };
7159         if(o.delay){
7160             h = createDelayed(h, o);
7161         }
7162         if(o.single){
7163             h = createSingle(h, el, ename, fn);
7164         }
7165         if(o.buffer){
7166             h = createBuffered(h, o);
7167         }
7168         
7169         fn._handlers = fn._handlers || [];
7170         
7171         
7172         fn._handlers.push([Roo.id(el), ename, h]);
7173         
7174         
7175          
7176         E.on(el, ename, h); // this adds the actuall listener to the object..
7177         
7178         
7179         if(ename == "mousewheel" && el.addEventListener){ // workaround for jQuery
7180             el.addEventListener("DOMMouseScroll", h, false);
7181             E.on(window, 'unload', function(){
7182                 el.removeEventListener("DOMMouseScroll", h, false);
7183             });
7184         }
7185         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7186             Roo.EventManager.stoppedMouseDownEvent.addListener(h);
7187         }
7188         return h;
7189     };
7190
7191     var stopListening = function(el, ename, fn){
7192         var id = Roo.id(el), hds = fn._handlers, hd = fn;
7193         if(hds){
7194             for(var i = 0, len = hds.length; i < len; i++){
7195                 var h = hds[i];
7196                 if(h[0] == id && h[1] == ename){
7197                     hd = h[2];
7198                     hds.splice(i, 1);
7199                     break;
7200                 }
7201             }
7202         }
7203         E.un(el, ename, hd);
7204         el = Roo.getDom(el);
7205         if(ename == "mousewheel" && el.addEventListener){
7206             el.removeEventListener("DOMMouseScroll", hd, false);
7207         }
7208         if(ename == "mousedown" && el == document){ // fix stopped mousedowns on the document
7209             Roo.EventManager.stoppedMouseDownEvent.removeListener(hd);
7210         }
7211     };
7212
7213     var propRe = /^(?:scope|delay|buffer|single|stopEvent|preventDefault|stopPropagation|normalized|args|delegate)$/;
7214     
7215     var pub = {
7216         
7217         
7218         /** 
7219          * Fix for doc tools
7220          * @scope Roo.EventManager
7221          */
7222         
7223         
7224         /** 
7225          * This is no longer needed and is deprecated. Places a simple wrapper around an event handler to override the browser event
7226          * object with a Roo.EventObject
7227          * @param {Function} fn        The method the event invokes
7228          * @param {Object}   scope    An object that becomes the scope of the handler
7229          * @param {boolean}  override If true, the obj passed in becomes
7230          *                             the execution scope of the listener
7231          * @return {Function} The wrapped function
7232          * @deprecated
7233          */
7234         wrap : function(fn, scope, override){
7235             return function(e){
7236                 Roo.EventObject.setEvent(e);
7237                 fn.call(override ? scope || window : window, Roo.EventObject, scope);
7238             };
7239         },
7240         
7241         /**
7242      * Appends an event handler to an element (shorthand for addListener)
7243      * @param {String/HTMLElement}   element        The html element or id to assign the
7244      * @param {String}   eventName The type of event to listen for
7245      * @param {Function} handler The method the event invokes
7246      * @param {Object}   scope (optional) The scope in which to execute the handler
7247      * function. The handler function's "this" context.
7248      * @param {Object}   options (optional) An object containing handler configuration
7249      * properties. This may contain any of the following properties:<ul>
7250      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7251      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7252      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7253      * <li>preventDefault {Boolean} True to prevent the default action</li>
7254      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7255      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7256      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7257      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7258      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7259      * by the specified number of milliseconds. If the event fires again within that time, the original
7260      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7261      * </ul><br>
7262      * <p>
7263      * <b>Combining Options</b><br>
7264      * Using the options argument, it is possible to combine different types of listeners:<br>
7265      * <br>
7266      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7267      * Code:<pre><code>
7268 el.on('click', this.onClick, this, {
7269     single: true,
7270     delay: 100,
7271     stopEvent : true,
7272     forumId: 4
7273 });</code></pre>
7274      * <p>
7275      * <b>Attaching multiple handlers in 1 call</b><br>
7276       * The method also allows for a single argument to be passed which is a config object containing properties
7277      * which specify multiple handlers.
7278      * <p>
7279      * Code:<pre><code>
7280 el.on({
7281     'click' : {
7282         fn: this.onClick
7283         scope: this,
7284         delay: 100
7285     },
7286     'mouseover' : {
7287         fn: this.onMouseOver
7288         scope: this
7289     },
7290     'mouseout' : {
7291         fn: this.onMouseOut
7292         scope: this
7293     }
7294 });</code></pre>
7295      * <p>
7296      * Or a shorthand syntax:<br>
7297      * Code:<pre><code>
7298 el.on({
7299     'click' : this.onClick,
7300     'mouseover' : this.onMouseOver,
7301     'mouseout' : this.onMouseOut
7302     scope: this
7303 });</code></pre>
7304      */
7305         addListener : function(element, eventName, fn, scope, options){
7306             if(typeof eventName == "object"){
7307                 var o = eventName;
7308                 for(var e in o){
7309                     if(propRe.test(e)){
7310                         continue;
7311                     }
7312                     if(typeof o[e] == "function"){
7313                         // shared options
7314                         listen(element, e, o, o[e], o.scope);
7315                     }else{
7316                         // individual options
7317                         listen(element, e, o[e]);
7318                     }
7319                 }
7320                 return;
7321             }
7322             return listen(element, eventName, options, fn, scope);
7323         },
7324         
7325         /**
7326          * Removes an event handler
7327          *
7328          * @param {String/HTMLElement}   element        The id or html element to remove the 
7329          *                             event from
7330          * @param {String}   eventName     The type of event
7331          * @param {Function} fn
7332          * @return {Boolean} True if a listener was actually removed
7333          */
7334         removeListener : function(element, eventName, fn){
7335             return stopListening(element, eventName, fn);
7336         },
7337         
7338         /**
7339          * Fires when the document is ready (before onload and before images are loaded). Can be 
7340          * accessed shorthanded Roo.onReady().
7341          * @param {Function} fn        The method the event invokes
7342          * @param {Object}   scope    An  object that becomes the scope of the handler
7343          * @param {boolean}  options
7344          */
7345         onDocumentReady : function(fn, scope, options){
7346             if(docReadyState){ // if it already fired
7347                 docReadyEvent.addListener(fn, scope, options);
7348                 docReadyEvent.fire();
7349                 docReadyEvent.clearListeners();
7350                 return;
7351             }
7352             if(!docReadyEvent){
7353                 initDocReady();
7354             }
7355             docReadyEvent.addListener(fn, scope, options);
7356         },
7357         
7358         /**
7359          * Fires when the window is resized and provides resize event buffering (50 milliseconds), passes new viewport width and height to handlers.
7360          * @param {Function} fn        The method the event invokes
7361          * @param {Object}   scope    An object that becomes the scope of the handler
7362          * @param {boolean}  options
7363          */
7364         onWindowResize : function(fn, scope, options)
7365         {
7366             if(!resizeEvent){
7367                 resizeEvent = new Roo.util.Event();
7368                 resizeTask = new Roo.util.DelayedTask(function(){
7369                     resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7370                 });
7371                 E.on(window, "resize", function()
7372                 {
7373                     if (Roo.isIE) {
7374                         resizeTask.delay(50);
7375                     } else {
7376                         resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7377                     }
7378                 });
7379             }
7380             resizeEvent.addListener(fn, scope, options);
7381         },
7382
7383         /**
7384          * Fires when the user changes the active text size. Handler gets called with 2 params, the old size and the new size.
7385          * @param {Function} fn        The method the event invokes
7386          * @param {Object}   scope    An object that becomes the scope of the handler
7387          * @param {boolean}  options
7388          */
7389         onTextResize : function(fn, scope, options){
7390             if(!textEvent){
7391                 textEvent = new Roo.util.Event();
7392                 var textEl = new Roo.Element(document.createElement('div'));
7393                 textEl.dom.className = 'x-text-resize';
7394                 textEl.dom.innerHTML = 'X';
7395                 textEl.appendTo(document.body);
7396                 textSize = textEl.dom.offsetHeight;
7397                 setInterval(function(){
7398                     if(textEl.dom.offsetHeight != textSize){
7399                         textEvent.fire(textSize, textSize = textEl.dom.offsetHeight);
7400                     }
7401                 }, this.textResizeInterval);
7402             }
7403             textEvent.addListener(fn, scope, options);
7404         },
7405
7406         /**
7407          * Removes the passed window resize listener.
7408          * @param {Function} fn        The method the event invokes
7409          * @param {Object}   scope    The scope of handler
7410          */
7411         removeResizeListener : function(fn, scope){
7412             if(resizeEvent){
7413                 resizeEvent.removeListener(fn, scope);
7414             }
7415         },
7416
7417         // private
7418         fireResize : function(){
7419             if(resizeEvent){
7420                 resizeEvent.fire(D.getViewWidth(), D.getViewHeight());
7421             }   
7422         },
7423         /**
7424          * Url used for onDocumentReady with using SSL (defaults to Roo.SSL_SECURE_URL)
7425          */
7426         ieDeferSrc : false,
7427         /**
7428          * The frequency, in milliseconds, to check for text resize events (defaults to 50)
7429          */
7430         textResizeInterval : 50
7431     };
7432     
7433     /**
7434      * Fix for doc tools
7435      * @scopeAlias pub=Roo.EventManager
7436      */
7437     
7438      /**
7439      * Appends an event handler to an element (shorthand for addListener)
7440      * @param {String/HTMLElement}   element        The html element or id to assign the
7441      * @param {String}   eventName The type of event to listen for
7442      * @param {Function} handler The method the event invokes
7443      * @param {Object}   scope (optional) The scope in which to execute the handler
7444      * function. The handler function's "this" context.
7445      * @param {Object}   options (optional) An object containing handler configuration
7446      * properties. This may contain any of the following properties:<ul>
7447      * <li>scope {Object} The scope in which to execute the handler function. The handler function's "this" context.</li>
7448      * <li>delegate {String} A simple selector to filter the target or look for a descendant of the target</li>
7449      * <li>stopEvent {Boolean} True to stop the event. That is stop propagation, and prevent the default action.</li>
7450      * <li>preventDefault {Boolean} True to prevent the default action</li>
7451      * <li>stopPropagation {Boolean} True to prevent event propagation</li>
7452      * <li>normalized {Boolean} False to pass a browser event to the handler function instead of an Roo.EventObject</li>
7453      * <li>delay {Number} The number of milliseconds to delay the invocation of the handler after te event fires.</li>
7454      * <li>single {Boolean} True to add a handler to handle just the next firing of the event, and then remove itself.</li>
7455      * <li>buffer {Number} Causes the handler to be scheduled to run in an {@link Roo.util.DelayedTask} delayed
7456      * by the specified number of milliseconds. If the event fires again within that time, the original
7457      * handler is <em>not</em> invoked, but the new handler is scheduled in its place.</li>
7458      * </ul><br>
7459      * <p>
7460      * <b>Combining Options</b><br>
7461      * Using the options argument, it is possible to combine different types of listeners:<br>
7462      * <br>
7463      * A normalized, delayed, one-time listener that auto stops the event and passes a custom argument (forumId)<div style="margin: 5px 20px 20px;">
7464      * Code:<pre><code>
7465 el.on('click', this.onClick, this, {
7466     single: true,
7467     delay: 100,
7468     stopEvent : true,
7469     forumId: 4
7470 });</code></pre>
7471      * <p>
7472      * <b>Attaching multiple handlers in 1 call</b><br>
7473       * The method also allows for a single argument to be passed which is a config object containing properties
7474      * which specify multiple handlers.
7475      * <p>
7476      * Code:<pre><code>
7477 el.on({
7478     'click' : {
7479         fn: this.onClick
7480         scope: this,
7481         delay: 100
7482     },
7483     'mouseover' : {
7484         fn: this.onMouseOver
7485         scope: this
7486     },
7487     'mouseout' : {
7488         fn: this.onMouseOut
7489         scope: this
7490     }
7491 });</code></pre>
7492      * <p>
7493      * Or a shorthand syntax:<br>
7494      * Code:<pre><code>
7495 el.on({
7496     'click' : this.onClick,
7497     'mouseover' : this.onMouseOver,
7498     'mouseout' : this.onMouseOut
7499     scope: this
7500 });</code></pre>
7501      */
7502     pub.on = pub.addListener;
7503     pub.un = pub.removeListener;
7504
7505     pub.stoppedMouseDownEvent = new Roo.util.Event();
7506     return pub;
7507 }();
7508 /**
7509   * Fires when the document is ready (before onload and before images are loaded).  Shorthand of {@link Roo.EventManager#onDocumentReady}.
7510   * @param {Function} fn        The method the event invokes
7511   * @param {Object}   scope    An  object that becomes the scope of the handler
7512   * @param {boolean}  override If true, the obj passed in becomes
7513   *                             the execution scope of the listener
7514   * @member Roo
7515   * @method onReady
7516  */
7517 Roo.onReady = Roo.EventManager.onDocumentReady;
7518
7519 Roo.onReady(function(){
7520     var bd = Roo.get(document.body);
7521     if(!bd){ return; }
7522
7523     var cls = [
7524             Roo.isIE ? "roo-ie"
7525             : Roo.isIE11 ? "roo-ie11"
7526             : Roo.isEdge ? "roo-edge"
7527             : Roo.isGecko ? "roo-gecko"
7528             : Roo.isOpera ? "roo-opera"
7529             : Roo.isSafari ? "roo-safari" : ""];
7530
7531     if(Roo.isMac){
7532         cls.push("roo-mac");
7533     }
7534     if(Roo.isLinux){
7535         cls.push("roo-linux");
7536     }
7537     if(Roo.isIOS){
7538         cls.push("roo-ios");
7539     }
7540     if(Roo.isTouch){
7541         cls.push("roo-touch");
7542     }
7543     if(Roo.isBorderBox){
7544         cls.push('roo-border-box');
7545     }
7546     if(Roo.isStrict){ // add to the parent to allow for selectors like ".ext-strict .ext-ie"
7547         var p = bd.dom.parentNode;
7548         if(p){
7549             p.className += ' roo-strict';
7550         }
7551     }
7552     bd.addClass(cls.join(' '));
7553 });
7554
7555 /**
7556  * @class Roo.EventObject
7557  * EventObject exposes the Yahoo! UI Event functionality directly on the object
7558  * passed to your event handler. It exists mostly for convenience. It also fixes the annoying null checks automatically to cleanup your code 
7559  * Example:
7560  * <pre><code>
7561  function handleClick(e){ // e is not a standard event object, it is a Roo.EventObject
7562     e.preventDefault();
7563     var target = e.getTarget();
7564     ...
7565  }
7566  var myDiv = Roo.get("myDiv");
7567  myDiv.on("click", handleClick);
7568  //or
7569  Roo.EventManager.on("myDiv", 'click', handleClick);
7570  Roo.EventManager.addListener("myDiv", 'click', handleClick);
7571  </code></pre>
7572  * @static
7573  */
7574 Roo.EventObject = function(){
7575     
7576     var E = Roo.lib.Event;
7577     
7578     // safari keypress events for special keys return bad keycodes
7579     var safariKeys = {
7580         63234 : 37, // left
7581         63235 : 39, // right
7582         63232 : 38, // up
7583         63233 : 40, // down
7584         63276 : 33, // page up
7585         63277 : 34, // page down
7586         63272 : 46, // delete
7587         63273 : 36, // home
7588         63275 : 35  // end
7589     };
7590
7591     // normalize button clicks
7592     var btnMap = Roo.isIE ? {1:0,4:1,2:2} :
7593                 (Roo.isSafari ? {1:0,2:1,3:2} : {0:0,1:1,2:2});
7594
7595     Roo.EventObjectImpl = function(e){
7596         if(e){
7597             this.setEvent(e.browserEvent || e);
7598         }
7599     };
7600     Roo.EventObjectImpl.prototype = {
7601         /**
7602          * Used to fix doc tools.
7603          * @scope Roo.EventObject.prototype
7604          */
7605             
7606
7607         
7608         
7609         /** The normal browser event */
7610         browserEvent : null,
7611         /** The button pressed in a mouse event */
7612         button : -1,
7613         /** True if the shift key was down during the event */
7614         shiftKey : false,
7615         /** True if the control key was down during the event */
7616         ctrlKey : false,
7617         /** True if the alt key was down during the event */
7618         altKey : false,
7619
7620         /** Key constant 
7621         * @type Number */
7622         BACKSPACE : 8,
7623         /** Key constant 
7624         * @type Number */
7625         TAB : 9,
7626         /** Key constant 
7627         * @type Number */
7628         RETURN : 13,
7629         /** Key constant 
7630         * @type Number */
7631         ENTER : 13,
7632         /** Key constant 
7633         * @type Number */
7634         SHIFT : 16,
7635         /** Key constant 
7636         * @type Number */
7637         CONTROL : 17,
7638         /** Key constant 
7639         * @type Number */
7640         ESC : 27,
7641         /** Key constant 
7642         * @type Number */
7643         SPACE : 32,
7644         /** Key constant 
7645         * @type Number */
7646         PAGEUP : 33,
7647         /** Key constant 
7648         * @type Number */
7649         PAGEDOWN : 34,
7650         /** Key constant 
7651         * @type Number */
7652         END : 35,
7653         /** Key constant 
7654         * @type Number */
7655         HOME : 36,
7656         /** Key constant 
7657         * @type Number */
7658         LEFT : 37,
7659         /** Key constant 
7660         * @type Number */
7661         UP : 38,
7662         /** Key constant 
7663         * @type Number */
7664         RIGHT : 39,
7665         /** Key constant 
7666         * @type Number */
7667         DOWN : 40,
7668         /** Key constant 
7669         * @type Number */
7670         DELETE : 46,
7671         /** Key constant 
7672         * @type Number */
7673         F5 : 116,
7674
7675            /** @private */
7676         setEvent : function(e){
7677             if(e == this || (e && e.browserEvent)){ // already wrapped
7678                 return e;
7679             }
7680             this.browserEvent = e;
7681             if(e){
7682                 // normalize buttons
7683                 this.button = e.button ? btnMap[e.button] : (e.which ? e.which-1 : -1);
7684                 if(e.type == 'click' && this.button == -1){
7685                     this.button = 0;
7686                 }
7687                 this.type = e.type;
7688                 this.shiftKey = e.shiftKey;
7689                 // mac metaKey behaves like ctrlKey
7690                 this.ctrlKey = e.ctrlKey || e.metaKey;
7691                 this.altKey = e.altKey;
7692                 // in getKey these will be normalized for the mac
7693                 this.keyCode = e.keyCode;
7694                 // keyup warnings on firefox.
7695                 this.charCode = (e.type == 'keyup' || e.type == 'keydown') ? 0 : e.charCode;
7696                 // cache the target for the delayed and or buffered events
7697                 this.target = E.getTarget(e);
7698                 // same for XY
7699                 this.xy = E.getXY(e);
7700             }else{
7701                 this.button = -1;
7702                 this.shiftKey = false;
7703                 this.ctrlKey = false;
7704                 this.altKey = false;
7705                 this.keyCode = 0;
7706                 this.charCode =0;
7707                 this.target = null;
7708                 this.xy = [0, 0];
7709             }
7710             return this;
7711         },
7712
7713         /**
7714          * Stop the event (preventDefault and stopPropagation)
7715          */
7716         stopEvent : function(){
7717             if(this.browserEvent){
7718                 if(this.browserEvent.type == 'mousedown'){
7719                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7720                 }
7721                 E.stopEvent(this.browserEvent);
7722             }
7723         },
7724
7725         /**
7726          * Prevents the browsers default handling of the event.
7727          */
7728         preventDefault : function(){
7729             if(this.browserEvent){
7730                 E.preventDefault(this.browserEvent);
7731             }
7732         },
7733
7734         /** @private */
7735         isNavKeyPress : function(){
7736             var k = this.keyCode;
7737             k = Roo.isSafari ? (safariKeys[k] || k) : k;
7738             return (k >= 33 && k <= 40) || k == this.RETURN || k == this.TAB || k == this.ESC;
7739         },
7740
7741         isSpecialKey : function(){
7742             var k = this.keyCode;
7743             return (this.type == 'keypress' && this.ctrlKey) || k == 9 || k == 13  || k == 40 || k == 27 ||
7744             (k == 16) || (k == 17) ||
7745             (k >= 18 && k <= 20) ||
7746             (k >= 33 && k <= 35) ||
7747             (k >= 36 && k <= 39) ||
7748             (k >= 44 && k <= 45);
7749         },
7750         /**
7751          * Cancels bubbling of the event.
7752          */
7753         stopPropagation : function(){
7754             if(this.browserEvent){
7755                 if(this.type == 'mousedown'){
7756                     Roo.EventManager.stoppedMouseDownEvent.fire(this);
7757                 }
7758                 E.stopPropagation(this.browserEvent);
7759             }
7760         },
7761
7762         /**
7763          * Gets the key code for the event.
7764          * @return {Number}
7765          */
7766         getCharCode : function(){
7767             return this.charCode || this.keyCode;
7768         },
7769
7770         /**
7771          * Returns a normalized keyCode for the event.
7772          * @return {Number} The key code
7773          */
7774         getKey : function(){
7775             var k = this.keyCode || this.charCode;
7776             return Roo.isSafari ? (safariKeys[k] || k) : k;
7777         },
7778
7779         /**
7780          * Gets the x coordinate of the event.
7781          * @return {Number}
7782          */
7783         getPageX : function(){
7784             return this.xy[0];
7785         },
7786
7787         /**
7788          * Gets the y coordinate of the event.
7789          * @return {Number}
7790          */
7791         getPageY : function(){
7792             return this.xy[1];
7793         },
7794
7795         /**
7796          * Gets the time of the event.
7797          * @return {Number}
7798          */
7799         getTime : function(){
7800             if(this.browserEvent){
7801                 return E.getTime(this.browserEvent);
7802             }
7803             return null;
7804         },
7805
7806         /**
7807          * Gets the page coordinates of the event.
7808          * @return {Array} The xy values like [x, y]
7809          */
7810         getXY : function(){
7811             return this.xy;
7812         },
7813
7814         /**
7815          * Gets the target for the event.
7816          * @param {String} selector (optional) A simple selector to filter the target or look for an ancestor of the target
7817          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
7818                 search as a number or element (defaults to 10 || document.body)
7819          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
7820          * @return {HTMLelement}
7821          */
7822         getTarget : function(selector, maxDepth, returnEl){
7823             return selector ? Roo.fly(this.target).findParent(selector, maxDepth, returnEl) : this.target;
7824         },
7825         /**
7826          * Gets the related target.
7827          * @return {HTMLElement}
7828          */
7829         getRelatedTarget : function(){
7830             if(this.browserEvent){
7831                 return E.getRelatedTarget(this.browserEvent);
7832             }
7833             return null;
7834         },
7835
7836         /**
7837          * Normalizes mouse wheel delta across browsers
7838          * @return {Number} The delta
7839          */
7840         getWheelDelta : function(){
7841             var e = this.browserEvent;
7842             var delta = 0;
7843             if(e.wheelDelta){ /* IE/Opera. */
7844                 delta = e.wheelDelta/120;
7845             }else if(e.detail){ /* Mozilla case. */
7846                 delta = -e.detail/3;
7847             }
7848             return delta;
7849         },
7850
7851         /**
7852          * Returns true if the control, meta, shift or alt key was pressed during this event.
7853          * @return {Boolean}
7854          */
7855         hasModifier : function(){
7856             return !!((this.ctrlKey || this.altKey) || this.shiftKey);
7857         },
7858
7859         /**
7860          * Returns true if the target of this event equals el or is a child of el
7861          * @param {String/HTMLElement/Element} el
7862          * @param {Boolean} related (optional) true to test if the related target is within el instead of the target
7863          * @return {Boolean}
7864          */
7865         within : function(el, related){
7866             var t = this[related ? "getRelatedTarget" : "getTarget"]();
7867             return t && Roo.fly(el).contains(t);
7868         },
7869
7870         getPoint : function(){
7871             return new Roo.lib.Point(this.xy[0], this.xy[1]);
7872         }
7873     };
7874
7875     return new Roo.EventObjectImpl();
7876 }();
7877             
7878     /*
7879  * Based on:
7880  * Ext JS Library 1.1.1
7881  * Copyright(c) 2006-2007, Ext JS, LLC.
7882  *
7883  * Originally Released Under LGPL - original licence link has changed is not relivant.
7884  *
7885  * Fork - LGPL
7886  * <script type="text/javascript">
7887  */
7888
7889  
7890 // was in Composite Element!??!?!
7891  
7892 (function(){
7893     var D = Roo.lib.Dom;
7894     var E = Roo.lib.Event;
7895     var A = Roo.lib.Anim;
7896
7897     // local style camelizing for speed
7898     var propCache = {};
7899     var camelRe = /(-[a-z])/gi;
7900     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
7901     var view = document.defaultView;
7902
7903 /**
7904  * @class Roo.Element
7905  * Represents an Element in the DOM.<br><br>
7906  * Usage:<br>
7907 <pre><code>
7908 var el = Roo.get("my-div");
7909
7910 // or with getEl
7911 var el = getEl("my-div");
7912
7913 // or with a DOM element
7914 var el = Roo.get(myDivElement);
7915 </code></pre>
7916  * Using Roo.get() or getEl() instead of calling the constructor directly ensures you get the same object
7917  * each call instead of constructing a new one.<br><br>
7918  * <b>Animations</b><br />
7919  * Many of the functions for manipulating an element have an optional "animate" parameter. The animate parameter
7920  * should either be a boolean (true) or an object literal with animation options. The animation options are:
7921 <pre>
7922 Option    Default   Description
7923 --------- --------  ---------------------------------------------
7924 duration  .35       The duration of the animation in seconds
7925 easing    easeOut   The YUI easing method
7926 callback  none      A function to execute when the anim completes
7927 scope     this      The scope (this) of the callback function
7928 </pre>
7929 * Also, the Anim object being used for the animation will be set on your options object as "anim", which allows you to stop or
7930 * manipulate the animation. Here's an example:
7931 <pre><code>
7932 var el = Roo.get("my-div");
7933
7934 // no animation
7935 el.setWidth(100);
7936
7937 // default animation
7938 el.setWidth(100, true);
7939
7940 // animation with some options set
7941 el.setWidth(100, {
7942     duration: 1,
7943     callback: this.foo,
7944     scope: this
7945 });
7946
7947 // using the "anim" property to get the Anim object
7948 var opt = {
7949     duration: 1,
7950     callback: this.foo,
7951     scope: this
7952 };
7953 el.setWidth(100, opt);
7954 ...
7955 if(opt.anim.isAnimated()){
7956     opt.anim.stop();
7957 }
7958 </code></pre>
7959 * <b> Composite (Collections of) Elements</b><br />
7960  * For working with collections of Elements, see <a href="Roo.CompositeElement.html">Roo.CompositeElement</a>
7961  * @constructor Create a new Element directly.
7962  * @param {String/HTMLElement} element
7963  * @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).
7964  */
7965     Roo.Element = function(element, forceNew)
7966     {
7967         var dom = typeof element == "string" ?
7968                 document.getElementById(element) : element;
7969         
7970         this.listeners = {};
7971         
7972         if(!dom){ // invalid id/element
7973             return null;
7974         }
7975         var id = dom.id;
7976         if(forceNew !== true && id && Roo.Element.cache[id]){ // element object already exists
7977             return Roo.Element.cache[id];
7978         }
7979
7980         /**
7981          * The DOM element
7982          * @type HTMLElement
7983          */
7984         this.dom = dom;
7985
7986         /**
7987          * The DOM element ID
7988          * @type String
7989          */
7990         this.id = id || Roo.id(dom);
7991         
7992         return this; // assumed for cctor?
7993     };
7994
7995     var El = Roo.Element;
7996
7997     El.prototype = {
7998         /**
7999          * The element's default display mode  (defaults to "") 
8000          * @type String
8001          */
8002         originalDisplay : "",
8003
8004         
8005         // note this is overridden in BS version..
8006         visibilityMode : 1, 
8007         /**
8008          * The default unit to append to CSS values where a unit isn't provided (defaults to px).
8009          * @type String
8010          */
8011         defaultUnit : "px",
8012         
8013         /**
8014          * Sets the element's visibility mode. When setVisible() is called it
8015          * will use this to determine whether to set the visibility or the display property.
8016          * @param visMode Element.VISIBILITY or Element.DISPLAY
8017          * @return {Roo.Element} this
8018          */
8019         setVisibilityMode : function(visMode){
8020             this.visibilityMode = visMode;
8021             return this;
8022         },
8023         /**
8024          * Convenience method for setVisibilityMode(Element.DISPLAY)
8025          * @param {String} display (optional) What to set display to when visible
8026          * @return {Roo.Element} this
8027          */
8028         enableDisplayMode : function(display){
8029             this.setVisibilityMode(El.DISPLAY);
8030             if(typeof display != "undefined") { this.originalDisplay = display; }
8031             return this;
8032         },
8033
8034         /**
8035          * 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)
8036          * @param {String} selector The simple selector to test
8037          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8038                 search as a number or element (defaults to 10 || document.body)
8039          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8040          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8041          */
8042         findParent : function(simpleSelector, maxDepth, returnEl){
8043             var p = this.dom, b = document.body, depth = 0, dq = Roo.DomQuery, stopEl;
8044             maxDepth = maxDepth || 50;
8045             if(typeof maxDepth != "number"){
8046                 stopEl = Roo.getDom(maxDepth);
8047                 maxDepth = 10;
8048             }
8049             while(p && p.nodeType == 1 && depth < maxDepth && p != b && p != stopEl){
8050                 if(dq.is(p, simpleSelector)){
8051                     return returnEl ? Roo.get(p) : p;
8052                 }
8053                 depth++;
8054                 p = p.parentNode;
8055             }
8056             return null;
8057         },
8058
8059
8060         /**
8061          * Looks at parent nodes for a match of the passed simple selector (e.g. div.some-class or span:first-child)
8062          * @param {String} selector The simple selector to test
8063          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8064                 search as a number or element (defaults to 10 || document.body)
8065          * @param {Boolean} returnEl (optional) True to return a Roo.Element object instead of DOM node
8066          * @return {HTMLElement} The matching DOM node (or null if no match was found)
8067          */
8068         findParentNode : function(simpleSelector, maxDepth, returnEl){
8069             var p = Roo.fly(this.dom.parentNode, '_internal');
8070             return p ? p.findParent(simpleSelector, maxDepth, returnEl) : null;
8071         },
8072         
8073         /**
8074          * Looks at  the scrollable parent element
8075          */
8076         findScrollableParent : function()
8077         {
8078             var overflowRegex = /(auto|scroll)/;
8079             
8080             if(this.getStyle('position') === 'fixed'){
8081                 return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8082             }
8083             
8084             var excludeStaticParent = this.getStyle('position') === "absolute";
8085             
8086             for (var parent = this; (parent = Roo.get(parent.dom.parentNode));){
8087                 
8088                 if (excludeStaticParent && parent.getStyle('position') === "static") {
8089                     continue;
8090                 }
8091                 
8092                 if (overflowRegex.test(parent.getStyle('overflow') + parent.getStyle('overflow-x') + parent.getStyle('overflow-y'))){
8093                     return parent;
8094                 }
8095                 
8096                 if(parent.dom.nodeName.toLowerCase() == 'body'){
8097                     return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8098                 }
8099             }
8100             
8101             return Roo.isAndroid ? Roo.get(document.documentElement) : Roo.get(document.body);
8102         },
8103
8104         /**
8105          * Walks up the dom looking for a parent node that matches the passed simple selector (e.g. div.some-class or span:first-child).
8106          * This is a shortcut for findParentNode() that always returns an Roo.Element.
8107          * @param {String} selector The simple selector to test
8108          * @param {Number/String/HTMLElement/Element} maxDepth (optional) The max depth to
8109                 search as a number or element (defaults to 10 || document.body)
8110          * @return {Roo.Element} The matching DOM node (or null if no match was found)
8111          */
8112         up : function(simpleSelector, maxDepth){
8113             return this.findParentNode(simpleSelector, maxDepth, true);
8114         },
8115
8116
8117
8118         /**
8119          * Returns true if this element matches the passed simple selector (e.g. div.some-class or span:first-child)
8120          * @param {String} selector The simple selector to test
8121          * @return {Boolean} True if this element matches the selector, else false
8122          */
8123         is : function(simpleSelector){
8124             return Roo.DomQuery.is(this.dom, simpleSelector);
8125         },
8126
8127         /**
8128          * Perform animation on this element.
8129          * @param {Object} args The YUI animation control args
8130          * @param {Float} duration (optional) How long the animation lasts in seconds (defaults to .35)
8131          * @param {Function} onComplete (optional) Function to call when animation completes
8132          * @param {String} easing (optional) Easing method to use (defaults to 'easeOut')
8133          * @param {String} animType (optional) 'run' is the default. Can also be 'color', 'motion', or 'scroll'
8134          * @return {Roo.Element} this
8135          */
8136         animate : function(args, duration, onComplete, easing, animType){
8137             this.anim(args, {duration: duration, callback: onComplete, easing: easing}, animType);
8138             return this;
8139         },
8140
8141         /*
8142          * @private Internal animation call
8143          */
8144         anim : function(args, opt, animType, defaultDur, defaultEase, cb){
8145             animType = animType || 'run';
8146             opt = opt || {};
8147             var anim = Roo.lib.Anim[animType](
8148                 this.dom, args,
8149                 (opt.duration || defaultDur) || .35,
8150                 (opt.easing || defaultEase) || 'easeOut',
8151                 function(){
8152                     Roo.callback(cb, this);
8153                     Roo.callback(opt.callback, opt.scope || this, [this, opt]);
8154                 },
8155                 this
8156             );
8157             opt.anim = anim;
8158             return anim;
8159         },
8160
8161         // private legacy anim prep
8162         preanim : function(a, i){
8163             return !a[i] ? false : (typeof a[i] == "object" ? a[i]: {duration: a[i+1], callback: a[i+2], easing: a[i+3]});
8164         },
8165
8166         /**
8167          * Removes worthless text nodes
8168          * @param {Boolean} forceReclean (optional) By default the element
8169          * keeps track if it has been cleaned already so
8170          * you can call this over and over. However, if you update the element and
8171          * need to force a reclean, you can pass true.
8172          */
8173         clean : function(forceReclean){
8174             if(this.isCleaned && forceReclean !== true){
8175                 return this;
8176             }
8177             var ns = /\S/;
8178             var d = this.dom, n = d.firstChild, ni = -1;
8179             while(n){
8180                 var nx = n.nextSibling;
8181                 if(n.nodeType == 3 && !ns.test(n.nodeValue)){
8182                     d.removeChild(n);
8183                 }else{
8184                     n.nodeIndex = ++ni;
8185                 }
8186                 n = nx;
8187             }
8188             this.isCleaned = true;
8189             return this;
8190         },
8191
8192         // private
8193         calcOffsetsTo : function(el){
8194             el = Roo.get(el);
8195             var d = el.dom;
8196             var restorePos = false;
8197             if(el.getStyle('position') == 'static'){
8198                 el.position('relative');
8199                 restorePos = true;
8200             }
8201             var x = 0, y =0;
8202             var op = this.dom;
8203             while(op && op != d && op.tagName != 'HTML'){
8204                 x+= op.offsetLeft;
8205                 y+= op.offsetTop;
8206                 op = op.offsetParent;
8207             }
8208             if(restorePos){
8209                 el.position('static');
8210             }
8211             return [x, y];
8212         },
8213
8214         /**
8215          * Scrolls this element into view within the passed container.
8216          * @param {String/HTMLElement/Element} container (optional) The container element to scroll (defaults to document.body)
8217          * @param {Boolean} hscroll (optional) False to disable horizontal scroll (defaults to true)
8218          * @return {Roo.Element} this
8219          */
8220         scrollIntoView : function(container, hscroll){
8221             var c = Roo.getDom(container) || document.body;
8222             var el = this.dom;
8223
8224             var o = this.calcOffsetsTo(c),
8225                 l = o[0],
8226                 t = o[1],
8227                 b = t+el.offsetHeight,
8228                 r = l+el.offsetWidth;
8229
8230             var ch = c.clientHeight;
8231             var ct = parseInt(c.scrollTop, 10);
8232             var cl = parseInt(c.scrollLeft, 10);
8233             var cb = ct + ch;
8234             var cr = cl + c.clientWidth;
8235
8236             if(t < ct){
8237                 c.scrollTop = t;
8238             }else if(b > cb){
8239                 c.scrollTop = b-ch;
8240             }
8241
8242             if(hscroll !== false){
8243                 if(l < cl){
8244                     c.scrollLeft = l;
8245                 }else if(r > cr){
8246                     c.scrollLeft = r-c.clientWidth;
8247                 }
8248             }
8249             return this;
8250         },
8251
8252         // private
8253         scrollChildIntoView : function(child, hscroll){
8254             Roo.fly(child, '_scrollChildIntoView').scrollIntoView(this, hscroll);
8255         },
8256
8257         /**
8258          * Measures the element's content height and updates height to match. Note: this function uses setTimeout so
8259          * the new height may not be available immediately.
8260          * @param {Boolean} animate (optional) Animate the transition (defaults to false)
8261          * @param {Float} duration (optional) Length of the animation in seconds (defaults to .35)
8262          * @param {Function} onComplete (optional) Function to call when animation completes
8263          * @param {String} easing (optional) Easing method to use (defaults to easeOut)
8264          * @return {Roo.Element} this
8265          */
8266         autoHeight : function(animate, duration, onComplete, easing){
8267             var oldHeight = this.getHeight();
8268             this.clip();
8269             this.setHeight(1); // force clipping
8270             setTimeout(function(){
8271                 var height = parseInt(this.dom.scrollHeight, 10); // parseInt for Safari
8272                 if(!animate){
8273                     this.setHeight(height);
8274                     this.unclip();
8275                     if(typeof onComplete == "function"){
8276                         onComplete();
8277                     }
8278                 }else{
8279                     this.setHeight(oldHeight); // restore original height
8280                     this.setHeight(height, animate, duration, function(){
8281                         this.unclip();
8282                         if(typeof onComplete == "function") { onComplete(); }
8283                     }.createDelegate(this), easing);
8284                 }
8285             }.createDelegate(this), 0);
8286             return this;
8287         },
8288
8289         /**
8290          * Returns true if this element is an ancestor of the passed element
8291          * @param {HTMLElement/String} el The element to check
8292          * @return {Boolean} True if this element is an ancestor of el, else false
8293          */
8294         contains : function(el){
8295             if(!el){return false;}
8296             return D.isAncestor(this.dom, el.dom ? el.dom : el);
8297         },
8298
8299         /**
8300          * Checks whether the element is currently visible using both visibility and display properties.
8301          * @param {Boolean} deep (optional) True to walk the dom and see if parent elements are hidden (defaults to false)
8302          * @return {Boolean} True if the element is currently visible, else false
8303          */
8304         isVisible : function(deep) {
8305             var vis = !(this.getStyle("visibility") == "hidden" || this.getStyle("display") == "none");
8306             if(deep !== true || !vis){
8307                 return vis;
8308             }
8309             var p = this.dom.parentNode;
8310             while(p && p.tagName.toLowerCase() != "body"){
8311                 if(!Roo.fly(p, '_isVisible').isVisible()){
8312                     return false;
8313                 }
8314                 p = p.parentNode;
8315             }
8316             return true;
8317         },
8318
8319         /**
8320          * Creates a {@link Roo.CompositeElement} for child nodes based on the passed CSS selector (the selector should not contain an id).
8321          * @param {String} selector The CSS selector
8322          * @param {Boolean} unique (optional) True to create a unique Roo.Element for each child (defaults to false, which creates a single shared flyweight object)
8323          * @return {CompositeElement/CompositeElementLite} The composite element
8324          */
8325         select : function(selector, unique){
8326             return El.select(selector, unique, this.dom);
8327         },
8328
8329         /**
8330          * Selects child nodes based on the passed CSS selector (the selector should not contain an id).
8331          * @param {String} selector The CSS selector
8332          * @return {Array} An array of the matched nodes
8333          */
8334         query : function(selector, unique){
8335             return Roo.DomQuery.select(selector, this.dom);
8336         },
8337
8338         /**
8339          * Selects a single child at any depth below this element based on the passed CSS selector (the selector should not contain an id).
8340          * @param {String} selector The CSS selector
8341          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8342          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8343          */
8344         child : function(selector, returnDom){
8345             var n = Roo.DomQuery.selectNode(selector, this.dom);
8346             return returnDom ? n : Roo.get(n);
8347         },
8348
8349         /**
8350          * Selects a single *direct* child based on the passed CSS selector (the selector should not contain an id).
8351          * @param {String} selector The CSS selector
8352          * @param {Boolean} returnDom (optional) True to return the DOM node instead of Roo.Element (defaults to false)
8353          * @return {HTMLElement/Roo.Element} The child Roo.Element (or DOM node if returnDom = true)
8354          */
8355         down : function(selector, returnDom){
8356             var n = Roo.DomQuery.selectNode(" > " + selector, this.dom);
8357             return returnDom ? n : Roo.get(n);
8358         },
8359
8360         /**
8361          * Initializes a {@link Roo.dd.DD} drag drop object for this element.
8362          * @param {String} group The group the DD object is member of
8363          * @param {Object} config The DD config object
8364          * @param {Object} overrides An object containing methods to override/implement on the DD object
8365          * @return {Roo.dd.DD} The DD object
8366          */
8367         initDD : function(group, config, overrides){
8368             var dd = new Roo.dd.DD(Roo.id(this.dom), group, config);
8369             return Roo.apply(dd, overrides);
8370         },
8371
8372         /**
8373          * Initializes a {@link Roo.dd.DDProxy} object for this element.
8374          * @param {String} group The group the DDProxy object is member of
8375          * @param {Object} config The DDProxy config object
8376          * @param {Object} overrides An object containing methods to override/implement on the DDProxy object
8377          * @return {Roo.dd.DDProxy} The DDProxy object
8378          */
8379         initDDProxy : function(group, config, overrides){
8380             var dd = new Roo.dd.DDProxy(Roo.id(this.dom), group, config);
8381             return Roo.apply(dd, overrides);
8382         },
8383
8384         /**
8385          * Initializes a {@link Roo.dd.DDTarget} object for this element.
8386          * @param {String} group The group the DDTarget object is member of
8387          * @param {Object} config The DDTarget config object
8388          * @param {Object} overrides An object containing methods to override/implement on the DDTarget object
8389          * @return {Roo.dd.DDTarget} The DDTarget object
8390          */
8391         initDDTarget : function(group, config, overrides){
8392             var dd = new Roo.dd.DDTarget(Roo.id(this.dom), group, config);
8393             return Roo.apply(dd, overrides);
8394         },
8395
8396         /**
8397          * Sets the visibility of the element (see details). If the visibilityMode is set to Element.DISPLAY, it will use
8398          * the display property to hide the element, otherwise it uses visibility. The default is to hide and show using the visibility property.
8399          * @param {Boolean} visible Whether the element is visible
8400          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8401          * @return {Roo.Element} this
8402          */
8403          setVisible : function(visible, animate){
8404             if(!animate || !A){
8405                 if(this.visibilityMode == El.DISPLAY){
8406                     this.setDisplayed(visible);
8407                 }else{
8408                     this.fixDisplay();
8409                     this.dom.style.visibility = visible ? "visible" : "hidden";
8410                 }
8411             }else{
8412                 // closure for composites
8413                 var dom = this.dom;
8414                 var visMode = this.visibilityMode;
8415                 if(visible){
8416                     this.setOpacity(.01);
8417                     this.setVisible(true);
8418                 }
8419                 this.anim({opacity: { to: (visible?1:0) }},
8420                       this.preanim(arguments, 1),
8421                       null, .35, 'easeIn', function(){
8422                          if(!visible){
8423                              if(visMode == El.DISPLAY){
8424                                  dom.style.display = "none";
8425                              }else{
8426                                  dom.style.visibility = "hidden";
8427                              }
8428                              Roo.get(dom).setOpacity(1);
8429                          }
8430                      });
8431             }
8432             return this;
8433         },
8434
8435         /**
8436          * Returns true if display is not "none"
8437          * @return {Boolean}
8438          */
8439         isDisplayed : function() {
8440             return this.getStyle("display") != "none";
8441         },
8442
8443         /**
8444          * Toggles the element's visibility or display, depending on visibility mode.
8445          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8446          * @return {Roo.Element} this
8447          */
8448         toggle : function(animate){
8449             this.setVisible(!this.isVisible(), this.preanim(arguments, 0));
8450             return this;
8451         },
8452
8453         /**
8454          * Sets the CSS display property. Uses originalDisplay if the specified value is a boolean true.
8455          * @param {Boolean} value Boolean value to display the element using its default display, or a string to set the display directly
8456          * @return {Roo.Element} this
8457          */
8458         setDisplayed : function(value) {
8459             if(typeof value == "boolean"){
8460                value = value ? this.originalDisplay : "none";
8461             }
8462             this.setStyle("display", value);
8463             return this;
8464         },
8465
8466         /**
8467          * Tries to focus the element. Any exceptions are caught and ignored.
8468          * @return {Roo.Element} this
8469          */
8470         focus : function() {
8471             try{
8472                 this.dom.focus();
8473             }catch(e){}
8474             return this;
8475         },
8476
8477         /**
8478          * Tries to blur the element. Any exceptions are caught and ignored.
8479          * @return {Roo.Element} this
8480          */
8481         blur : function() {
8482             try{
8483                 this.dom.blur();
8484             }catch(e){}
8485             return this;
8486         },
8487
8488         /**
8489          * Adds one or more CSS classes to the element. Duplicate classes are automatically filtered out.
8490          * @param {String/Array} className The CSS class to add, or an array of classes
8491          * @return {Roo.Element} this
8492          */
8493         addClass : function(className){
8494             if(className instanceof Array){
8495                 for(var i = 0, len = className.length; i < len; i++) {
8496                     this.addClass(className[i]);
8497                 }
8498             }else{
8499                 if(className && !this.hasClass(className)){
8500                     if (this.dom instanceof SVGElement) {
8501                         this.dom.className.baseVal =this.dom.className.baseVal  + " " + className;
8502                     } else {
8503                         this.dom.className = this.dom.className + " " + className;
8504                     }
8505                 }
8506             }
8507             return this;
8508         },
8509
8510         /**
8511          * Adds one or more CSS classes to this element and removes the same class(es) from all siblings.
8512          * @param {String/Array} className The CSS class to add, or an array of classes
8513          * @return {Roo.Element} this
8514          */
8515         radioClass : function(className){
8516             var siblings = this.dom.parentNode.childNodes;
8517             for(var i = 0; i < siblings.length; i++) {
8518                 var s = siblings[i];
8519                 if(s.nodeType == 1){
8520                     Roo.get(s).removeClass(className);
8521                 }
8522             }
8523             this.addClass(className);
8524             return this;
8525         },
8526
8527         /**
8528          * Removes one or more CSS classes from the element.
8529          * @param {String/Array} className The CSS class to remove, or an array of classes
8530          * @return {Roo.Element} this
8531          */
8532         removeClass : function(className){
8533             
8534             var cn = this.dom instanceof SVGElement ? this.dom.className.baseVal : this.dom.className;
8535             if(!className || !cn){
8536                 return this;
8537             }
8538             if(className instanceof Array){
8539                 for(var i = 0, len = className.length; i < len; i++) {
8540                     this.removeClass(className[i]);
8541                 }
8542             }else{
8543                 if(this.hasClass(className)){
8544                     var re = this.classReCache[className];
8545                     if (!re) {
8546                        re = new RegExp('(?:^|\\s+)' + className + '(?:\\s+|$)', "g");
8547                        this.classReCache[className] = re;
8548                     }
8549                     if (this.dom instanceof SVGElement) {
8550                         this.dom.className.baseVal = cn.replace(re, " ");
8551                     } else {
8552                         this.dom.className = cn.replace(re, " ");
8553                     }
8554                 }
8555             }
8556             return this;
8557         },
8558
8559         // private
8560         classReCache: {},
8561
8562         /**
8563          * Toggles the specified CSS class on this element (removes it if it already exists, otherwise adds it).
8564          * @param {String} className The CSS class to toggle
8565          * @return {Roo.Element} this
8566          */
8567         toggleClass : function(className){
8568             if(this.hasClass(className)){
8569                 this.removeClass(className);
8570             }else{
8571                 this.addClass(className);
8572             }
8573             return this;
8574         },
8575
8576         /**
8577          * Checks if the specified CSS class exists on this element's DOM node.
8578          * @param {String} className The CSS class to check for
8579          * @return {Boolean} True if the class exists, else false
8580          */
8581         hasClass : function(className){
8582             if (this.dom instanceof SVGElement) {
8583                 return className && (' '+this.dom.className.baseVal +' ').indexOf(' '+className+' ') != -1; 
8584             } 
8585             return className && (' '+this.dom.className+' ').indexOf(' '+className+' ') != -1;
8586         },
8587
8588         /**
8589          * Replaces a CSS class on the element with another.  If the old name does not exist, the new name will simply be added.
8590          * @param {String} oldClassName The CSS class to replace
8591          * @param {String} newClassName The replacement CSS class
8592          * @return {Roo.Element} this
8593          */
8594         replaceClass : function(oldClassName, newClassName){
8595             this.removeClass(oldClassName);
8596             this.addClass(newClassName);
8597             return this;
8598         },
8599
8600         /**
8601          * Returns an object with properties matching the styles requested.
8602          * For example, el.getStyles('color', 'font-size', 'width') might return
8603          * {'color': '#FFFFFF', 'font-size': '13px', 'width': '100px'}.
8604          * @param {String} style1 A style name
8605          * @param {String} style2 A style name
8606          * @param {String} etc.
8607          * @return {Object} The style object
8608          */
8609         getStyles : function(){
8610             var a = arguments, len = a.length, r = {};
8611             for(var i = 0; i < len; i++){
8612                 r[a[i]] = this.getStyle(a[i]);
8613             }
8614             return r;
8615         },
8616
8617         /**
8618          * Normalizes currentStyle and computedStyle. This is not YUI getStyle, it is an optimised version.
8619          * @param {String} property The style property whose value is returned.
8620          * @return {String} The current value of the style property for this element.
8621          */
8622         getStyle : function(){
8623             return view && view.getComputedStyle ?
8624                 function(prop){
8625                     var el = this.dom, v, cs, camel;
8626                     if(prop == 'float'){
8627                         prop = "cssFloat";
8628                     }
8629                     if(el.style && (v = el.style[prop])){
8630                         return v;
8631                     }
8632                     if(cs = view.getComputedStyle(el, "")){
8633                         if(!(camel = propCache[prop])){
8634                             camel = propCache[prop] = prop.replace(camelRe, camelFn);
8635                         }
8636                         return cs[camel];
8637                     }
8638                     return null;
8639                 } :
8640                 function(prop){
8641                     var el = this.dom, v, cs, camel;
8642                     if(prop == 'opacity'){
8643                         if(typeof el.style.filter == 'string'){
8644                             var m = el.style.filter.match(/alpha\(opacity=(.*)\)/i);
8645                             if(m){
8646                                 var fv = parseFloat(m[1]);
8647                                 if(!isNaN(fv)){
8648                                     return fv ? fv / 100 : 0;
8649                                 }
8650                             }
8651                         }
8652                         return 1;
8653                     }else if(prop == 'float'){
8654                         prop = "styleFloat";
8655                     }
8656                     if(!(camel = propCache[prop])){
8657                         camel = propCache[prop] = prop.replace(camelRe, camelFn);
8658                     }
8659                     if(v = el.style[camel]){
8660                         return v;
8661                     }
8662                     if(cs = el.currentStyle){
8663                         return cs[camel];
8664                     }
8665                     return null;
8666                 };
8667         }(),
8668
8669         /**
8670          * Wrapper for setting style properties, also takes single object parameter of multiple styles.
8671          * @param {String/Object} property The style property to be set, or an object of multiple styles.
8672          * @param {String} value (optional) The value to apply to the given property, or null if an object was passed.
8673          * @return {Roo.Element} this
8674          */
8675         setStyle : function(prop, value){
8676             if(typeof prop == "string"){
8677                 
8678                 if (prop == 'float') {
8679                     this.setStyle(Roo.isIE ? 'styleFloat'  : 'cssFloat', value);
8680                     return this;
8681                 }
8682                 
8683                 var camel;
8684                 if(!(camel = propCache[prop])){
8685                     camel = propCache[prop] = prop.replace(camelRe, camelFn);
8686                 }
8687                 
8688                 if(camel == 'opacity') {
8689                     this.setOpacity(value);
8690                 }else{
8691                     this.dom.style[camel] = value;
8692                 }
8693             }else{
8694                 for(var style in prop){
8695                     if(typeof prop[style] != "function"){
8696                        this.setStyle(style, prop[style]);
8697                     }
8698                 }
8699             }
8700             return this;
8701         },
8702
8703         /**
8704          * More flexible version of {@link #setStyle} for setting style properties.
8705          * @param {String/Object/Function} styles A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
8706          * a function which returns such a specification.
8707          * @return {Roo.Element} this
8708          */
8709         applyStyles : function(style){
8710             Roo.DomHelper.applyStyles(this.dom, style);
8711             return this;
8712         },
8713
8714         /**
8715           * 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).
8716           * @return {Number} The X position of the element
8717           */
8718         getX : function(){
8719             return D.getX(this.dom);
8720         },
8721
8722         /**
8723           * 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).
8724           * @return {Number} The Y position of the element
8725           */
8726         getY : function(){
8727             return D.getY(this.dom);
8728         },
8729
8730         /**
8731           * 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).
8732           * @return {Array} The XY position of the element
8733           */
8734         getXY : function(){
8735             return D.getXY(this.dom);
8736         },
8737
8738         /**
8739          * 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).
8740          * @param {Number} The X position of the element
8741          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8742          * @return {Roo.Element} this
8743          */
8744         setX : function(x, animate){
8745             if(!animate || !A){
8746                 D.setX(this.dom, x);
8747             }else{
8748                 this.setXY([x, this.getY()], this.preanim(arguments, 1));
8749             }
8750             return this;
8751         },
8752
8753         /**
8754          * 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).
8755          * @param {Number} The Y position of the element
8756          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8757          * @return {Roo.Element} this
8758          */
8759         setY : function(y, animate){
8760             if(!animate || !A){
8761                 D.setY(this.dom, y);
8762             }else{
8763                 this.setXY([this.getX(), y], this.preanim(arguments, 1));
8764             }
8765             return this;
8766         },
8767
8768         /**
8769          * Sets the element's left position directly using CSS style (instead of {@link #setX}).
8770          * @param {String} left The left CSS property value
8771          * @return {Roo.Element} this
8772          */
8773         setLeft : function(left){
8774             this.setStyle("left", this.addUnits(left));
8775             return this;
8776         },
8777
8778         /**
8779          * Sets the element's top position directly using CSS style (instead of {@link #setY}).
8780          * @param {String} top The top CSS property value
8781          * @return {Roo.Element} this
8782          */
8783         setTop : function(top){
8784             this.setStyle("top", this.addUnits(top));
8785             return this;
8786         },
8787
8788         /**
8789          * Sets the element's CSS right style.
8790          * @param {String} right The right CSS property value
8791          * @return {Roo.Element} this
8792          */
8793         setRight : function(right){
8794             this.setStyle("right", this.addUnits(right));
8795             return this;
8796         },
8797
8798         /**
8799          * Sets the element's CSS bottom style.
8800          * @param {String} bottom The bottom CSS property value
8801          * @return {Roo.Element} this
8802          */
8803         setBottom : function(bottom){
8804             this.setStyle("bottom", this.addUnits(bottom));
8805             return this;
8806         },
8807
8808         /**
8809          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8810          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8811          * @param {Array} pos Contains X & Y [x, y] values for new position (coordinates are page-based)
8812          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8813          * @return {Roo.Element} this
8814          */
8815         setXY : function(pos, animate){
8816             if(!animate || !A){
8817                 D.setXY(this.dom, pos);
8818             }else{
8819                 this.anim({points: {to: pos}}, this.preanim(arguments, 1), 'motion');
8820             }
8821             return this;
8822         },
8823
8824         /**
8825          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8826          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8827          * @param {Number} x X value for new position (coordinates are page-based)
8828          * @param {Number} y Y value for new position (coordinates are page-based)
8829          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8830          * @return {Roo.Element} this
8831          */
8832         setLocation : function(x, y, animate){
8833             this.setXY([x, y], this.preanim(arguments, 2));
8834             return this;
8835         },
8836
8837         /**
8838          * Sets the position of the element in page coordinates, regardless of how the element is positioned.
8839          * The element must be part of the DOM tree to have page coordinates (display:none or elements not appended return false).
8840          * @param {Number} x X value for new position (coordinates are page-based)
8841          * @param {Number} y Y value for new position (coordinates are page-based)
8842          * @param {Boolean/Object} animate (optional) True for the default animation, or a standard Element animation config object
8843          * @return {Roo.Element} this
8844          */
8845         moveTo : function(x, y, animate){
8846             this.setXY([x, y], this.preanim(arguments, 2));
8847             return this;
8848         },
8849
8850         /**
8851          * Returns the region of the given element.
8852          * The element must be part of the DOM tree to have a region (display:none or elements not appended return false).
8853          * @return {Region} A Roo.lib.Region containing "top, left, bottom, right" member data.
8854          */
8855         getRegion : function(){
8856             return D.getRegion(this.dom);
8857         },
8858
8859         /**
8860          * Returns the offset height of the element
8861          * @param {Boolean} contentHeight (optional) true to get the height minus borders and padding
8862          * @return {Number} The element's height
8863          */
8864         getHeight : function(contentHeight){
8865             var h = this.dom.offsetHeight || 0;
8866             return contentHeight !== true ? h : h-this.getBorderWidth("tb")-this.getPadding("tb");
8867         },
8868
8869         /**
8870          * Returns the offset width of the element
8871          * @param {Boolean} contentWidth (optional) true to get the width minus borders and padding
8872          * @return {Number} The element's width
8873          */
8874         getWidth : function(contentWidth){
8875             var w = this.dom.offsetWidth || 0;
8876             return contentWidth !== true ? w : w-this.getBorderWidth("lr")-this.getPadding("lr");
8877         },
8878
8879         /**
8880          * Returns either the offsetHeight or the height of this element based on CSS height adjusted by padding or borders
8881          * when needed to simulate offsetHeight when offsets aren't available. This may not work on display:none elements
8882          * if a height has not been set using CSS.
8883          * @return {Number}
8884          */
8885         getComputedHeight : function(){
8886             var h = Math.max(this.dom.offsetHeight, this.dom.clientHeight);
8887             if(!h){
8888                 h = parseInt(this.getStyle('height'), 10) || 0;
8889                 if(!this.isBorderBox()){
8890                     h += this.getFrameWidth('tb');
8891                 }
8892             }
8893             return h;
8894         },
8895
8896         /**
8897          * Returns either the offsetWidth or the width of this element based on CSS width adjusted by padding or borders
8898          * when needed to simulate offsetWidth when offsets aren't available. This may not work on display:none elements
8899          * if a width has not been set using CSS.
8900          * @return {Number}
8901          */
8902         getComputedWidth : function(){
8903             var w = Math.max(this.dom.offsetWidth, this.dom.clientWidth);
8904             if(!w){
8905                 w = parseInt(this.getStyle('width'), 10) || 0;
8906                 if(!this.isBorderBox()){
8907                     w += this.getFrameWidth('lr');
8908                 }
8909             }
8910             return w;
8911         },
8912
8913         /**
8914          * Returns the size of the element.
8915          * @param {Boolean} contentSize (optional) true to get the width/size minus borders and padding
8916          * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
8917          */
8918         getSize : function(contentSize){
8919             return {width: this.getWidth(contentSize), height: this.getHeight(contentSize)};
8920         },
8921
8922         /**
8923          * Returns the width and height of the viewport.
8924          * @return {Object} An object containing the viewport's size {width: (viewport width), height: (viewport height)}
8925          */
8926         getViewSize : function(){
8927             var d = this.dom, doc = document, aw = 0, ah = 0;
8928             if(d == doc || d == doc.body){
8929                 return {width : D.getViewWidth(), height: D.getViewHeight()};
8930             }else{
8931                 return {
8932                     width : d.clientWidth,
8933                     height: d.clientHeight
8934                 };
8935             }
8936         },
8937
8938         /**
8939          * Returns the value of the "value" attribute
8940          * @param {Boolean} asNumber true to parse the value as a number
8941          * @return {String/Number}
8942          */
8943         getValue : function(asNumber){
8944             return asNumber ? parseInt(this.dom.value, 10) : this.dom.value;
8945         },
8946
8947         // private
8948         adjustWidth : function(width){
8949             if(typeof width == "number"){
8950                 if(this.autoBoxAdjust && !this.isBorderBox()){
8951                    width -= (this.getBorderWidth("lr") + this.getPadding("lr"));
8952                 }
8953                 if(width < 0){
8954                     width = 0;
8955                 }
8956             }
8957             return width;
8958         },
8959
8960         // private
8961         adjustHeight : function(height){
8962             if(typeof height == "number"){
8963                if(this.autoBoxAdjust && !this.isBorderBox()){
8964                    height -= (this.getBorderWidth("tb") + this.getPadding("tb"));
8965                }
8966                if(height < 0){
8967                    height = 0;
8968                }
8969             }
8970             return height;
8971         },
8972
8973         /**
8974          * Set the width of the element
8975          * @param {Number} width The new width
8976          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8977          * @return {Roo.Element} this
8978          */
8979         setWidth : function(width, animate){
8980             width = this.adjustWidth(width);
8981             if(!animate || !A){
8982                 this.dom.style.width = this.addUnits(width);
8983             }else{
8984                 this.anim({width: {to: width}}, this.preanim(arguments, 1));
8985             }
8986             return this;
8987         },
8988
8989         /**
8990          * Set the height of the element
8991          * @param {Number} height The new height
8992          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
8993          * @return {Roo.Element} this
8994          */
8995          setHeight : function(height, animate){
8996             height = this.adjustHeight(height);
8997             if(!animate || !A){
8998                 this.dom.style.height = this.addUnits(height);
8999             }else{
9000                 this.anim({height: {to: height}}, this.preanim(arguments, 1));
9001             }
9002             return this;
9003         },
9004
9005         /**
9006          * Set the size of the element. If animation is true, both width an height will be animated concurrently.
9007          * @param {Number} width The new width
9008          * @param {Number} height The new height
9009          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9010          * @return {Roo.Element} this
9011          */
9012          setSize : function(width, height, animate){
9013             if(typeof width == "object"){ // in case of object from getSize()
9014                 height = width.height; width = width.width;
9015             }
9016             width = this.adjustWidth(width); height = this.adjustHeight(height);
9017             if(!animate || !A){
9018                 this.dom.style.width = this.addUnits(width);
9019                 this.dom.style.height = this.addUnits(height);
9020             }else{
9021                 this.anim({width: {to: width}, height: {to: height}}, this.preanim(arguments, 2));
9022             }
9023             return this;
9024         },
9025
9026         /**
9027          * Sets the element's position and size in one shot. If animation is true then width, height, x and y will be animated concurrently.
9028          * @param {Number} x X value for new position (coordinates are page-based)
9029          * @param {Number} y Y value for new position (coordinates are page-based)
9030          * @param {Number} width The new width
9031          * @param {Number} height The new height
9032          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9033          * @return {Roo.Element} this
9034          */
9035         setBounds : function(x, y, width, height, animate){
9036             if(!animate || !A){
9037                 this.setSize(width, height);
9038                 this.setLocation(x, y);
9039             }else{
9040                 width = this.adjustWidth(width); height = this.adjustHeight(height);
9041                 this.anim({points: {to: [x, y]}, width: {to: width}, height: {to: height}},
9042                               this.preanim(arguments, 4), 'motion');
9043             }
9044             return this;
9045         },
9046
9047         /**
9048          * 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.
9049          * @param {Roo.lib.Region} region The region to fill
9050          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9051          * @return {Roo.Element} this
9052          */
9053         setRegion : function(region, animate){
9054             this.setBounds(region.left, region.top, region.right-region.left, region.bottom-region.top, this.preanim(arguments, 1));
9055             return this;
9056         },
9057
9058         /**
9059          * Appends an event handler
9060          *
9061          * @param {String}   eventName     The type of event to append
9062          * @param {Function} fn        The method the event invokes
9063          * @param {Object} scope       (optional) The scope (this object) of the fn
9064          * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
9065          */
9066         addListener : function(eventName, fn, scope, options)
9067         {
9068             if (eventName == 'dblclick') { // doublclick (touchstart) - faked on touch.
9069                 this.addListener('touchstart', this.onTapHandler, this);
9070             }
9071             
9072             // we need to handle a special case where dom element is a svg element.
9073             // in this case we do not actua
9074             if (!this.dom) {
9075                 return;
9076             }
9077             
9078             if (this.dom instanceof SVGElement && !(this.dom instanceof SVGSVGElement)) {
9079                 if (typeof(this.listeners[eventName]) == 'undefined') {
9080                     this.listeners[eventName] =  new Roo.util.Event(this, eventName);
9081                 }
9082                 this.listeners[eventName].addListener(fn, scope, options);
9083                 return;
9084             }
9085             
9086                 
9087             Roo.EventManager.on(this.dom,  eventName, fn, scope || this, options);
9088             
9089             
9090         },
9091         tapedTwice : false,
9092         onTapHandler : function(event)
9093         {
9094             if(!this.tapedTwice) {
9095                 this.tapedTwice = true;
9096                 var s = this;
9097                 setTimeout( function() {
9098                     s.tapedTwice = false;
9099                 }, 300 );
9100                 return;
9101             }
9102             event.preventDefault();
9103             var revent = new MouseEvent('dblclick',  {
9104                 view: window,
9105                 bubbles: true,
9106                 cancelable: true
9107             });
9108              
9109             this.dom.dispatchEvent(revent);
9110             //action on double tap goes below
9111              
9112         }, 
9113  
9114         /**
9115          * Removes an event handler from this element
9116          * @param {String} eventName the type of event to remove
9117          * @param {Function} fn the method the event invokes
9118          * @param {Function} scope (needed for svg fake listeners)
9119          * @return {Roo.Element} this
9120          */
9121         removeListener : function(eventName, fn, scope){
9122             Roo.EventManager.removeListener(this.dom,  eventName, fn);
9123             if (typeof(this.listeners) == 'undefined'  || typeof(this.listeners[eventName]) == 'undefined') {
9124                 return this;
9125             }
9126             this.listeners[eventName].removeListener(fn, scope);
9127             return this;
9128         },
9129
9130         /**
9131          * Removes all previous added listeners from this element
9132          * @return {Roo.Element} this
9133          */
9134         removeAllListeners : function(){
9135             E.purgeElement(this.dom);
9136             this.listeners = {};
9137             return this;
9138         },
9139
9140         relayEvent : function(eventName, observable){
9141             this.on(eventName, function(e){
9142                 observable.fireEvent(eventName, e);
9143             });
9144         },
9145
9146         
9147         /**
9148          * Set the opacity of the element
9149          * @param {Float} opacity The new opacity. 0 = transparent, .5 = 50% visibile, 1 = fully visible, etc
9150          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9151          * @return {Roo.Element} this
9152          */
9153          setOpacity : function(opacity, animate){
9154             if(!animate || !A){
9155                 var s = this.dom.style;
9156                 if(Roo.isIE){
9157                     s.zoom = 1;
9158                     s.filter = (s.filter || '').replace(/alpha\([^\)]*\)/gi,"") +
9159                                (opacity == 1 ? "" : "alpha(opacity=" + opacity * 100 + ")");
9160                 }else{
9161                     s.opacity = opacity;
9162                 }
9163             }else{
9164                 this.anim({opacity: {to: opacity}}, this.preanim(arguments, 1), null, .35, 'easeIn');
9165             }
9166             return this;
9167         },
9168
9169         /**
9170          * Gets the left X coordinate
9171          * @param {Boolean} local True to get the local css position instead of page coordinate
9172          * @return {Number}
9173          */
9174         getLeft : function(local){
9175             if(!local){
9176                 return this.getX();
9177             }else{
9178                 return parseInt(this.getStyle("left"), 10) || 0;
9179             }
9180         },
9181
9182         /**
9183          * Gets the right X coordinate of the element (element X position + element width)
9184          * @param {Boolean} local True to get the local css position instead of page coordinate
9185          * @return {Number}
9186          */
9187         getRight : function(local){
9188             if(!local){
9189                 return this.getX() + this.getWidth();
9190             }else{
9191                 return (this.getLeft(true) + this.getWidth()) || 0;
9192             }
9193         },
9194
9195         /**
9196          * Gets the top Y coordinate
9197          * @param {Boolean} local True to get the local css position instead of page coordinate
9198          * @return {Number}
9199          */
9200         getTop : function(local) {
9201             if(!local){
9202                 return this.getY();
9203             }else{
9204                 return parseInt(this.getStyle("top"), 10) || 0;
9205             }
9206         },
9207
9208         /**
9209          * Gets the bottom Y coordinate of the element (element Y position + element height)
9210          * @param {Boolean} local True to get the local css position instead of page coordinate
9211          * @return {Number}
9212          */
9213         getBottom : function(local){
9214             if(!local){
9215                 return this.getY() + this.getHeight();
9216             }else{
9217                 return (this.getTop(true) + this.getHeight()) || 0;
9218             }
9219         },
9220
9221         /**
9222         * Initializes positioning on this element. If a desired position is not passed, it will make the
9223         * the element positioned relative IF it is not already positioned.
9224         * @param {String} pos (optional) Positioning to use "relative", "absolute" or "fixed"
9225         * @param {Number} zIndex (optional) The zIndex to apply
9226         * @param {Number} x (optional) Set the page X position
9227         * @param {Number} y (optional) Set the page Y position
9228         */
9229         position : function(pos, zIndex, x, y){
9230             if(!pos){
9231                if(this.getStyle('position') == 'static'){
9232                    this.setStyle('position', 'relative');
9233                }
9234             }else{
9235                 this.setStyle("position", pos);
9236             }
9237             if(zIndex){
9238                 this.setStyle("z-index", zIndex);
9239             }
9240             if(x !== undefined && y !== undefined){
9241                 this.setXY([x, y]);
9242             }else if(x !== undefined){
9243                 this.setX(x);
9244             }else if(y !== undefined){
9245                 this.setY(y);
9246             }
9247         },
9248
9249         /**
9250         * Clear positioning back to the default when the document was loaded
9251         * @param {String} value (optional) The value to use for the left,right,top,bottom, defaults to '' (empty string). You could use 'auto'.
9252         * @return {Roo.Element} this
9253          */
9254         clearPositioning : function(value){
9255             value = value ||'';
9256             this.setStyle({
9257                 "left": value,
9258                 "right": value,
9259                 "top": value,
9260                 "bottom": value,
9261                 "z-index": "",
9262                 "position" : "static"
9263             });
9264             return this;
9265         },
9266
9267         /**
9268         * Gets an object with all CSS positioning properties. Useful along with setPostioning to get
9269         * snapshot before performing an update and then restoring the element.
9270         * @return {Object}
9271         */
9272         getPositioning : function(){
9273             var l = this.getStyle("left");
9274             var t = this.getStyle("top");
9275             return {
9276                 "position" : this.getStyle("position"),
9277                 "left" : l,
9278                 "right" : l ? "" : this.getStyle("right"),
9279                 "top" : t,
9280                 "bottom" : t ? "" : this.getStyle("bottom"),
9281                 "z-index" : this.getStyle("z-index")
9282             };
9283         },
9284
9285         /**
9286          * Gets the width of the border(s) for the specified side(s)
9287          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9288          * passing lr would get the border (l)eft width + the border (r)ight width.
9289          * @return {Number} The width of the sides passed added together
9290          */
9291         getBorderWidth : function(side){
9292             return this.addStyles(side, El.borders);
9293         },
9294
9295         /**
9296          * Gets the width of the padding(s) for the specified side(s)
9297          * @param {String} side Can be t, l, r, b or any combination of those to add multiple values. For example,
9298          * passing lr would get the padding (l)eft + the padding (r)ight.
9299          * @return {Number} The padding of the sides passed added together
9300          */
9301         getPadding : function(side){
9302             return this.addStyles(side, El.paddings);
9303         },
9304
9305         /**
9306         * Set positioning with an object returned by getPositioning().
9307         * @param {Object} posCfg
9308         * @return {Roo.Element} this
9309          */
9310         setPositioning : function(pc){
9311             this.applyStyles(pc);
9312             if(pc.right == "auto"){
9313                 this.dom.style.right = "";
9314             }
9315             if(pc.bottom == "auto"){
9316                 this.dom.style.bottom = "";
9317             }
9318             return this;
9319         },
9320
9321         // private
9322         fixDisplay : function(){
9323             if(this.getStyle("display") == "none"){
9324                 this.setStyle("visibility", "hidden");
9325                 this.setStyle("display", this.originalDisplay); // first try reverting to default
9326                 if(this.getStyle("display") == "none"){ // if that fails, default to block
9327                     this.setStyle("display", "block");
9328                 }
9329             }
9330         },
9331
9332         /**
9333          * Quick set left and top adding default units
9334          * @param {String} left The left CSS property value
9335          * @param {String} top The top CSS property value
9336          * @return {Roo.Element} this
9337          */
9338          setLeftTop : function(left, top){
9339             this.dom.style.left = this.addUnits(left);
9340             this.dom.style.top = this.addUnits(top);
9341             return this;
9342         },
9343
9344         /**
9345          * Move this element relative to its current position.
9346          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
9347          * @param {Number} distance How far to move the element in pixels
9348          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9349          * @return {Roo.Element} this
9350          */
9351          move : function(direction, distance, animate){
9352             var xy = this.getXY();
9353             direction = direction.toLowerCase();
9354             switch(direction){
9355                 case "l":
9356                 case "left":
9357                     this.moveTo(xy[0]-distance, xy[1], this.preanim(arguments, 2));
9358                     break;
9359                case "r":
9360                case "right":
9361                     this.moveTo(xy[0]+distance, xy[1], this.preanim(arguments, 2));
9362                     break;
9363                case "t":
9364                case "top":
9365                case "up":
9366                     this.moveTo(xy[0], xy[1]-distance, this.preanim(arguments, 2));
9367                     break;
9368                case "b":
9369                case "bottom":
9370                case "down":
9371                     this.moveTo(xy[0], xy[1]+distance, this.preanim(arguments, 2));
9372                     break;
9373             }
9374             return this;
9375         },
9376
9377         /**
9378          *  Store the current overflow setting and clip overflow on the element - use {@link #unclip} to remove
9379          * @return {Roo.Element} this
9380          */
9381         clip : function(){
9382             if(!this.isClipped){
9383                this.isClipped = true;
9384                this.originalClip = {
9385                    "o": this.getStyle("overflow"),
9386                    "x": this.getStyle("overflow-x"),
9387                    "y": this.getStyle("overflow-y")
9388                };
9389                this.setStyle("overflow", "hidden");
9390                this.setStyle("overflow-x", "hidden");
9391                this.setStyle("overflow-y", "hidden");
9392             }
9393             return this;
9394         },
9395
9396         /**
9397          *  Return clipping (overflow) to original clipping before clip() was called
9398          * @return {Roo.Element} this
9399          */
9400         unclip : function(){
9401             if(this.isClipped){
9402                 this.isClipped = false;
9403                 var o = this.originalClip;
9404                 if(o.o){this.setStyle("overflow", o.o);}
9405                 if(o.x){this.setStyle("overflow-x", o.x);}
9406                 if(o.y){this.setStyle("overflow-y", o.y);}
9407             }
9408             return this;
9409         },
9410
9411
9412         /**
9413          * Gets the x,y coordinates specified by the anchor position on the element.
9414          * @param {String} anchor (optional) The specified anchor position (defaults to "c").  See {@link #alignTo} for details on supported anchor positions.
9415          * @param {Object} size (optional) An object containing the size to use for calculating anchor position
9416          *                       {width: (target width), height: (target height)} (defaults to the element's current size)
9417          * @param {Boolean} local (optional) True to get the local (element top/left-relative) anchor position instead of page coordinates
9418          * @return {Array} [x, y] An array containing the element's x and y coordinates
9419          */
9420         getAnchorXY : function(anchor, local, s){
9421             //Passing a different size is useful for pre-calculating anchors,
9422             //especially for anchored animations that change the el size.
9423
9424             var w, h, vp = false;
9425             if(!s){
9426                 var d = this.dom;
9427                 if(d == document.body || d == document){
9428                     vp = true;
9429                     w = D.getViewWidth(); h = D.getViewHeight();
9430                 }else{
9431                     w = this.getWidth(); h = this.getHeight();
9432                 }
9433             }else{
9434                 w = s.width;  h = s.height;
9435             }
9436             var x = 0, y = 0, r = Math.round;
9437             switch((anchor || "tl").toLowerCase()){
9438                 case "c":
9439                     x = r(w*.5);
9440                     y = r(h*.5);
9441                 break;
9442                 case "t":
9443                     x = r(w*.5);
9444                     y = 0;
9445                 break;
9446                 case "l":
9447                     x = 0;
9448                     y = r(h*.5);
9449                 break;
9450                 case "r":
9451                     x = w;
9452                     y = r(h*.5);
9453                 break;
9454                 case "b":
9455                     x = r(w*.5);
9456                     y = h;
9457                 break;
9458                 case "tl":
9459                     x = 0;
9460                     y = 0;
9461                 break;
9462                 case "bl":
9463                     x = 0;
9464                     y = h;
9465                 break;
9466                 case "br":
9467                     x = w;
9468                     y = h;
9469                 break;
9470                 case "tr":
9471                     x = w;
9472                     y = 0;
9473                 break;
9474             }
9475             if(local === true){
9476                 return [x, y];
9477             }
9478             if(vp){
9479                 var sc = this.getScroll();
9480                 return [x + sc.left, y + sc.top];
9481             }
9482             //Add the element's offset xy
9483             var o = this.getXY();
9484             return [x+o[0], y+o[1]];
9485         },
9486
9487         /**
9488          * Gets the x,y coordinates to align this element with another element. See {@link #alignTo} for more info on the
9489          * supported position values.
9490          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9491          * @param {String} position The position to align to.
9492          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9493          * @return {Array} [x, y]
9494          */
9495         getAlignToXY : function(el, p, o)
9496         {
9497             el = Roo.get(el);
9498             var d = this.dom;
9499             if(!el.dom){
9500                 throw "Element.alignTo with an element that doesn't exist";
9501             }
9502             var c = false; //constrain to viewport
9503             var p1 = "", p2 = "";
9504             o = o || [0,0];
9505
9506             if(!p){
9507                 p = "tl-bl";
9508             }else if(p == "?"){
9509                 p = "tl-bl?";
9510             }else if(p.indexOf("-") == -1){
9511                 p = "tl-" + p;
9512             }
9513             p = p.toLowerCase();
9514             var m = p.match(/^([a-z]+)-([a-z]+)(\?)?$/);
9515             if(!m){
9516                throw "Element.alignTo with an invalid alignment " + p;
9517             }
9518             p1 = m[1]; p2 = m[2]; c = !!m[3];
9519
9520             //Subtract the aligned el's internal xy from the target's offset xy
9521             //plus custom offset to get the aligned el's new offset xy
9522             var a1 = this.getAnchorXY(p1, true);
9523             var a2 = el.getAnchorXY(p2, false);
9524             var x = a2[0] - a1[0] + o[0];
9525             var y = a2[1] - a1[1] + o[1];
9526             if(c){
9527                 //constrain the aligned el to viewport if necessary
9528                 var w = this.getWidth(), h = this.getHeight(), r = el.getRegion();
9529                 // 5px of margin for ie
9530                 var dw = D.getViewWidth()-5, dh = D.getViewHeight()-5;
9531
9532                 //If we are at a viewport boundary and the aligned el is anchored on a target border that is
9533                 //perpendicular to the vp border, allow the aligned el to slide on that border,
9534                 //otherwise swap the aligned el to the opposite border of the target.
9535                 var p1y = p1.charAt(0), p1x = p1.charAt(p1.length-1);
9536                var p2y = p2.charAt(0), p2x = p2.charAt(p2.length-1);
9537                var swapY = ((p1y=="t" && p2y=="b") || (p1y=="b" && p2y=="t")  );
9538                var swapX = ((p1x=="r" && p2x=="l") || (p1x=="l" && p2x=="r"));
9539
9540                var doc = document;
9541                var scrollX = (doc.documentElement.scrollLeft || doc.body.scrollLeft || 0)+5;
9542                var scrollY = (doc.documentElement.scrollTop || doc.body.scrollTop || 0)+5;
9543
9544                if((x+w) > dw + scrollX){
9545                     x = swapX ? r.left-w : dw+scrollX-w;
9546                 }
9547                if(x < scrollX){
9548                    x = swapX ? r.right : scrollX;
9549                }
9550                if((y+h) > dh + scrollY){
9551                     y = swapY ? r.top-h : dh+scrollY-h;
9552                 }
9553                if (y < scrollY){
9554                    y = swapY ? r.bottom : scrollY;
9555                }
9556             }
9557             return [x,y];
9558         },
9559
9560         // private
9561         getConstrainToXY : function(){
9562             var os = {top:0, left:0, bottom:0, right: 0};
9563
9564             return function(el, local, offsets, proposedXY){
9565                 el = Roo.get(el);
9566                 offsets = offsets ? Roo.applyIf(offsets, os) : os;
9567
9568                 var vw, vh, vx = 0, vy = 0;
9569                 if(el.dom == document.body || el.dom == document){
9570                     vw = Roo.lib.Dom.getViewWidth();
9571                     vh = Roo.lib.Dom.getViewHeight();
9572                 }else{
9573                     vw = el.dom.clientWidth;
9574                     vh = el.dom.clientHeight;
9575                     if(!local){
9576                         var vxy = el.getXY();
9577                         vx = vxy[0];
9578                         vy = vxy[1];
9579                     }
9580                 }
9581
9582                 var s = el.getScroll();
9583
9584                 vx += offsets.left + s.left;
9585                 vy += offsets.top + s.top;
9586
9587                 vw -= offsets.right;
9588                 vh -= offsets.bottom;
9589
9590                 var vr = vx+vw;
9591                 var vb = vy+vh;
9592
9593                 var xy = proposedXY || (!local ? this.getXY() : [this.getLeft(true), this.getTop(true)]);
9594                 var x = xy[0], y = xy[1];
9595                 var w = this.dom.offsetWidth, h = this.dom.offsetHeight;
9596
9597                 // only move it if it needs it
9598                 var moved = false;
9599
9600                 // first validate right/bottom
9601                 if((x + w) > vr){
9602                     x = vr - w;
9603                     moved = true;
9604                 }
9605                 if((y + h) > vb){
9606                     y = vb - h;
9607                     moved = true;
9608                 }
9609                 // then make sure top/left isn't negative
9610                 if(x < vx){
9611                     x = vx;
9612                     moved = true;
9613                 }
9614                 if(y < vy){
9615                     y = vy;
9616                     moved = true;
9617                 }
9618                 return moved ? [x, y] : false;
9619             };
9620         }(),
9621
9622         // private
9623         adjustForConstraints : function(xy, parent, offsets){
9624             return this.getConstrainToXY(parent || document, false, offsets, xy) ||  xy;
9625         },
9626
9627         /**
9628          * Aligns this element with another element relative to the specified anchor points. If the other element is the
9629          * document it aligns it to the viewport.
9630          * The position parameter is optional, and can be specified in any one of the following formats:
9631          * <ul>
9632          *   <li><b>Blank</b>: Defaults to aligning the element's top-left corner to the target's bottom-left corner ("tl-bl").</li>
9633          *   <li><b>One anchor (deprecated)</b>: The passed anchor position is used as the target element's anchor point.
9634          *       The element being aligned will position its top-left corner (tl) to that point.  <i>This method has been
9635          *       deprecated in favor of the newer two anchor syntax below</i>.</li>
9636          *   <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
9637          *       element's anchor point, and the second value is used as the target's anchor point.</li>
9638          * </ul>
9639          * In addition to the anchor points, the position parameter also supports the "?" character.  If "?" is passed at the end of
9640          * the position string, the element will attempt to align as specified, but the position will be adjusted to constrain to
9641          * the viewport if necessary.  Note that the element being aligned might be swapped to align to a different position than
9642          * that specified in order to enforce the viewport constraints.
9643          * Following are all of the supported anchor positions:
9644     <pre>
9645     Value  Description
9646     -----  -----------------------------
9647     tl     The top left corner (default)
9648     t      The center of the top edge
9649     tr     The top right corner
9650     l      The center of the left edge
9651     c      In the center of the element
9652     r      The center of the right edge
9653     bl     The bottom left corner
9654     b      The center of the bottom edge
9655     br     The bottom right corner
9656     </pre>
9657     Example Usage:
9658     <pre><code>
9659     // align el to other-el using the default positioning ("tl-bl", non-constrained)
9660     el.alignTo("other-el");
9661
9662     // align the top left corner of el with the top right corner of other-el (constrained to viewport)
9663     el.alignTo("other-el", "tr?");
9664
9665     // align the bottom right corner of el with the center left edge of other-el
9666     el.alignTo("other-el", "br-l?");
9667
9668     // align the center of el with the bottom left corner of other-el and
9669     // adjust the x position by -6 pixels (and the y position by 0)
9670     el.alignTo("other-el", "c-bl", [-6, 0]);
9671     </code></pre>
9672          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9673          * @param {String} position The position to align to.
9674          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9675          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9676          * @return {Roo.Element} this
9677          */
9678         alignTo : function(element, position, offsets, animate){
9679             var xy = this.getAlignToXY(element, position, offsets);
9680             this.setXY(xy, this.preanim(arguments, 3));
9681             return this;
9682         },
9683
9684         /**
9685          * Anchors an element to another element and realigns it when the window is resized.
9686          * @param {String/HTMLElement/Roo.Element} element The element to align to.
9687          * @param {String} position The position to align to.
9688          * @param {Array} offsets (optional) Offset the positioning by [x, y]
9689          * @param {Boolean/Object} animate (optional) True for the default animation or a standard Element animation config object
9690          * @param {Boolean/Number} monitorScroll (optional) True to monitor body scroll and reposition. If this parameter
9691          * is a number, it is used as the buffer delay (defaults to 50ms).
9692          * @param {Function} callback The function to call after the animation finishes
9693          * @return {Roo.Element} this
9694          */
9695         anchorTo : function(el, alignment, offsets, animate, monitorScroll, callback){
9696             var action = function(){
9697                 this.alignTo(el, alignment, offsets, animate);
9698                 Roo.callback(callback, this);
9699             };
9700             Roo.EventManager.onWindowResize(action, this);
9701             var tm = typeof monitorScroll;
9702             if(tm != 'undefined'){
9703                 Roo.EventManager.on(window, 'scroll', action, this,
9704                     {buffer: tm == 'number' ? monitorScroll : 50});
9705             }
9706             action.call(this); // align immediately
9707             return this;
9708         },
9709         /**
9710          * Clears any opacity settings from this element. Required in some cases for IE.
9711          * @return {Roo.Element} this
9712          */
9713         clearOpacity : function(){
9714             if (window.ActiveXObject) {
9715                 if(typeof this.dom.style.filter == 'string' && (/alpha/i).test(this.dom.style.filter)){
9716                     this.dom.style.filter = "";
9717                 }
9718             } else {
9719                 this.dom.style.opacity = "";
9720                 this.dom.style["-moz-opacity"] = "";
9721                 this.dom.style["-khtml-opacity"] = "";
9722             }
9723             return this;
9724         },
9725
9726         /**
9727          * Hide this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9728          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9729          * @return {Roo.Element} this
9730          */
9731         hide : function(animate){
9732             this.setVisible(false, this.preanim(arguments, 0));
9733             return this;
9734         },
9735
9736         /**
9737         * Show this element - Uses display mode to determine whether to use "display" or "visibility". See {@link #setVisible}.
9738         * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9739          * @return {Roo.Element} this
9740          */
9741         show : function(animate){
9742             this.setVisible(true, this.preanim(arguments, 0));
9743             return this;
9744         },
9745
9746         /**
9747          * @private Test if size has a unit, otherwise appends the default
9748          */
9749         addUnits : function(size){
9750             return Roo.Element.addUnits(size, this.defaultUnit);
9751         },
9752
9753         /**
9754          * Temporarily enables offsets (width,height,x,y) for an element with display:none, use endMeasure() when done.
9755          * @return {Roo.Element} this
9756          */
9757         beginMeasure : function(){
9758             var el = this.dom;
9759             if(el.offsetWidth || el.offsetHeight){
9760                 return this; // offsets work already
9761             }
9762             var changed = [];
9763             var p = this.dom, b = document.body; // start with this element
9764             while((!el.offsetWidth && !el.offsetHeight) && p && p.tagName && p != b){
9765                 var pe = Roo.get(p);
9766                 if(pe.getStyle('display') == 'none'){
9767                     changed.push({el: p, visibility: pe.getStyle("visibility")});
9768                     p.style.visibility = "hidden";
9769                     p.style.display = "block";
9770                 }
9771                 p = p.parentNode;
9772             }
9773             this._measureChanged = changed;
9774             return this;
9775
9776         },
9777
9778         /**
9779          * Restores displays to before beginMeasure was called
9780          * @return {Roo.Element} this
9781          */
9782         endMeasure : function(){
9783             var changed = this._measureChanged;
9784             if(changed){
9785                 for(var i = 0, len = changed.length; i < len; i++) {
9786                     var r = changed[i];
9787                     r.el.style.visibility = r.visibility;
9788                     r.el.style.display = "none";
9789                 }
9790                 this._measureChanged = null;
9791             }
9792             return this;
9793         },
9794
9795         /**
9796         * Update the innerHTML of this element, optionally searching for and processing scripts
9797         * @param {String} html The new HTML
9798         * @param {Boolean} loadScripts (optional) true to look for and process scripts
9799         * @param {Function} callback For async script loading you can be noticed when the update completes
9800         * @return {Roo.Element} this
9801          */
9802         update : function(html, loadScripts, callback){
9803             if(typeof html == "undefined"){
9804                 html = "";
9805             }
9806             if(loadScripts !== true){
9807                 this.dom.innerHTML = html;
9808                 if(typeof callback == "function"){
9809                     callback();
9810                 }
9811                 return this;
9812             }
9813             var id = Roo.id();
9814             var dom = this.dom;
9815
9816             html += '<span id="' + id + '"></span>';
9817
9818             E.onAvailable(id, function(){
9819                 var hd = document.getElementsByTagName("head")[0];
9820                 var re = /(?:<script([^>]*)?>)((\n|\r|.)*?)(?:<\/script>)/ig;
9821                 var srcRe = /\ssrc=([\'\"])(.*?)\1/i;
9822                 var typeRe = /\stype=([\'\"])(.*?)\1/i;
9823
9824                 var match;
9825                 while(match = re.exec(html)){
9826                     var attrs = match[1];
9827                     var srcMatch = attrs ? attrs.match(srcRe) : false;
9828                     if(srcMatch && srcMatch[2]){
9829                        var s = document.createElement("script");
9830                        s.src = srcMatch[2];
9831                        var typeMatch = attrs.match(typeRe);
9832                        if(typeMatch && typeMatch[2]){
9833                            s.type = typeMatch[2];
9834                        }
9835                        hd.appendChild(s);
9836                     }else if(match[2] && match[2].length > 0){
9837                         if(window.execScript) {
9838                            window.execScript(match[2]);
9839                         } else {
9840                             /**
9841                              * eval:var:id
9842                              * eval:var:dom
9843                              * eval:var:html
9844                              * 
9845                              */
9846                            window.eval(match[2]);
9847                         }
9848                     }
9849                 }
9850                 var el = document.getElementById(id);
9851                 if(el){el.parentNode.removeChild(el);}
9852                 if(typeof callback == "function"){
9853                     callback();
9854                 }
9855             });
9856             dom.innerHTML = html.replace(/(?:<script.*?>)((\n|\r|.)*?)(?:<\/script>)/ig, "");
9857             return this;
9858         },
9859
9860         /**
9861          * Direct access to the UpdateManager update() method (takes the same parameters).
9862          * @param {String/Function} url The url for this request or a function to call to get the url
9863          * @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}
9864          * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
9865          * @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.
9866          * @return {Roo.Element} this
9867          */
9868         load : function(){
9869             var um = this.getUpdateManager();
9870             um.update.apply(um, arguments);
9871             return this;
9872         },
9873
9874         /**
9875         * Gets this element's UpdateManager
9876         * @return {Roo.UpdateManager} The UpdateManager
9877         */
9878         getUpdateManager : function(){
9879             if(!this.updateManager){
9880                 this.updateManager = new Roo.UpdateManager(this);
9881             }
9882             return this.updateManager;
9883         },
9884
9885         /**
9886          * Disables text selection for this element (normalized across browsers)
9887          * @return {Roo.Element} this
9888          */
9889         unselectable : function(){
9890             this.dom.unselectable = "on";
9891             this.swallowEvent("selectstart", true);
9892             this.applyStyles("-moz-user-select:none;-khtml-user-select:none;");
9893             this.addClass("x-unselectable");
9894             return this;
9895         },
9896
9897         /**
9898         * Calculates the x, y to center this element on the screen
9899         * @return {Array} The x, y values [x, y]
9900         */
9901         getCenterXY : function(){
9902             return this.getAlignToXY(document, 'c-c');
9903         },
9904
9905         /**
9906         * Centers the Element in either the viewport, or another Element.
9907         * @param {String/HTMLElement/Roo.Element} centerIn (optional) The element in which to center the element.
9908         */
9909         center : function(centerIn){
9910             this.alignTo(centerIn || document, 'c-c');
9911             return this;
9912         },
9913
9914         /**
9915          * Tests various css rules/browsers to determine if this element uses a border box
9916          * @return {Boolean}
9917          */
9918         isBorderBox : function(){
9919             return noBoxAdjust[this.dom.tagName.toLowerCase()] || Roo.isBorderBox;
9920         },
9921
9922         /**
9923          * Return a box {x, y, width, height} that can be used to set another elements
9924          * size/location to match this element.
9925          * @param {Boolean} contentBox (optional) If true a box for the content of the element is returned.
9926          * @param {Boolean} local (optional) If true the element's left and top are returned instead of page x/y.
9927          * @return {Object} box An object in the format {x, y, width, height}
9928          */
9929         getBox : function(contentBox, local){
9930             var xy;
9931             if(!local){
9932                 xy = this.getXY();
9933             }else{
9934                 var left = parseInt(this.getStyle("left"), 10) || 0;
9935                 var top = parseInt(this.getStyle("top"), 10) || 0;
9936                 xy = [left, top];
9937             }
9938             var el = this.dom, w = el.offsetWidth, h = el.offsetHeight, bx;
9939             if(!contentBox){
9940                 bx = {x: xy[0], y: xy[1], 0: xy[0], 1: xy[1], width: w, height: h};
9941             }else{
9942                 var l = this.getBorderWidth("l")+this.getPadding("l");
9943                 var r = this.getBorderWidth("r")+this.getPadding("r");
9944                 var t = this.getBorderWidth("t")+this.getPadding("t");
9945                 var b = this.getBorderWidth("b")+this.getPadding("b");
9946                 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)};
9947             }
9948             bx.right = bx.x + bx.width;
9949             bx.bottom = bx.y + bx.height;
9950             return bx;
9951         },
9952
9953         /**
9954          * Returns the sum width of the padding and borders for the passed "sides". See getBorderWidth()
9955          for more information about the sides.
9956          * @param {String} sides
9957          * @return {Number}
9958          */
9959         getFrameWidth : function(sides, onlyContentBox){
9960             return onlyContentBox && Roo.isBorderBox ? 0 : (this.getPadding(sides) + this.getBorderWidth(sides));
9961         },
9962
9963         /**
9964          * 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.
9965          * @param {Object} box The box to fill {x, y, width, height}
9966          * @param {Boolean} adjust (optional) Whether to adjust for box-model issues automatically
9967          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
9968          * @return {Roo.Element} this
9969          */
9970         setBox : function(box, adjust, animate){
9971             var w = box.width, h = box.height;
9972             if((adjust && !this.autoBoxAdjust) && !this.isBorderBox()){
9973                w -= (this.getBorderWidth("lr") + this.getPadding("lr"));
9974                h -= (this.getBorderWidth("tb") + this.getPadding("tb"));
9975             }
9976             this.setBounds(box.x, box.y, w, h, this.preanim(arguments, 2));
9977             return this;
9978         },
9979
9980         /**
9981          * Forces the browser to repaint this element
9982          * @return {Roo.Element} this
9983          */
9984          repaint : function(){
9985             var dom = this.dom;
9986             this.addClass("x-repaint");
9987             setTimeout(function(){
9988                 Roo.get(dom).removeClass("x-repaint");
9989             }, 1);
9990             return this;
9991         },
9992
9993         /**
9994          * Returns an object with properties top, left, right and bottom representing the margins of this element unless sides is passed,
9995          * then it returns the calculated width of the sides (see getPadding)
9996          * @param {String} sides (optional) Any combination of l, r, t, b to get the sum of those sides
9997          * @return {Object/Number}
9998          */
9999         getMargins : function(side){
10000             if(!side){
10001                 return {
10002                     top: parseInt(this.getStyle("margin-top"), 10) || 0,
10003                     left: parseInt(this.getStyle("margin-left"), 10) || 0,
10004                     bottom: parseInt(this.getStyle("margin-bottom"), 10) || 0,
10005                     right: parseInt(this.getStyle("margin-right"), 10) || 0
10006                 };
10007             }else{
10008                 return this.addStyles(side, El.margins);
10009              }
10010         },
10011
10012         // private
10013         addStyles : function(sides, styles){
10014             var val = 0, v, w;
10015             for(var i = 0, len = sides.length; i < len; i++){
10016                 v = this.getStyle(styles[sides.charAt(i)]);
10017                 if(v){
10018                      w = parseInt(v, 10);
10019                      if(w){ val += w; }
10020                 }
10021             }
10022             return val;
10023         },
10024
10025         /**
10026          * Creates a proxy element of this element
10027          * @param {String/Object} config The class name of the proxy element or a DomHelper config object
10028          * @param {String/HTMLElement} renderTo (optional) The element or element id to render the proxy to (defaults to document.body)
10029          * @param {Boolean} matchBox (optional) True to align and size the proxy to this element now (defaults to false)
10030          * @return {Roo.Element} The new proxy element
10031          */
10032         createProxy : function(config, renderTo, matchBox){
10033             if(renderTo){
10034                 renderTo = Roo.getDom(renderTo);
10035             }else{
10036                 renderTo = document.body;
10037             }
10038             config = typeof config == "object" ?
10039                 config : {tag : "div", cls: config};
10040             var proxy = Roo.DomHelper.append(renderTo, config, true);
10041             if(matchBox){
10042                proxy.setBox(this.getBox());
10043             }
10044             return proxy;
10045         },
10046
10047         /**
10048          * Puts a mask over this element to disable user interaction. Requires core.css.
10049          * This method can only be applied to elements which accept child nodes.
10050          * @param {String} msg (optional) A message to display in the mask
10051          * @param {String} msgCls (optional) A css class to apply to the msg element - use no-spinner to hide the spinner on bootstrap
10052          * @return {Element} The mask  element
10053          */
10054         mask : function(msg, msgCls)
10055         {
10056             if(this.getStyle("position") == "static" && this.dom.tagName !== 'BODY'){
10057                 this.setStyle("position", "relative");
10058             }
10059             if(!this._mask){
10060                 this._mask = Roo.DomHelper.append(this.dom, {cls:"roo-el-mask"}, true);
10061             }
10062             
10063             this.addClass("x-masked");
10064             this._mask.setDisplayed(true);
10065             
10066             // we wander
10067             var z = 0;
10068             var dom = this.dom;
10069             while (dom && dom.style) {
10070                 if (!isNaN(parseInt(dom.style.zIndex))) {
10071                     z = Math.max(z, parseInt(dom.style.zIndex));
10072                 }
10073                 dom = dom.parentNode;
10074             }
10075             // if we are masking the body - then it hides everything..
10076             if (this.dom == document.body) {
10077                 z = 1000000;
10078                 this._mask.setWidth(Roo.lib.Dom.getDocumentWidth());
10079                 this._mask.setHeight(Roo.lib.Dom.getDocumentHeight());
10080             }
10081            
10082             if(typeof msg == 'string'){
10083                 if(!this._maskMsg){
10084                     this._maskMsg = Roo.DomHelper.append(this.dom, {
10085                         cls: "roo-el-mask-msg", 
10086                         cn: [
10087                             {
10088                                 tag: 'i',
10089                                 cls: 'fa fa-spinner fa-spin'
10090                             },
10091                             {
10092                                 tag: 'div'
10093                             }   
10094                         ]
10095                     }, true);
10096                 }
10097                 var mm = this._maskMsg;
10098                 mm.dom.className = msgCls ? "roo-el-mask-msg " + msgCls : "roo-el-mask-msg";
10099                 if (mm.dom.lastChild) { // weird IE issue?
10100                     mm.dom.lastChild.innerHTML = msg;
10101                 }
10102                 mm.setDisplayed(true);
10103                 mm.center(this);
10104                 mm.setStyle('z-index', z + 102);
10105             }
10106             if(Roo.isIE && !(Roo.isIE7 && Roo.isStrict) && this.getStyle('height') == 'auto'){ // ie will not expand full height automatically
10107                 this._mask.setHeight(this.getHeight());
10108             }
10109             this._mask.setStyle('z-index', z + 100);
10110             
10111             return this._mask;
10112         },
10113
10114         /**
10115          * Removes a previously applied mask. If removeEl is true the mask overlay is destroyed, otherwise
10116          * it is cached for reuse.
10117          */
10118         unmask : function(removeEl){
10119             if(this._mask){
10120                 if(removeEl === true){
10121                     this._mask.remove();
10122                     delete this._mask;
10123                     if(this._maskMsg){
10124                         this._maskMsg.remove();
10125                         delete this._maskMsg;
10126                     }
10127                 }else{
10128                     this._mask.setDisplayed(false);
10129                     if(this._maskMsg){
10130                         this._maskMsg.setDisplayed(false);
10131                     }
10132                 }
10133             }
10134             this.removeClass("x-masked");
10135         },
10136
10137         /**
10138          * Returns true if this element is masked
10139          * @return {Boolean}
10140          */
10141         isMasked : function(){
10142             return this._mask && this._mask.isVisible();
10143         },
10144
10145         /**
10146          * Creates an iframe shim for this element to keep selects and other windowed objects from
10147          * showing through.
10148          * @return {Roo.Element} The new shim element
10149          */
10150         createShim : function(){
10151             var el = document.createElement('iframe');
10152             el.frameBorder = 'no';
10153             el.className = 'roo-shim';
10154             if(Roo.isIE && Roo.isSecure){
10155                 el.src = Roo.SSL_SECURE_URL;
10156             }
10157             var shim = Roo.get(this.dom.parentNode.insertBefore(el, this.dom));
10158             shim.autoBoxAdjust = false;
10159             return shim;
10160         },
10161
10162         /**
10163          * Removes this element from the DOM and deletes it from the cache
10164          */
10165         remove : function(){
10166             if(this.dom.parentNode){
10167                 this.dom.parentNode.removeChild(this.dom);
10168             }
10169             delete El.cache[this.dom.id];
10170         },
10171
10172         /**
10173          * Sets up event handlers to add and remove a css class when the mouse is over this element
10174          * @param {String} className
10175          * @param {Boolean} preventFlicker (optional) If set to true, it prevents flickering by filtering
10176          * mouseout events for children elements
10177          * @return {Roo.Element} this
10178          */
10179         addClassOnOver : function(className, preventFlicker){
10180             this.on("mouseover", function(){
10181                 Roo.fly(this, '_internal').addClass(className);
10182             }, this.dom);
10183             var removeFn = function(e){
10184                 if(preventFlicker !== true || !e.within(this, true)){
10185                     Roo.fly(this, '_internal').removeClass(className);
10186                 }
10187             };
10188             this.on("mouseout", removeFn, this.dom);
10189             return this;
10190         },
10191
10192         /**
10193          * Sets up event handlers to add and remove a css class when this element has the focus
10194          * @param {String} className
10195          * @return {Roo.Element} this
10196          */
10197         addClassOnFocus : function(className){
10198             this.on("focus", function(){
10199                 Roo.fly(this, '_internal').addClass(className);
10200             }, this.dom);
10201             this.on("blur", function(){
10202                 Roo.fly(this, '_internal').removeClass(className);
10203             }, this.dom);
10204             return this;
10205         },
10206         /**
10207          * 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)
10208          * @param {String} className
10209          * @return {Roo.Element} this
10210          */
10211         addClassOnClick : function(className){
10212             var dom = this.dom;
10213             this.on("mousedown", function(){
10214                 Roo.fly(dom, '_internal').addClass(className);
10215                 var d = Roo.get(document);
10216                 var fn = function(){
10217                     Roo.fly(dom, '_internal').removeClass(className);
10218                     d.removeListener("mouseup", fn);
10219                 };
10220                 d.on("mouseup", fn);
10221             });
10222             return this;
10223         },
10224
10225         /**
10226          * Stops the specified event from bubbling and optionally prevents the default action
10227          * @param {String} eventName
10228          * @param {Boolean} preventDefault (optional) true to prevent the default action too
10229          * @return {Roo.Element} this
10230          */
10231         swallowEvent : function(eventName, preventDefault){
10232             var fn = function(e){
10233                 e.stopPropagation();
10234                 if(preventDefault){
10235                     e.preventDefault();
10236                 }
10237             };
10238             if(eventName instanceof Array){
10239                 for(var i = 0, len = eventName.length; i < len; i++){
10240                      this.on(eventName[i], fn);
10241                 }
10242                 return this;
10243             }
10244             this.on(eventName, fn);
10245             return this;
10246         },
10247
10248         /**
10249          * @private
10250          */
10251         fitToParentDelegate : Roo.emptyFn, // keep a reference to the fitToParent delegate
10252
10253         /**
10254          * Sizes this element to its parent element's dimensions performing
10255          * neccessary box adjustments.
10256          * @param {Boolean} monitorResize (optional) If true maintains the fit when the browser window is resized.
10257          * @param {String/HTMLElment/Element} targetParent (optional) The target parent, default to the parentNode.
10258          * @return {Roo.Element} this
10259          */
10260         fitToParent : function(monitorResize, targetParent) {
10261           Roo.EventManager.removeResizeListener(this.fitToParentDelegate); // always remove previous fitToParent delegate from onWindowResize
10262           this.fitToParentDelegate = Roo.emptyFn; // remove reference to previous delegate
10263           if (monitorResize === true && !this.dom.parentNode) { // check if this Element still exists
10264             return this;
10265           }
10266           var p = Roo.get(targetParent || this.dom.parentNode);
10267           this.setSize(p.getComputedWidth() - p.getFrameWidth('lr'), p.getComputedHeight() - p.getFrameWidth('tb'));
10268           if (monitorResize === true) {
10269             this.fitToParentDelegate = this.fitToParent.createDelegate(this, [true, targetParent]);
10270             Roo.EventManager.onWindowResize(this.fitToParentDelegate);
10271           }
10272           return this;
10273         },
10274
10275         /**
10276          * Gets the next sibling, skipping text nodes
10277          * @return {HTMLElement} The next sibling or null
10278          */
10279         getNextSibling : function(){
10280             var n = this.dom.nextSibling;
10281             while(n && n.nodeType != 1){
10282                 n = n.nextSibling;
10283             }
10284             return n;
10285         },
10286
10287         /**
10288          * Gets the previous sibling, skipping text nodes
10289          * @return {HTMLElement} The previous sibling or null
10290          */
10291         getPrevSibling : function(){
10292             var n = this.dom.previousSibling;
10293             while(n && n.nodeType != 1){
10294                 n = n.previousSibling;
10295             }
10296             return n;
10297         },
10298
10299
10300         /**
10301          * Appends the passed element(s) to this element
10302          * @param {String/HTMLElement/Array/Element/CompositeElement} el
10303          * @return {Roo.Element} this
10304          */
10305         appendChild: function(el){
10306             el = Roo.get(el);
10307             el.appendTo(this);
10308             return this;
10309         },
10310
10311         /**
10312          * Creates the passed DomHelper config and appends it to this element or optionally inserts it before the passed child element.
10313          * @param {Object} config DomHelper element config object.  If no tag is specified (e.g., {tag:'input'}) then a div will be
10314          * automatically generated with the specified attributes.
10315          * @param {HTMLElement} insertBefore (optional) a child element of this element
10316          * @param {Boolean} returnDom (optional) true to return the dom node instead of creating an Element
10317          * @return {Roo.Element} The new child element
10318          */
10319         createChild: function(config, insertBefore, returnDom){
10320             config = config || {tag:'div'};
10321             if(insertBefore){
10322                 return Roo.DomHelper.insertBefore(insertBefore, config, returnDom !== true);
10323             }
10324             return Roo.DomHelper[!this.dom.firstChild ? 'overwrite' : 'append'](this.dom, config,  returnDom !== true);
10325         },
10326
10327         /**
10328          * Appends this element to the passed element
10329          * @param {String/HTMLElement/Element} el The new parent element
10330          * @return {Roo.Element} this
10331          */
10332         appendTo: function(el){
10333             el = Roo.getDom(el);
10334             el.appendChild(this.dom);
10335             return this;
10336         },
10337
10338         /**
10339          * Inserts this element before the passed element in the DOM
10340          * @param {String/HTMLElement/Element} el The element to insert before
10341          * @return {Roo.Element} this
10342          */
10343         insertBefore: function(el){
10344             el = Roo.getDom(el);
10345             el.parentNode.insertBefore(this.dom, el);
10346             return this;
10347         },
10348
10349         /**
10350          * Inserts this element after the passed element in the DOM
10351          * @param {String/HTMLElement/Element} el The element to insert after
10352          * @return {Roo.Element} this
10353          */
10354         insertAfter: function(el){
10355             el = Roo.getDom(el);
10356             el.parentNode.insertBefore(this.dom, el.nextSibling);
10357             return this;
10358         },
10359
10360         /**
10361          * Inserts (or creates) an element (or DomHelper config) as the first child of the this element
10362          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10363          * @return {Roo.Element} The new child
10364          */
10365         insertFirst: function(el, returnDom){
10366             el = el || {};
10367             if(typeof el == 'object' && !el.nodeType){ // dh config
10368                 return this.createChild(el, this.dom.firstChild, returnDom);
10369             }else{
10370                 el = Roo.getDom(el);
10371                 this.dom.insertBefore(el, this.dom.firstChild);
10372                 return !returnDom ? Roo.get(el) : el;
10373             }
10374         },
10375
10376         /**
10377          * Inserts (or creates) the passed element (or DomHelper config) as a sibling of this element
10378          * @param {String/HTMLElement/Element/Object} el The id or element to insert or a DomHelper config to create and insert
10379          * @param {String} where (optional) 'before' or 'after' defaults to before
10380          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10381          * @return {Roo.Element} the inserted Element
10382          */
10383         insertSibling: function(el, where, returnDom){
10384             where = where ? where.toLowerCase() : 'before';
10385             el = el || {};
10386             var rt, refNode = where == 'before' ? this.dom : this.dom.nextSibling;
10387
10388             if(typeof el == 'object' && !el.nodeType){ // dh config
10389                 if(where == 'after' && !this.dom.nextSibling){
10390                     rt = Roo.DomHelper.append(this.dom.parentNode, el, !returnDom);
10391                 }else{
10392                     rt = Roo.DomHelper[where == 'after' ? 'insertAfter' : 'insertBefore'](this.dom, el, !returnDom);
10393                 }
10394
10395             }else{
10396                 rt = this.dom.parentNode.insertBefore(Roo.getDom(el),
10397                             where == 'before' ? this.dom : this.dom.nextSibling);
10398                 if(!returnDom){
10399                     rt = Roo.get(rt);
10400                 }
10401             }
10402             return rt;
10403         },
10404
10405         /**
10406          * Creates and wraps this element with another element
10407          * @param {Object} config (optional) DomHelper element config object for the wrapper element or null for an empty div
10408          * @param {Boolean} returnDom (optional) True to return the raw DOM element instead of Roo.Element
10409          * @return {HTMLElement/Element} The newly created wrapper element
10410          */
10411         wrap: function(config, returnDom){
10412             if(!config){
10413                 config = {tag: "div"};
10414             }
10415             var newEl = Roo.DomHelper.insertBefore(this.dom, config, !returnDom);
10416             newEl.dom ? newEl.dom.appendChild(this.dom) : newEl.appendChild(this.dom);
10417             return newEl;
10418         },
10419
10420         /**
10421          * Replaces the passed element with this element
10422          * @param {String/HTMLElement/Element} el The element to replace
10423          * @return {Roo.Element} this
10424          */
10425         replace: function(el){
10426             el = Roo.get(el);
10427             this.insertBefore(el);
10428             el.remove();
10429             return this;
10430         },
10431
10432         /**
10433          * Inserts an html fragment into this element
10434          * @param {String} where Where to insert the html in relation to the this element - beforeBegin, afterBegin, beforeEnd, afterEnd.
10435          * @param {String} html The HTML fragment
10436          * @param {Boolean} returnEl True to return an Roo.Element
10437          * @return {HTMLElement/Roo.Element} The inserted node (or nearest related if more than 1 inserted)
10438          */
10439         insertHtml : function(where, html, returnEl){
10440             var el = Roo.DomHelper.insertHtml(where, this.dom, html);
10441             return returnEl ? Roo.get(el) : el;
10442         },
10443
10444         /**
10445          * Sets the passed attributes as attributes of this element (a style attribute can be a string, object or function)
10446          * @param {Object} o The object with the attributes
10447          * @param {Boolean} useSet (optional) false to override the default setAttribute to use expandos.
10448          * @return {Roo.Element} this
10449          */
10450         set : function(o, useSet){
10451             var el = this.dom;
10452             useSet = typeof useSet == 'undefined' ? (el.setAttribute ? true : false) : useSet;
10453             for(var attr in o){
10454                 if(attr == "style" || typeof o[attr] == "function")  { continue; }
10455                 if(attr=="cls"){
10456                     el.className = o["cls"];
10457                 }else{
10458                     if(useSet) {
10459                         el.setAttribute(attr, o[attr]);
10460                     } else {
10461                         el[attr] = o[attr];
10462                     }
10463                 }
10464             }
10465             if(o.style){
10466                 Roo.DomHelper.applyStyles(el, o.style);
10467             }
10468             return this;
10469         },
10470
10471         /**
10472          * Convenience method for constructing a KeyMap
10473          * @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:
10474          *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
10475          * @param {Function} fn The function to call
10476          * @param {Object} scope (optional) The scope of the function
10477          * @return {Roo.KeyMap} The KeyMap created
10478          */
10479         addKeyListener : function(key, fn, scope){
10480             var config;
10481             if(typeof key != "object" || key instanceof Array){
10482                 config = {
10483                     key: key,
10484                     fn: fn,
10485                     scope: scope
10486                 };
10487             }else{
10488                 config = {
10489                     key : key.key,
10490                     shift : key.shift,
10491                     ctrl : key.ctrl,
10492                     alt : key.alt,
10493                     fn: fn,
10494                     scope: scope
10495                 };
10496             }
10497             return new Roo.KeyMap(this, config);
10498         },
10499
10500         /**
10501          * Creates a KeyMap for this element
10502          * @param {Object} config The KeyMap config. See {@link Roo.KeyMap} for more details
10503          * @return {Roo.KeyMap} The KeyMap created
10504          */
10505         addKeyMap : function(config){
10506             return new Roo.KeyMap(this, config);
10507         },
10508
10509         /**
10510          * Returns true if this element is scrollable.
10511          * @return {Boolean}
10512          */
10513          isScrollable : function(){
10514             var dom = this.dom;
10515             return dom.scrollHeight > dom.clientHeight || dom.scrollWidth > dom.clientWidth;
10516         },
10517
10518         /**
10519          * 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().
10520          * @param {String} side Either "left" for scrollLeft values or "top" for scrollTop values.
10521          * @param {Number} value The new scroll value
10522          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10523          * @return {Element} this
10524          */
10525
10526         scrollTo : function(side, value, animate){
10527             var prop = side.toLowerCase() == "left" ? "scrollLeft" : "scrollTop";
10528             if(!animate || !A){
10529                 this.dom[prop] = value;
10530             }else{
10531                 var to = prop == "scrollLeft" ? [value, this.dom.scrollTop] : [this.dom.scrollLeft, value];
10532                 this.anim({scroll: {"to": to}}, this.preanim(arguments, 2), 'scroll');
10533             }
10534             return this;
10535         },
10536
10537         /**
10538          * Scrolls this element the specified direction. Does bounds checking to make sure the scroll is
10539          * within this element's scrollable range.
10540          * @param {String} direction Possible values are: "l","left" - "r","right" - "t","top","up" - "b","bottom","down".
10541          * @param {Number} distance How far to scroll the element in pixels
10542          * @param {Boolean/Object} animate (optional) true for the default animation or a standard Element animation config object
10543          * @return {Boolean} Returns true if a scroll was triggered or false if the element
10544          * was scrolled as far as it could go.
10545          */
10546          scroll : function(direction, distance, animate){
10547              if(!this.isScrollable()){
10548                  return;
10549              }
10550              var el = this.dom;
10551              var l = el.scrollLeft, t = el.scrollTop;
10552              var w = el.scrollWidth, h = el.scrollHeight;
10553              var cw = el.clientWidth, ch = el.clientHeight;
10554              direction = direction.toLowerCase();
10555              var scrolled = false;
10556              var a = this.preanim(arguments, 2);
10557              switch(direction){
10558                  case "l":
10559                  case "left":
10560                      if(w - l > cw){
10561                          var v = Math.min(l + distance, w-cw);
10562                          this.scrollTo("left", v, a);
10563                          scrolled = true;
10564                      }
10565                      break;
10566                 case "r":
10567                 case "right":
10568                      if(l > 0){
10569                          var v = Math.max(l - distance, 0);
10570                          this.scrollTo("left", v, a);
10571                          scrolled = true;
10572                      }
10573                      break;
10574                 case "t":
10575                 case "top":
10576                 case "up":
10577                      if(t > 0){
10578                          var v = Math.max(t - distance, 0);
10579                          this.scrollTo("top", v, a);
10580                          scrolled = true;
10581                      }
10582                      break;
10583                 case "b":
10584                 case "bottom":
10585                 case "down":
10586                      if(h - t > ch){
10587                          var v = Math.min(t + distance, h-ch);
10588                          this.scrollTo("top", v, a);
10589                          scrolled = true;
10590                      }
10591                      break;
10592              }
10593              return scrolled;
10594         },
10595
10596         /**
10597          * Translates the passed page coordinates into left/top css values for this element
10598          * @param {Number/Array} x The page x or an array containing [x, y]
10599          * @param {Number} y The page y
10600          * @return {Object} An object with left and top properties. e.g. {left: (value), top: (value)}
10601          */
10602         translatePoints : function(x, y){
10603             if(typeof x == 'object' || x instanceof Array){
10604                 y = x[1]; x = x[0];
10605             }
10606             var p = this.getStyle('position');
10607             var o = this.getXY();
10608
10609             var l = parseInt(this.getStyle('left'), 10);
10610             var t = parseInt(this.getStyle('top'), 10);
10611
10612             if(isNaN(l)){
10613                 l = (p == "relative") ? 0 : this.dom.offsetLeft;
10614             }
10615             if(isNaN(t)){
10616                 t = (p == "relative") ? 0 : this.dom.offsetTop;
10617             }
10618
10619             return {left: (x - o[0] + l), top: (y - o[1] + t)};
10620         },
10621
10622         /**
10623          * Returns the current scroll position of the element.
10624          * @return {Object} An object containing the scroll position in the format {left: (scrollLeft), top: (scrollTop)}
10625          */
10626         getScroll : function(){
10627             var d = this.dom, doc = document;
10628             if(d == doc || d == doc.body){
10629                 var l = window.pageXOffset || doc.documentElement.scrollLeft || doc.body.scrollLeft || 0;
10630                 var t = window.pageYOffset || doc.documentElement.scrollTop || doc.body.scrollTop || 0;
10631                 return {left: l, top: t};
10632             }else{
10633                 return {left: d.scrollLeft, top: d.scrollTop};
10634             }
10635         },
10636
10637         /**
10638          * Return the CSS color for the specified CSS attribute. rgb, 3 digit (like #fff) and valid values
10639          * are convert to standard 6 digit hex color.
10640          * @param {String} attr The css attribute
10641          * @param {String} defaultValue The default value to use when a valid color isn't found
10642          * @param {String} prefix (optional) defaults to #. Use an empty string when working with
10643          * YUI color anims.
10644          */
10645         getColor : function(attr, defaultValue, prefix){
10646             var v = this.getStyle(attr);
10647             if(!v || v == "transparent" || v == "inherit") {
10648                 return defaultValue;
10649             }
10650             var color = typeof prefix == "undefined" ? "#" : prefix;
10651             if(v.substr(0, 4) == "rgb("){
10652                 var rvs = v.slice(4, v.length -1).split(",");
10653                 for(var i = 0; i < 3; i++){
10654                     var h = parseInt(rvs[i]).toString(16);
10655                     if(h < 16){
10656                         h = "0" + h;
10657                     }
10658                     color += h;
10659                 }
10660             } else {
10661                 if(v.substr(0, 1) == "#"){
10662                     if(v.length == 4) {
10663                         for(var i = 1; i < 4; i++){
10664                             var c = v.charAt(i);
10665                             color +=  c + c;
10666                         }
10667                     }else if(v.length == 7){
10668                         color += v.substr(1);
10669                     }
10670                 }
10671             }
10672             return(color.length > 5 ? color.toLowerCase() : defaultValue);
10673         },
10674
10675         /**
10676          * Wraps the specified element with a special markup/CSS block that renders by default as a gray container with a
10677          * gradient background, rounded corners and a 4-way shadow.
10678          * @param {String} class (optional) A base CSS class to apply to the containing wrapper element (defaults to 'x-box').
10679          * Note that there are a number of CSS rules that are dependent on this name to make the overall effect work,
10680          * so if you supply an alternate base class, make sure you also supply all of the necessary rules.
10681          * @return {Roo.Element} this
10682          */
10683         boxWrap : function(cls){
10684             cls = cls || 'x-box';
10685             var el = Roo.get(this.insertHtml('beforeBegin', String.format('<div class="{0}">'+El.boxMarkup+'</div>', cls)));
10686             el.child('.'+cls+'-mc').dom.appendChild(this.dom);
10687             return el;
10688         },
10689
10690         /**
10691          * Returns the value of a namespaced attribute from the element's underlying DOM node.
10692          * @param {String} namespace The namespace in which to look for the attribute
10693          * @param {String} name The attribute name
10694          * @return {String} The attribute value
10695          */
10696         getAttributeNS : Roo.isIE ? function(ns, name){
10697             var d = this.dom;
10698             var type = typeof d[ns+":"+name];
10699             if(type != 'undefined' && type != 'unknown'){
10700                 return d[ns+":"+name];
10701             }
10702             return d[name];
10703         } : function(ns, name){
10704             var d = this.dom;
10705             return d.getAttributeNS(ns, name) || d.getAttribute(ns+":"+name) || d.getAttribute(name) || d[name];
10706         },
10707         
10708         
10709         /**
10710          * Sets or Returns the value the dom attribute value
10711          * @param {String|Object} name The attribute name (or object to set multiple attributes)
10712          * @param {String} value (optional) The value to set the attribute to
10713          * @return {String} The attribute value
10714          */
10715         attr : function(name){
10716             if (arguments.length > 1) {
10717                 this.dom.setAttribute(name, arguments[1]);
10718                 return arguments[1];
10719             }
10720             if (typeof(name) == 'object') {
10721                 for(var i in name) {
10722                     this.attr(i, name[i]);
10723                 }
10724                 return name;
10725             }
10726             
10727             
10728             if (!this.dom.hasAttribute(name)) {
10729                 return undefined;
10730             }
10731             return this.dom.getAttribute(name);
10732         }
10733         
10734         
10735         
10736     };
10737
10738     var ep = El.prototype;
10739
10740     /**
10741      * Appends an event handler (Shorthand for addListener)
10742      * @param {String}   eventName     The type of event to append
10743      * @param {Function} fn        The method the event invokes
10744      * @param {Object} scope       (optional) The scope (this object) of the fn
10745      * @param {Object}   options   (optional)An object with standard {@link Roo.EventManager#addListener} options
10746      * @method
10747      */
10748     ep.on = ep.addListener;
10749         // backwards compat
10750     ep.mon = ep.addListener;
10751
10752     /**
10753      * Removes an event handler from this element (shorthand for removeListener)
10754      * @param {String} eventName the type of event to remove
10755      * @param {Function} fn the method the event invokes
10756      * @return {Roo.Element} this
10757      * @method
10758      */
10759     ep.un = ep.removeListener;
10760
10761     /**
10762      * true to automatically adjust width and height settings for box-model issues (default to true)
10763      */
10764     ep.autoBoxAdjust = true;
10765
10766     // private
10767     El.unitPattern = /\d+(px|em|%|en|ex|pt|in|cm|mm|pc)$/i;
10768
10769     // private
10770     El.addUnits = function(v, defaultUnit){
10771         if(v === "" || v == "auto"){
10772             return v;
10773         }
10774         if(v === undefined){
10775             return '';
10776         }
10777         if(typeof v == "number" || !El.unitPattern.test(v)){
10778             return v + (defaultUnit || 'px');
10779         }
10780         return v;
10781     };
10782
10783     // special markup used throughout Roo when box wrapping elements
10784     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>';
10785     /**
10786      * Visibility mode constant - Use visibility to hide element
10787      * @static
10788      * @type Number
10789      */
10790     El.VISIBILITY = 1;
10791     /**
10792      * Visibility mode constant - Use display to hide element
10793      * @static
10794      * @type Number
10795      */
10796     El.DISPLAY = 2;
10797
10798     El.borders = {l: "border-left-width", r: "border-right-width", t: "border-top-width", b: "border-bottom-width"};
10799     El.paddings = {l: "padding-left", r: "padding-right", t: "padding-top", b: "padding-bottom"};
10800     El.margins = {l: "margin-left", r: "margin-right", t: "margin-top", b: "margin-bottom"};
10801
10802
10803
10804     /**
10805      * @private
10806      */
10807     El.cache = {};
10808
10809     var docEl;
10810
10811     /**
10812      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10813      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10814      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10815      * @return {Element} The Element object
10816      * @static
10817      */
10818     El.get = function(el){
10819         var ex, elm, id;
10820         if(!el){ return null; }
10821         if(typeof el == "string"){ // element id
10822             if(!(elm = document.getElementById(el))){
10823                 return null;
10824             }
10825             if(ex = El.cache[el]){
10826                 ex.dom = elm;
10827             }else{
10828                 ex = El.cache[el] = new El(elm);
10829             }
10830             return ex;
10831         }else if(el.tagName){ // dom element
10832             if(!(id = el.id)){
10833                 id = Roo.id(el);
10834             }
10835             if(ex = El.cache[id]){
10836                 ex.dom = el;
10837             }else{
10838                 ex = El.cache[id] = new El(el);
10839             }
10840             return ex;
10841         }else if(el instanceof El){
10842             if(el != docEl){
10843                 el.dom = document.getElementById(el.id) || el.dom; // refresh dom element in case no longer valid,
10844                                                               // catch case where it hasn't been appended
10845                 El.cache[el.id] = el; // in case it was created directly with Element(), let's cache it
10846             }
10847             return el;
10848         }else if(el.isComposite){
10849             return el;
10850         }else if(el instanceof Array){
10851             return El.select(el);
10852         }else if(el == document){
10853             // create a bogus element object representing the document object
10854             if(!docEl){
10855                 var f = function(){};
10856                 f.prototype = El.prototype;
10857                 docEl = new f();
10858                 docEl.dom = document;
10859             }
10860             return docEl;
10861         }
10862         return null;
10863     };
10864
10865     // private
10866     El.uncache = function(el){
10867         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
10868             if(a[i]){
10869                 delete El.cache[a[i].id || a[i]];
10870             }
10871         }
10872     };
10873
10874     // private
10875     // Garbage collection - uncache elements/purge listeners on orphaned elements
10876     // so we don't hold a reference and cause the browser to retain them
10877     El.garbageCollect = function(){
10878         if(!Roo.enableGarbageCollector){
10879             clearInterval(El.collectorThread);
10880             return;
10881         }
10882         for(var eid in El.cache){
10883             var el = El.cache[eid], d = el.dom;
10884             // -------------------------------------------------------
10885             // Determining what is garbage:
10886             // -------------------------------------------------------
10887             // !d
10888             // dom node is null, definitely garbage
10889             // -------------------------------------------------------
10890             // !d.parentNode
10891             // no parentNode == direct orphan, definitely garbage
10892             // -------------------------------------------------------
10893             // !d.offsetParent && !document.getElementById(eid)
10894             // display none elements have no offsetParent so we will
10895             // also try to look it up by it's id. However, check
10896             // offsetParent first so we don't do unneeded lookups.
10897             // This enables collection of elements that are not orphans
10898             // directly, but somewhere up the line they have an orphan
10899             // parent.
10900             // -------------------------------------------------------
10901             if(!d || !d.parentNode || (!d.offsetParent && !document.getElementById(eid))){
10902                 delete El.cache[eid];
10903                 if(d && Roo.enableListenerCollection){
10904                     E.purgeElement(d);
10905                 }
10906             }
10907         }
10908     }
10909     El.collectorThreadId = setInterval(El.garbageCollect, 30000);
10910
10911
10912     // dom is optional
10913     El.Flyweight = function(dom){
10914         this.dom = dom;
10915     };
10916     El.Flyweight.prototype = El.prototype;
10917
10918     El._flyweights = {};
10919     /**
10920      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10921      * the dom node can be overwritten by other code.
10922      * @param {String/HTMLElement} el The dom node or id
10923      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10924      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10925      * @static
10926      * @return {Element} The shared Element object
10927      */
10928     El.fly = function(el, named){
10929         named = named || '_global';
10930         el = Roo.getDom(el);
10931         if(!el){
10932             return null;
10933         }
10934         if(!El._flyweights[named]){
10935             El._flyweights[named] = new El.Flyweight();
10936         }
10937         El._flyweights[named].dom = el;
10938         return El._flyweights[named];
10939     };
10940
10941     /**
10942      * Static method to retrieve Element objects. Uses simple caching to consistently return the same object.
10943      * Automatically fixes if an object was recreated with the same id via AJAX or DOM.
10944      * Shorthand of {@link Roo.Element#get}
10945      * @param {String/HTMLElement/Element} el The id of the node, a DOM Node or an existing Element.
10946      * @return {Element} The Element object
10947      * @member Roo
10948      * @method get
10949      */
10950     Roo.get = El.get;
10951     /**
10952      * Gets the globally shared flyweight Element, with the passed node as the active element. Do not store a reference to this element -
10953      * the dom node can be overwritten by other code.
10954      * Shorthand of {@link Roo.Element#fly}
10955      * @param {String/HTMLElement} el The dom node or id
10956      * @param {String} named (optional) Allows for creation of named reusable flyweights to
10957      *                                  prevent conflicts (e.g. internally Roo uses "_internal")
10958      * @static
10959      * @return {Element} The shared Element object
10960      * @member Roo
10961      * @method fly
10962      */
10963     Roo.fly = El.fly;
10964
10965     // speedy lookup for elements never to box adjust
10966     var noBoxAdjust = Roo.isStrict ? {
10967         select:1
10968     } : {
10969         input:1, select:1, textarea:1
10970     };
10971     if(Roo.isIE || Roo.isGecko){
10972         noBoxAdjust['button'] = 1;
10973     }
10974
10975
10976     Roo.EventManager.on(window, 'unload', function(){
10977         delete El.cache;
10978         delete El._flyweights;
10979     });
10980 })();
10981
10982
10983
10984
10985 if(Roo.DomQuery){
10986     Roo.Element.selectorFunction = Roo.DomQuery.select;
10987 }
10988
10989 Roo.Element.select = function(selector, unique, root){
10990     var els;
10991     if(typeof selector == "string"){
10992         els = Roo.Element.selectorFunction(selector, root);
10993     }else if(selector.length !== undefined){
10994         els = selector;
10995     }else{
10996         throw "Invalid selector";
10997     }
10998     if(unique === true){
10999         return new Roo.CompositeElement(els);
11000     }else{
11001         return new Roo.CompositeElementLite(els);
11002     }
11003 };
11004 /**
11005  * Selects elements based on the passed CSS selector to enable working on them as 1.
11006  * @param {String/Array} selector The CSS selector or an array of elements
11007  * @param {Boolean} unique (optional) true to create a unique Roo.Element for each element (defaults to a shared flyweight object)
11008  * @param {HTMLElement/String} root (optional) The root element of the query or id of the root
11009  * @return {CompositeElementLite/CompositeElement}
11010  * @member Roo
11011  * @method select
11012  */
11013 Roo.select = Roo.Element.select;
11014
11015
11016
11017
11018
11019
11020
11021
11022
11023
11024
11025
11026
11027
11028 /*
11029  * Based on:
11030  * Ext JS Library 1.1.1
11031  * Copyright(c) 2006-2007, Ext JS, LLC.
11032  *
11033  * Originally Released Under LGPL - original licence link has changed is not relivant.
11034  *
11035  * Fork - LGPL
11036  * <script type="text/javascript">
11037  */
11038
11039
11040
11041 //Notifies Element that fx methods are available
11042 Roo.enableFx = true;
11043
11044 /**
11045  * @class Roo.Fx
11046  * <p>A class to provide basic animation and visual effects support.  <b>Note:</b> This class is automatically applied
11047  * to the {@link Roo.Element} interface when included, so all effects calls should be performed via Element.
11048  * Conversely, since the effects are not actually defined in Element, Roo.Fx <b>must</b> be included in order for the 
11049  * Element effects to work.</p><br/>
11050  *
11051  * <p>It is important to note that although the Fx methods and many non-Fx Element methods support "method chaining" in that
11052  * they return the Element object itself as the method return value, it is not always possible to mix the two in a single
11053  * method chain.  The Fx methods use an internal effects queue so that each effect can be properly timed and sequenced.
11054  * Non-Fx methods, on the other hand, have no such internal queueing and will always execute immediately.  For this reason,
11055  * while it may be possible to mix certain Fx and non-Fx method calls in a single chain, it may not always provide the
11056  * expected results and should be done with care.</p><br/>
11057  *
11058  * <p>Motion effects support 8-way anchoring, meaning that you can choose one of 8 different anchor points on the Element
11059  * that will serve as either the start or end point of the animation.  Following are all of the supported anchor positions:</p>
11060 <pre>
11061 Value  Description
11062 -----  -----------------------------
11063 tl     The top left corner
11064 t      The center of the top edge
11065 tr     The top right corner
11066 l      The center of the left edge
11067 r      The center of the right edge
11068 bl     The bottom left corner
11069 b      The center of the bottom edge
11070 br     The bottom right corner
11071 </pre>
11072  * <b>Although some Fx methods accept specific custom config parameters, the ones shown in the Config Options section
11073  * below are common options that can be passed to any Fx method.</b>
11074  * @cfg {Function} callback A function called when the effect is finished
11075  * @cfg {Object} scope The scope of the effect function
11076  * @cfg {String} easing A valid Easing value for the effect
11077  * @cfg {String} afterCls A css class to apply after the effect
11078  * @cfg {Number} duration The length of time (in seconds) that the effect should last
11079  * @cfg {Boolean} remove Whether the Element should be removed from the DOM and destroyed after the effect finishes
11080  * @cfg {Boolean} useDisplay Whether to use the <i>display</i> CSS property instead of <i>visibility</i> when hiding Elements (only applies to 
11081  * effects that end with the element being visually hidden, ignored otherwise)
11082  * @cfg {String/Object/Function} afterStyle A style specification string, e.g. "width:100px", or an object in the form {width:"100px"}, or
11083  * a function which returns such a specification that will be applied to the Element after the effect finishes
11084  * @cfg {Boolean} block Whether the effect should block other effects from queueing while it runs
11085  * @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
11086  * @cfg {Boolean} stopFx Whether subsequent effects should be stopped and removed after the current effect finishes
11087  */
11088 Roo.Fx = {
11089         /**
11090          * Slides the element into view.  An anchor point can be optionally passed to set the point of
11091          * origin for the slide effect.  This function automatically handles wrapping the element with
11092          * a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11093          * Usage:
11094          *<pre><code>
11095 // default: slide the element in from the top
11096 el.slideIn();
11097
11098 // custom: slide the element in from the right with a 2-second duration
11099 el.slideIn('r', { duration: 2 });
11100
11101 // common config options shown with default values
11102 el.slideIn('t', {
11103     easing: 'easeOut',
11104     duration: .5
11105 });
11106 </code></pre>
11107          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11108          * @param {Object} options (optional) Object literal with any of the Fx config options
11109          * @return {Roo.Element} The Element
11110          */
11111     slideIn : function(anchor, o){
11112         var el = this.getFxEl();
11113         o = o || {};
11114
11115         el.queueFx(o, function(){
11116
11117             anchor = anchor || "t";
11118
11119             // fix display to visibility
11120             this.fixDisplay();
11121
11122             // restore values after effect
11123             var r = this.getFxRestore();
11124             var b = this.getBox();
11125             // fixed size for slide
11126             this.setSize(b);
11127
11128             // wrap if needed
11129             var wrap = this.fxWrap(r.pos, o, "hidden");
11130
11131             var st = this.dom.style;
11132             st.visibility = "visible";
11133             st.position = "absolute";
11134
11135             // clear out temp styles after slide and unwrap
11136             var after = function(){
11137                 el.fxUnwrap(wrap, r.pos, o);
11138                 st.width = r.width;
11139                 st.height = r.height;
11140                 el.afterFx(o);
11141             };
11142             // time to calc the positions
11143             var a, pt = {to: [b.x, b.y]}, bw = {to: b.width}, bh = {to: b.height};
11144
11145             switch(anchor.toLowerCase()){
11146                 case "t":
11147                     wrap.setSize(b.width, 0);
11148                     st.left = st.bottom = "0";
11149                     a = {height: bh};
11150                 break;
11151                 case "l":
11152                     wrap.setSize(0, b.height);
11153                     st.right = st.top = "0";
11154                     a = {width: bw};
11155                 break;
11156                 case "r":
11157                     wrap.setSize(0, b.height);
11158                     wrap.setX(b.right);
11159                     st.left = st.top = "0";
11160                     a = {width: bw, points: pt};
11161                 break;
11162                 case "b":
11163                     wrap.setSize(b.width, 0);
11164                     wrap.setY(b.bottom);
11165                     st.left = st.top = "0";
11166                     a = {height: bh, points: pt};
11167                 break;
11168                 case "tl":
11169                     wrap.setSize(0, 0);
11170                     st.right = st.bottom = "0";
11171                     a = {width: bw, height: bh};
11172                 break;
11173                 case "bl":
11174                     wrap.setSize(0, 0);
11175                     wrap.setY(b.y+b.height);
11176                     st.right = st.top = "0";
11177                     a = {width: bw, height: bh, points: pt};
11178                 break;
11179                 case "br":
11180                     wrap.setSize(0, 0);
11181                     wrap.setXY([b.right, b.bottom]);
11182                     st.left = st.top = "0";
11183                     a = {width: bw, height: bh, points: pt};
11184                 break;
11185                 case "tr":
11186                     wrap.setSize(0, 0);
11187                     wrap.setX(b.x+b.width);
11188                     st.left = st.bottom = "0";
11189                     a = {width: bw, height: bh, points: pt};
11190                 break;
11191             }
11192             this.dom.style.visibility = "visible";
11193             wrap.show();
11194
11195             arguments.callee.anim = wrap.fxanim(a,
11196                 o,
11197                 'motion',
11198                 .5,
11199                 'easeOut', after);
11200         });
11201         return this;
11202     },
11203     
11204         /**
11205          * Slides the element out of view.  An anchor point can be optionally passed to set the end point
11206          * for the slide effect.  When the effect is completed, the element will be hidden (visibility = 
11207          * 'hidden') but block elements will still take up space in the document.  The element must be removed
11208          * from the DOM using the 'remove' config option if desired.  This function automatically handles 
11209          * wrapping the element with a fixed-size container if needed.  See the Fx class overview for valid anchor point options.
11210          * Usage:
11211          *<pre><code>
11212 // default: slide the element out to the top
11213 el.slideOut();
11214
11215 // custom: slide the element out to the right with a 2-second duration
11216 el.slideOut('r', { duration: 2 });
11217
11218 // common config options shown with default values
11219 el.slideOut('t', {
11220     easing: 'easeOut',
11221     duration: .5,
11222     remove: false,
11223     useDisplay: false
11224 });
11225 </code></pre>
11226          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to top: 't')
11227          * @param {Object} options (optional) Object literal with any of the Fx config options
11228          * @return {Roo.Element} The Element
11229          */
11230     slideOut : function(anchor, o){
11231         var el = this.getFxEl();
11232         o = o || {};
11233
11234         el.queueFx(o, function(){
11235
11236             anchor = anchor || "t";
11237
11238             // restore values after effect
11239             var r = this.getFxRestore();
11240             
11241             var b = this.getBox();
11242             // fixed size for slide
11243             this.setSize(b);
11244
11245             // wrap if needed
11246             var wrap = this.fxWrap(r.pos, o, "visible");
11247
11248             var st = this.dom.style;
11249             st.visibility = "visible";
11250             st.position = "absolute";
11251
11252             wrap.setSize(b);
11253
11254             var after = function(){
11255                 if(o.useDisplay){
11256                     el.setDisplayed(false);
11257                 }else{
11258                     el.hide();
11259                 }
11260
11261                 el.fxUnwrap(wrap, r.pos, o);
11262
11263                 st.width = r.width;
11264                 st.height = r.height;
11265
11266                 el.afterFx(o);
11267             };
11268
11269             var a, zero = {to: 0};
11270             switch(anchor.toLowerCase()){
11271                 case "t":
11272                     st.left = st.bottom = "0";
11273                     a = {height: zero};
11274                 break;
11275                 case "l":
11276                     st.right = st.top = "0";
11277                     a = {width: zero};
11278                 break;
11279                 case "r":
11280                     st.left = st.top = "0";
11281                     a = {width: zero, points: {to:[b.right, b.y]}};
11282                 break;
11283                 case "b":
11284                     st.left = st.top = "0";
11285                     a = {height: zero, points: {to:[b.x, b.bottom]}};
11286                 break;
11287                 case "tl":
11288                     st.right = st.bottom = "0";
11289                     a = {width: zero, height: zero};
11290                 break;
11291                 case "bl":
11292                     st.right = st.top = "0";
11293                     a = {width: zero, height: zero, points: {to:[b.x, b.bottom]}};
11294                 break;
11295                 case "br":
11296                     st.left = st.top = "0";
11297                     a = {width: zero, height: zero, points: {to:[b.x+b.width, b.bottom]}};
11298                 break;
11299                 case "tr":
11300                     st.left = st.bottom = "0";
11301                     a = {width: zero, height: zero, points: {to:[b.right, b.y]}};
11302                 break;
11303             }
11304
11305             arguments.callee.anim = wrap.fxanim(a,
11306                 o,
11307                 'motion',
11308                 .5,
11309                 "easeOut", after);
11310         });
11311         return this;
11312     },
11313
11314         /**
11315          * Fades the element out while slowly expanding it in all directions.  When the effect is completed, the 
11316          * element will be hidden (visibility = 'hidden') but block elements will still take up space in the document. 
11317          * The element must be removed from the DOM using the 'remove' config option if desired.
11318          * Usage:
11319          *<pre><code>
11320 // default
11321 el.puff();
11322
11323 // common config options shown with default values
11324 el.puff({
11325     easing: 'easeOut',
11326     duration: .5,
11327     remove: false,
11328     useDisplay: false
11329 });
11330 </code></pre>
11331          * @param {Object} options (optional) Object literal with any of the Fx config options
11332          * @return {Roo.Element} The Element
11333          */
11334     puff : function(o){
11335         var el = this.getFxEl();
11336         o = o || {};
11337
11338         el.queueFx(o, function(){
11339             this.clearOpacity();
11340             this.show();
11341
11342             // restore values after effect
11343             var r = this.getFxRestore();
11344             var st = this.dom.style;
11345
11346             var after = function(){
11347                 if(o.useDisplay){
11348                     el.setDisplayed(false);
11349                 }else{
11350                     el.hide();
11351                 }
11352
11353                 el.clearOpacity();
11354
11355                 el.setPositioning(r.pos);
11356                 st.width = r.width;
11357                 st.height = r.height;
11358                 st.fontSize = '';
11359                 el.afterFx(o);
11360             };
11361
11362             var width = this.getWidth();
11363             var height = this.getHeight();
11364
11365             arguments.callee.anim = this.fxanim({
11366                     width : {to: this.adjustWidth(width * 2)},
11367                     height : {to: this.adjustHeight(height * 2)},
11368                     points : {by: [-(width * .5), -(height * .5)]},
11369                     opacity : {to: 0},
11370                     fontSize: {to:200, unit: "%"}
11371                 },
11372                 o,
11373                 'motion',
11374                 .5,
11375                 "easeOut", after);
11376         });
11377         return this;
11378     },
11379
11380         /**
11381          * Blinks the element as if it was clicked and then collapses on its center (similar to switching off a television).
11382          * When the effect is completed, the element will be hidden (visibility = 'hidden') but block elements will still 
11383          * take up space in the document. The element must be removed from the DOM using the 'remove' config option if desired.
11384          * Usage:
11385          *<pre><code>
11386 // default
11387 el.switchOff();
11388
11389 // all config options shown with default values
11390 el.switchOff({
11391     easing: 'easeIn',
11392     duration: .3,
11393     remove: false,
11394     useDisplay: false
11395 });
11396 </code></pre>
11397          * @param {Object} options (optional) Object literal with any of the Fx config options
11398          * @return {Roo.Element} The Element
11399          */
11400     switchOff : function(o){
11401         var el = this.getFxEl();
11402         o = o || {};
11403
11404         el.queueFx(o, function(){
11405             this.clearOpacity();
11406             this.clip();
11407
11408             // restore values after effect
11409             var r = this.getFxRestore();
11410             var st = this.dom.style;
11411
11412             var after = function(){
11413                 if(o.useDisplay){
11414                     el.setDisplayed(false);
11415                 }else{
11416                     el.hide();
11417                 }
11418
11419                 el.clearOpacity();
11420                 el.setPositioning(r.pos);
11421                 st.width = r.width;
11422                 st.height = r.height;
11423
11424                 el.afterFx(o);
11425             };
11426
11427             this.fxanim({opacity:{to:0.3}}, null, null, .1, null, function(){
11428                 this.clearOpacity();
11429                 (function(){
11430                     this.fxanim({
11431                         height:{to:1},
11432                         points:{by:[0, this.getHeight() * .5]}
11433                     }, o, 'motion', 0.3, 'easeIn', after);
11434                 }).defer(100, this);
11435             });
11436         });
11437         return this;
11438     },
11439
11440     /**
11441      * Highlights the Element by setting a color (applies to the background-color by default, but can be
11442      * changed using the "attr" config option) and then fading back to the original color. If no original
11443      * color is available, you should provide the "endColor" config option which will be cleared after the animation.
11444      * Usage:
11445 <pre><code>
11446 // default: highlight background to yellow
11447 el.highlight();
11448
11449 // custom: highlight foreground text to blue for 2 seconds
11450 el.highlight("0000ff", { attr: 'color', duration: 2 });
11451
11452 // common config options shown with default values
11453 el.highlight("ffff9c", {
11454     attr: "background-color", //can be any valid CSS property (attribute) that supports a color value
11455     endColor: (current color) or "ffffff",
11456     easing: 'easeIn',
11457     duration: 1
11458 });
11459 </code></pre>
11460      * @param {String} color (optional) The highlight color. Should be a 6 char hex color without the leading # (defaults to yellow: 'ffff9c')
11461      * @param {Object} options (optional) Object literal with any of the Fx config options
11462      * @return {Roo.Element} The Element
11463      */ 
11464     highlight : function(color, o){
11465         var el = this.getFxEl();
11466         o = o || {};
11467
11468         el.queueFx(o, function(){
11469             color = color || "ffff9c";
11470             attr = o.attr || "backgroundColor";
11471
11472             this.clearOpacity();
11473             this.show();
11474
11475             var origColor = this.getColor(attr);
11476             var restoreColor = this.dom.style[attr];
11477             endColor = (o.endColor || origColor) || "ffffff";
11478
11479             var after = function(){
11480                 el.dom.style[attr] = restoreColor;
11481                 el.afterFx(o);
11482             };
11483
11484             var a = {};
11485             a[attr] = {from: color, to: endColor};
11486             arguments.callee.anim = this.fxanim(a,
11487                 o,
11488                 'color',
11489                 1,
11490                 'easeIn', after);
11491         });
11492         return this;
11493     },
11494
11495    /**
11496     * Shows a ripple of exploding, attenuating borders to draw attention to an Element.
11497     * Usage:
11498 <pre><code>
11499 // default: a single light blue ripple
11500 el.frame();
11501
11502 // custom: 3 red ripples lasting 3 seconds total
11503 el.frame("ff0000", 3, { duration: 3 });
11504
11505 // common config options shown with default values
11506 el.frame("C3DAF9", 1, {
11507     duration: 1 //duration of entire animation (not each individual ripple)
11508     // Note: Easing is not configurable and will be ignored if included
11509 });
11510 </code></pre>
11511     * @param {String} color (optional) The color of the border.  Should be a 6 char hex color without the leading # (defaults to light blue: 'C3DAF9').
11512     * @param {Number} count (optional) The number of ripples to display (defaults to 1)
11513     * @param {Object} options (optional) Object literal with any of the Fx config options
11514     * @return {Roo.Element} The Element
11515     */
11516     frame : function(color, count, o){
11517         var el = this.getFxEl();
11518         o = o || {};
11519
11520         el.queueFx(o, function(){
11521             color = color || "#C3DAF9";
11522             if(color.length == 6){
11523                 color = "#" + color;
11524             }
11525             count = count || 1;
11526             duration = o.duration || 1;
11527             this.show();
11528
11529             var b = this.getBox();
11530             var animFn = function(){
11531                 var proxy = this.createProxy({
11532
11533                      style:{
11534                         visbility:"hidden",
11535                         position:"absolute",
11536                         "z-index":"35000", // yee haw
11537                         border:"0px solid " + color
11538                      }
11539                   });
11540                 var scale = Roo.isBorderBox ? 2 : 1;
11541                 proxy.animate({
11542                     top:{from:b.y, to:b.y - 20},
11543                     left:{from:b.x, to:b.x - 20},
11544                     borderWidth:{from:0, to:10},
11545                     opacity:{from:1, to:0},
11546                     height:{from:b.height, to:(b.height + (20*scale))},
11547                     width:{from:b.width, to:(b.width + (20*scale))}
11548                 }, duration, function(){
11549                     proxy.remove();
11550                 });
11551                 if(--count > 0){
11552                      animFn.defer((duration/2)*1000, this);
11553                 }else{
11554                     el.afterFx(o);
11555                 }
11556             };
11557             animFn.call(this);
11558         });
11559         return this;
11560     },
11561
11562    /**
11563     * Creates a pause before any subsequent queued effects begin.  If there are
11564     * no effects queued after the pause it will have no effect.
11565     * Usage:
11566 <pre><code>
11567 el.pause(1);
11568 </code></pre>
11569     * @param {Number} seconds The length of time to pause (in seconds)
11570     * @return {Roo.Element} The Element
11571     */
11572     pause : function(seconds){
11573         var el = this.getFxEl();
11574         var o = {};
11575
11576         el.queueFx(o, function(){
11577             setTimeout(function(){
11578                 el.afterFx(o);
11579             }, seconds * 1000);
11580         });
11581         return this;
11582     },
11583
11584    /**
11585     * Fade an element in (from transparent to opaque).  The ending opacity can be specified
11586     * using the "endOpacity" config option.
11587     * Usage:
11588 <pre><code>
11589 // default: fade in from opacity 0 to 100%
11590 el.fadeIn();
11591
11592 // custom: fade in from opacity 0 to 75% over 2 seconds
11593 el.fadeIn({ endOpacity: .75, duration: 2});
11594
11595 // common config options shown with default values
11596 el.fadeIn({
11597     endOpacity: 1, //can be any value between 0 and 1 (e.g. .5)
11598     easing: 'easeOut',
11599     duration: .5
11600 });
11601 </code></pre>
11602     * @param {Object} options (optional) Object literal with any of the Fx config options
11603     * @return {Roo.Element} The Element
11604     */
11605     fadeIn : function(o){
11606         var el = this.getFxEl();
11607         o = o || {};
11608         el.queueFx(o, function(){
11609             this.setOpacity(0);
11610             this.fixDisplay();
11611             this.dom.style.visibility = 'visible';
11612             var to = o.endOpacity || 1;
11613             arguments.callee.anim = this.fxanim({opacity:{to:to}},
11614                 o, null, .5, "easeOut", function(){
11615                 if(to == 1){
11616                     this.clearOpacity();
11617                 }
11618                 el.afterFx(o);
11619             });
11620         });
11621         return this;
11622     },
11623
11624    /**
11625     * Fade an element out (from opaque to transparent).  The ending opacity can be specified
11626     * using the "endOpacity" config option.
11627     * Usage:
11628 <pre><code>
11629 // default: fade out from the element's current opacity to 0
11630 el.fadeOut();
11631
11632 // custom: fade out from the element's current opacity to 25% over 2 seconds
11633 el.fadeOut({ endOpacity: .25, duration: 2});
11634
11635 // common config options shown with default values
11636 el.fadeOut({
11637     endOpacity: 0, //can be any value between 0 and 1 (e.g. .5)
11638     easing: 'easeOut',
11639     duration: .5
11640     remove: false,
11641     useDisplay: false
11642 });
11643 </code></pre>
11644     * @param {Object} options (optional) Object literal with any of the Fx config options
11645     * @return {Roo.Element} The Element
11646     */
11647     fadeOut : function(o){
11648         var el = this.getFxEl();
11649         o = o || {};
11650         el.queueFx(o, function(){
11651             arguments.callee.anim = this.fxanim({opacity:{to:o.endOpacity || 0}},
11652                 o, null, .5, "easeOut", function(){
11653                 if(this.visibilityMode == Roo.Element.DISPLAY || o.useDisplay){
11654                      this.dom.style.display = "none";
11655                 }else{
11656                      this.dom.style.visibility = "hidden";
11657                 }
11658                 this.clearOpacity();
11659                 el.afterFx(o);
11660             });
11661         });
11662         return this;
11663     },
11664
11665    /**
11666     * Animates the transition of an element's dimensions from a starting height/width
11667     * to an ending height/width.
11668     * Usage:
11669 <pre><code>
11670 // change height and width to 100x100 pixels
11671 el.scale(100, 100);
11672
11673 // common config options shown with default values.  The height and width will default to
11674 // the element's existing values if passed as null.
11675 el.scale(
11676     [element's width],
11677     [element's height], {
11678     easing: 'easeOut',
11679     duration: .35
11680 });
11681 </code></pre>
11682     * @param {Number} width  The new width (pass undefined to keep the original width)
11683     * @param {Number} height  The new height (pass undefined to keep the original height)
11684     * @param {Object} options (optional) Object literal with any of the Fx config options
11685     * @return {Roo.Element} The Element
11686     */
11687     scale : function(w, h, o){
11688         this.shift(Roo.apply({}, o, {
11689             width: w,
11690             height: h
11691         }));
11692         return this;
11693     },
11694
11695    /**
11696     * Animates the transition of any combination of an element's dimensions, xy position and/or opacity.
11697     * Any of these properties not specified in the config object will not be changed.  This effect 
11698     * requires that at least one new dimension, position or opacity setting must be passed in on
11699     * the config object in order for the function to have any effect.
11700     * Usage:
11701 <pre><code>
11702 // slide the element horizontally to x position 200 while changing the height and opacity
11703 el.shift({ x: 200, height: 50, opacity: .8 });
11704
11705 // common config options shown with default values.
11706 el.shift({
11707     width: [element's width],
11708     height: [element's height],
11709     x: [element's x position],
11710     y: [element's y position],
11711     opacity: [element's opacity],
11712     easing: 'easeOut',
11713     duration: .35
11714 });
11715 </code></pre>
11716     * @param {Object} options  Object literal with any of the Fx config options
11717     * @return {Roo.Element} The Element
11718     */
11719     shift : function(o){
11720         var el = this.getFxEl();
11721         o = o || {};
11722         el.queueFx(o, function(){
11723             var a = {}, w = o.width, h = o.height, x = o.x, y = o.y,  op = o.opacity;
11724             if(w !== undefined){
11725                 a.width = {to: this.adjustWidth(w)};
11726             }
11727             if(h !== undefined){
11728                 a.height = {to: this.adjustHeight(h)};
11729             }
11730             if(x !== undefined || y !== undefined){
11731                 a.points = {to: [
11732                     x !== undefined ? x : this.getX(),
11733                     y !== undefined ? y : this.getY()
11734                 ]};
11735             }
11736             if(op !== undefined){
11737                 a.opacity = {to: op};
11738             }
11739             if(o.xy !== undefined){
11740                 a.points = {to: o.xy};
11741             }
11742             arguments.callee.anim = this.fxanim(a,
11743                 o, 'motion', .35, "easeOut", function(){
11744                 el.afterFx(o);
11745             });
11746         });
11747         return this;
11748     },
11749
11750         /**
11751          * Slides the element while fading it out of view.  An anchor point can be optionally passed to set the 
11752          * ending point of the effect.
11753          * Usage:
11754          *<pre><code>
11755 // default: slide the element downward while fading out
11756 el.ghost();
11757
11758 // custom: slide the element out to the right with a 2-second duration
11759 el.ghost('r', { duration: 2 });
11760
11761 // common config options shown with default values
11762 el.ghost('b', {
11763     easing: 'easeOut',
11764     duration: .5
11765     remove: false,
11766     useDisplay: false
11767 });
11768 </code></pre>
11769          * @param {String} anchor (optional) One of the valid Fx anchor positions (defaults to bottom: 'b')
11770          * @param {Object} options (optional) Object literal with any of the Fx config options
11771          * @return {Roo.Element} The Element
11772          */
11773     ghost : function(anchor, o){
11774         var el = this.getFxEl();
11775         o = o || {};
11776
11777         el.queueFx(o, function(){
11778             anchor = anchor || "b";
11779
11780             // restore values after effect
11781             var r = this.getFxRestore();
11782             var w = this.getWidth(),
11783                 h = this.getHeight();
11784
11785             var st = this.dom.style;
11786
11787             var after = function(){
11788                 if(o.useDisplay){
11789                     el.setDisplayed(false);
11790                 }else{
11791                     el.hide();
11792                 }
11793
11794                 el.clearOpacity();
11795                 el.setPositioning(r.pos);
11796                 st.width = r.width;
11797                 st.height = r.height;
11798
11799                 el.afterFx(o);
11800             };
11801
11802             var a = {opacity: {to: 0}, points: {}}, pt = a.points;
11803             switch(anchor.toLowerCase()){
11804                 case "t":
11805                     pt.by = [0, -h];
11806                 break;
11807                 case "l":
11808                     pt.by = [-w, 0];
11809                 break;
11810                 case "r":
11811                     pt.by = [w, 0];
11812                 break;
11813                 case "b":
11814                     pt.by = [0, h];
11815                 break;
11816                 case "tl":
11817                     pt.by = [-w, -h];
11818                 break;
11819                 case "bl":
11820                     pt.by = [-w, h];
11821                 break;
11822                 case "br":
11823                     pt.by = [w, h];
11824                 break;
11825                 case "tr":
11826                     pt.by = [w, -h];
11827                 break;
11828             }
11829
11830             arguments.callee.anim = this.fxanim(a,
11831                 o,
11832                 'motion',
11833                 .5,
11834                 "easeOut", after);
11835         });
11836         return this;
11837     },
11838
11839         /**
11840          * Ensures that all effects queued after syncFx is called on the element are
11841          * run concurrently.  This is the opposite of {@link #sequenceFx}.
11842          * @return {Roo.Element} The Element
11843          */
11844     syncFx : function(){
11845         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11846             block : false,
11847             concurrent : true,
11848             stopFx : false
11849         });
11850         return this;
11851     },
11852
11853         /**
11854          * Ensures that all effects queued after sequenceFx is called on the element are
11855          * run in sequence.  This is the opposite of {@link #syncFx}.
11856          * @return {Roo.Element} The Element
11857          */
11858     sequenceFx : function(){
11859         this.fxDefaults = Roo.apply(this.fxDefaults || {}, {
11860             block : false,
11861             concurrent : false,
11862             stopFx : false
11863         });
11864         return this;
11865     },
11866
11867         /* @private */
11868     nextFx : function(){
11869         var ef = this.fxQueue[0];
11870         if(ef){
11871             ef.call(this);
11872         }
11873     },
11874
11875         /**
11876          * Returns true if the element has any effects actively running or queued, else returns false.
11877          * @return {Boolean} True if element has active effects, else false
11878          */
11879     hasActiveFx : function(){
11880         return this.fxQueue && this.fxQueue[0];
11881     },
11882
11883         /**
11884          * Stops any running effects and clears the element's internal effects queue if it contains
11885          * any additional effects that haven't started yet.
11886          * @return {Roo.Element} The Element
11887          */
11888     stopFx : function(){
11889         if(this.hasActiveFx()){
11890             var cur = this.fxQueue[0];
11891             if(cur && cur.anim && cur.anim.isAnimated()){
11892                 this.fxQueue = [cur]; // clear out others
11893                 cur.anim.stop(true);
11894             }
11895         }
11896         return this;
11897     },
11898
11899         /* @private */
11900     beforeFx : function(o){
11901         if(this.hasActiveFx() && !o.concurrent){
11902            if(o.stopFx){
11903                this.stopFx();
11904                return true;
11905            }
11906            return false;
11907         }
11908         return true;
11909     },
11910
11911         /**
11912          * Returns true if the element is currently blocking so that no other effect can be queued
11913          * until this effect is finished, else returns false if blocking is not set.  This is commonly
11914          * used to ensure that an effect initiated by a user action runs to completion prior to the
11915          * same effect being restarted (e.g., firing only one effect even if the user clicks several times).
11916          * @return {Boolean} True if blocking, else false
11917          */
11918     hasFxBlock : function(){
11919         var q = this.fxQueue;
11920         return q && q[0] && q[0].block;
11921     },
11922
11923         /* @private */
11924     queueFx : function(o, fn){
11925         if(!this.fxQueue){
11926             this.fxQueue = [];
11927         }
11928         if(!this.hasFxBlock()){
11929             Roo.applyIf(o, this.fxDefaults);
11930             if(!o.concurrent){
11931                 var run = this.beforeFx(o);
11932                 fn.block = o.block;
11933                 this.fxQueue.push(fn);
11934                 if(run){
11935                     this.nextFx();
11936                 }
11937             }else{
11938                 fn.call(this);
11939             }
11940         }
11941         return this;
11942     },
11943
11944         /* @private */
11945     fxWrap : function(pos, o, vis){
11946         var wrap;
11947         if(!o.wrap || !(wrap = Roo.get(o.wrap))){
11948             var wrapXY;
11949             if(o.fixPosition){
11950                 wrapXY = this.getXY();
11951             }
11952             var div = document.createElement("div");
11953             div.style.visibility = vis;
11954             wrap = Roo.get(this.dom.parentNode.insertBefore(div, this.dom));
11955             wrap.setPositioning(pos);
11956             if(wrap.getStyle("position") == "static"){
11957                 wrap.position("relative");
11958             }
11959             this.clearPositioning('auto');
11960             wrap.clip();
11961             wrap.dom.appendChild(this.dom);
11962             if(wrapXY){
11963                 wrap.setXY(wrapXY);
11964             }
11965         }
11966         return wrap;
11967     },
11968
11969         /* @private */
11970     fxUnwrap : function(wrap, pos, o){
11971         this.clearPositioning();
11972         this.setPositioning(pos);
11973         if(!o.wrap){
11974             wrap.dom.parentNode.insertBefore(this.dom, wrap.dom);
11975             wrap.remove();
11976         }
11977     },
11978
11979         /* @private */
11980     getFxRestore : function(){
11981         var st = this.dom.style;
11982         return {pos: this.getPositioning(), width: st.width, height : st.height};
11983     },
11984
11985         /* @private */
11986     afterFx : function(o){
11987         if(o.afterStyle){
11988             this.applyStyles(o.afterStyle);
11989         }
11990         if(o.afterCls){
11991             this.addClass(o.afterCls);
11992         }
11993         if(o.remove === true){
11994             this.remove();
11995         }
11996         Roo.callback(o.callback, o.scope, [this]);
11997         if(!o.concurrent){
11998             this.fxQueue.shift();
11999             this.nextFx();
12000         }
12001     },
12002
12003         /* @private */
12004     getFxEl : function(){ // support for composite element fx
12005         return Roo.get(this.dom);
12006     },
12007
12008         /* @private */
12009     fxanim : function(args, opt, animType, defaultDur, defaultEase, cb){
12010         animType = animType || 'run';
12011         opt = opt || {};
12012         var anim = Roo.lib.Anim[animType](
12013             this.dom, args,
12014             (opt.duration || defaultDur) || .35,
12015             (opt.easing || defaultEase) || 'easeOut',
12016             function(){
12017                 Roo.callback(cb, this);
12018             },
12019             this
12020         );
12021         opt.anim = anim;
12022         return anim;
12023     }
12024 };
12025
12026 // backwords compat
12027 Roo.Fx.resize = Roo.Fx.scale;
12028
12029 //When included, Roo.Fx is automatically applied to Element so that all basic
12030 //effects are available directly via the Element API
12031 Roo.apply(Roo.Element.prototype, Roo.Fx);/*
12032  * Based on:
12033  * Ext JS Library 1.1.1
12034  * Copyright(c) 2006-2007, Ext JS, LLC.
12035  *
12036  * Originally Released Under LGPL - original licence link has changed is not relivant.
12037  *
12038  * Fork - LGPL
12039  * <script type="text/javascript">
12040  */
12041
12042
12043 /**
12044  * @class Roo.CompositeElement
12045  * Standard composite class. Creates a Roo.Element for every element in the collection.
12046  * <br><br>
12047  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12048  * actions will be performed on all the elements in this collection.</b>
12049  * <br><br>
12050  * All methods return <i>this</i> and can be chained.
12051  <pre><code>
12052  var els = Roo.select("#some-el div.some-class", true);
12053  // or select directly from an existing element
12054  var el = Roo.get('some-el');
12055  el.select('div.some-class', true);
12056
12057  els.setWidth(100); // all elements become 100 width
12058  els.hide(true); // all elements fade out and hide
12059  // or
12060  els.setWidth(100).hide(true);
12061  </code></pre>
12062  */
12063 Roo.CompositeElement = function(els){
12064     this.elements = [];
12065     this.addElements(els);
12066 };
12067 Roo.CompositeElement.prototype = {
12068     isComposite: true,
12069     addElements : function(els){
12070         if(!els) {
12071             return this;
12072         }
12073         if(typeof els == "string"){
12074             els = Roo.Element.selectorFunction(els);
12075         }
12076         var yels = this.elements;
12077         var index = yels.length-1;
12078         for(var i = 0, len = els.length; i < len; i++) {
12079                 yels[++index] = Roo.get(els[i]);
12080         }
12081         return this;
12082     },
12083
12084     /**
12085     * Clears this composite and adds the elements returned by the passed selector.
12086     * @param {String/Array} els A string CSS selector, an array of elements or an element
12087     * @return {CompositeElement} this
12088     */
12089     fill : function(els){
12090         this.elements = [];
12091         this.add(els);
12092         return this;
12093     },
12094
12095     /**
12096     * Filters this composite to only elements that match the passed selector.
12097     * @param {String} selector A string CSS selector
12098     * @param {Boolean} inverse return inverse filter (not matches)
12099     * @return {CompositeElement} this
12100     */
12101     filter : function(selector, inverse){
12102         var els = [];
12103         inverse = inverse || false;
12104         this.each(function(el){
12105             var match = inverse ? !el.is(selector) : el.is(selector);
12106             if(match){
12107                 els[els.length] = el.dom;
12108             }
12109         });
12110         this.fill(els);
12111         return this;
12112     },
12113
12114     invoke : function(fn, args){
12115         var els = this.elements;
12116         for(var i = 0, len = els.length; i < len; i++) {
12117                 Roo.Element.prototype[fn].apply(els[i], args);
12118         }
12119         return this;
12120     },
12121     /**
12122     * Adds elements to this composite.
12123     * @param {String/Array} els A string CSS selector, an array of elements or an element
12124     * @return {CompositeElement} this
12125     */
12126     add : function(els){
12127         if(typeof els == "string"){
12128             this.addElements(Roo.Element.selectorFunction(els));
12129         }else if(els.length !== undefined){
12130             this.addElements(els);
12131         }else{
12132             this.addElements([els]);
12133         }
12134         return this;
12135     },
12136     /**
12137     * Calls the passed function passing (el, this, index) for each element in this composite.
12138     * @param {Function} fn The function to call
12139     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12140     * @return {CompositeElement} this
12141     */
12142     each : function(fn, scope){
12143         var els = this.elements;
12144         for(var i = 0, len = els.length; i < len; i++){
12145             if(fn.call(scope || els[i], els[i], this, i) === false) {
12146                 break;
12147             }
12148         }
12149         return this;
12150     },
12151
12152     /**
12153      * Returns the Element object at the specified index
12154      * @param {Number} index
12155      * @return {Roo.Element}
12156      */
12157     item : function(index){
12158         return this.elements[index] || null;
12159     },
12160
12161     /**
12162      * Returns the first Element
12163      * @return {Roo.Element}
12164      */
12165     first : function(){
12166         return this.item(0);
12167     },
12168
12169     /**
12170      * Returns the last Element
12171      * @return {Roo.Element}
12172      */
12173     last : function(){
12174         return this.item(this.elements.length-1);
12175     },
12176
12177     /**
12178      * Returns the number of elements in this composite
12179      * @return Number
12180      */
12181     getCount : function(){
12182         return this.elements.length;
12183     },
12184
12185     /**
12186      * Returns true if this composite contains the passed element
12187      * @return Boolean
12188      */
12189     contains : function(el){
12190         return this.indexOf(el) !== -1;
12191     },
12192
12193     /**
12194      * Returns true if this composite contains the passed element
12195      * @return Boolean
12196      */
12197     indexOf : function(el){
12198         return this.elements.indexOf(Roo.get(el));
12199     },
12200
12201
12202     /**
12203     * Removes the specified element(s).
12204     * @param {Mixed} el The id of an element, the Element itself, the index of the element in this composite
12205     * or an array of any of those.
12206     * @param {Boolean} removeDom (optional) True to also remove the element from the document
12207     * @return {CompositeElement} this
12208     */
12209     removeElement : function(el, removeDom){
12210         if(el instanceof Array){
12211             for(var i = 0, len = el.length; i < len; i++){
12212                 this.removeElement(el[i]);
12213             }
12214             return this;
12215         }
12216         var index = typeof el == 'number' ? el : this.indexOf(el);
12217         if(index !== -1){
12218             if(removeDom){
12219                 var d = this.elements[index];
12220                 if(d.dom){
12221                     d.remove();
12222                 }else{
12223                     d.parentNode.removeChild(d);
12224                 }
12225             }
12226             this.elements.splice(index, 1);
12227         }
12228         return this;
12229     },
12230
12231     /**
12232     * Replaces the specified element with the passed element.
12233     * @param {String/HTMLElement/Element/Number} el The id of an element, the Element itself, the index of the element in this composite
12234     * to replace.
12235     * @param {String/HTMLElement/Element} replacement The id of an element or the Element itself.
12236     * @param {Boolean} domReplace (Optional) True to remove and replace the element in the document too.
12237     * @return {CompositeElement} this
12238     */
12239     replaceElement : function(el, replacement, domReplace){
12240         var index = typeof el == 'number' ? el : this.indexOf(el);
12241         if(index !== -1){
12242             if(domReplace){
12243                 this.elements[index].replaceWith(replacement);
12244             }else{
12245                 this.elements.splice(index, 1, Roo.get(replacement))
12246             }
12247         }
12248         return this;
12249     },
12250
12251     /**
12252      * Removes all elements.
12253      */
12254     clear : function(){
12255         this.elements = [];
12256     }
12257 };
12258 (function(){
12259     Roo.CompositeElement.createCall = function(proto, fnName){
12260         if(!proto[fnName]){
12261             proto[fnName] = function(){
12262                 return this.invoke(fnName, arguments);
12263             };
12264         }
12265     };
12266     for(var fnName in Roo.Element.prototype){
12267         if(typeof Roo.Element.prototype[fnName] == "function"){
12268             Roo.CompositeElement.createCall(Roo.CompositeElement.prototype, fnName);
12269         }
12270     };
12271 })();
12272 /*
12273  * Based on:
12274  * Ext JS Library 1.1.1
12275  * Copyright(c) 2006-2007, Ext JS, LLC.
12276  *
12277  * Originally Released Under LGPL - original licence link has changed is not relivant.
12278  *
12279  * Fork - LGPL
12280  * <script type="text/javascript">
12281  */
12282
12283 /**
12284  * @class Roo.CompositeElementLite
12285  * @extends Roo.CompositeElement
12286  * Flyweight composite class. Reuses the same Roo.Element for element operations.
12287  <pre><code>
12288  var els = Roo.select("#some-el div.some-class");
12289  // or select directly from an existing element
12290  var el = Roo.get('some-el');
12291  el.select('div.some-class');
12292
12293  els.setWidth(100); // all elements become 100 width
12294  els.hide(true); // all elements fade out and hide
12295  // or
12296  els.setWidth(100).hide(true);
12297  </code></pre><br><br>
12298  * <b>NOTE: Although they are not listed, this class supports all of the set/update methods of Roo.Element. All Roo.Element
12299  * actions will be performed on all the elements in this collection.</b>
12300  */
12301 Roo.CompositeElementLite = function(els){
12302     Roo.CompositeElementLite.superclass.constructor.call(this, els);
12303     this.el = new Roo.Element.Flyweight();
12304 };
12305 Roo.extend(Roo.CompositeElementLite, Roo.CompositeElement, {
12306     addElements : function(els){
12307         if(els){
12308             if(els instanceof Array){
12309                 this.elements = this.elements.concat(els);
12310             }else{
12311                 var yels = this.elements;
12312                 var index = yels.length-1;
12313                 for(var i = 0, len = els.length; i < len; i++) {
12314                     yels[++index] = els[i];
12315                 }
12316             }
12317         }
12318         return this;
12319     },
12320     invoke : function(fn, args){
12321         var els = this.elements;
12322         var el = this.el;
12323         for(var i = 0, len = els.length; i < len; i++) {
12324             el.dom = els[i];
12325                 Roo.Element.prototype[fn].apply(el, args);
12326         }
12327         return this;
12328     },
12329     /**
12330      * Returns a flyweight Element of the dom element object at the specified index
12331      * @param {Number} index
12332      * @return {Roo.Element}
12333      */
12334     item : function(index){
12335         if(!this.elements[index]){
12336             return null;
12337         }
12338         this.el.dom = this.elements[index];
12339         return this.el;
12340     },
12341
12342     // fixes scope with flyweight
12343     addListener : function(eventName, handler, scope, opt){
12344         var els = this.elements;
12345         for(var i = 0, len = els.length; i < len; i++) {
12346             Roo.EventManager.on(els[i], eventName, handler, scope || els[i], opt);
12347         }
12348         return this;
12349     },
12350
12351     /**
12352     * Calls the passed function passing (el, this, index) for each element in this composite. <b>The element
12353     * passed is the flyweight (shared) Roo.Element instance, so if you require a
12354     * a reference to the dom node, use el.dom.</b>
12355     * @param {Function} fn The function to call
12356     * @param {Object} scope (optional) The <i>this</i> object (defaults to the element)
12357     * @return {CompositeElement} this
12358     */
12359     each : function(fn, scope){
12360         var els = this.elements;
12361         var el = this.el;
12362         for(var i = 0, len = els.length; i < len; i++){
12363             el.dom = els[i];
12364                 if(fn.call(scope || el, el, this, i) === false){
12365                 break;
12366             }
12367         }
12368         return this;
12369     },
12370
12371     indexOf : function(el){
12372         return this.elements.indexOf(Roo.getDom(el));
12373     },
12374
12375     replaceElement : function(el, replacement, domReplace){
12376         var index = typeof el == 'number' ? el : this.indexOf(el);
12377         if(index !== -1){
12378             replacement = Roo.getDom(replacement);
12379             if(domReplace){
12380                 var d = this.elements[index];
12381                 d.parentNode.insertBefore(replacement, d);
12382                 d.parentNode.removeChild(d);
12383             }
12384             this.elements.splice(index, 1, replacement);
12385         }
12386         return this;
12387     }
12388 });
12389 Roo.CompositeElementLite.prototype.on = Roo.CompositeElementLite.prototype.addListener;
12390
12391 /*
12392  * Based on:
12393  * Ext JS Library 1.1.1
12394  * Copyright(c) 2006-2007, Ext JS, LLC.
12395  *
12396  * Originally Released Under LGPL - original licence link has changed is not relivant.
12397  *
12398  * Fork - LGPL
12399  * <script type="text/javascript">
12400  */
12401
12402  
12403
12404 /**
12405  * @class Roo.data.Connection
12406  * @extends Roo.util.Observable
12407  * The class encapsulates a connection to the page's originating domain, allowing requests to be made
12408  * either to a configured URL, or to a URL specified at request time. 
12409  * 
12410  * Requests made by this class are asynchronous, and will return immediately. No data from
12411  * the server will be available to the statement immediately following the {@link #request} call.
12412  * To process returned data, use a callback in the request options object, or an event listener.
12413  * 
12414  * Note: If you are doing a file upload, you will not get a normal response object sent back to
12415  * your callback or event handler.  Since the upload is handled via in IFRAME, there is no XMLHttpRequest.
12416  * The response object is created using the innerHTML of the IFRAME's document as the responseText
12417  * property and, if present, the IFRAME's XML document as the responseXML property.
12418  * 
12419  * This means that a valid XML or HTML document must be returned. If JSON data is required, it is suggested
12420  * that it be placed either inside a &lt;textarea> in an HTML document and retrieved from the responseText
12421  * using a regex, or inside a CDATA section in an XML document and retrieved from the responseXML using
12422  * standard DOM methods.
12423  * @constructor
12424  * @param {Object} config a configuration object.
12425  */
12426 Roo.data.Connection = function(config){
12427     Roo.apply(this, config);
12428     this.addEvents({
12429         /**
12430          * @event beforerequest
12431          * Fires before a network request is made to retrieve a data object.
12432          * @param {Connection} conn This Connection object.
12433          * @param {Object} options The options config object passed to the {@link #request} method.
12434          */
12435         "beforerequest" : true,
12436         /**
12437          * @event requestcomplete
12438          * Fires if the request was successfully completed.
12439          * @param {Connection} conn This Connection object.
12440          * @param {Object} response The XHR object containing the response data.
12441          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12442          * @param {Object} options The options config object passed to the {@link #request} method.
12443          */
12444         "requestcomplete" : true,
12445         /**
12446          * @event requestexception
12447          * Fires if an error HTTP status was returned from the server.
12448          * See {@link http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html} for details of HTTP status codes.
12449          * @param {Connection} conn This Connection object.
12450          * @param {Object} response The XHR object containing the response data.
12451          * See {@link http://www.w3.org/TR/XMLHttpRequest/} for details.
12452          * @param {Object} options The options config object passed to the {@link #request} method.
12453          */
12454         "requestexception" : true
12455     });
12456     Roo.data.Connection.superclass.constructor.call(this);
12457 };
12458
12459 Roo.extend(Roo.data.Connection, Roo.util.Observable, {
12460     /**
12461      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
12462      */
12463     /**
12464      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
12465      * extra parameters to each request made by this object. (defaults to undefined)
12466      */
12467     /**
12468      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
12469      *  to each request made by this object. (defaults to undefined)
12470      */
12471     /**
12472      * @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)
12473      */
12474     /**
12475      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12476      */
12477     timeout : 30000,
12478     /**
12479      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
12480      * @type Boolean
12481      */
12482     autoAbort:false,
12483
12484     /**
12485      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
12486      * @type Boolean
12487      */
12488     disableCaching: true,
12489
12490     /**
12491      * Sends an HTTP request to a remote server.
12492      * @param {Object} options An object which may contain the following properties:<ul>
12493      * <li><b>url</b> {String} (Optional) The URL to which to send the request. Defaults to configured URL</li>
12494      * <li><b>params</b> {Object/String/Function} (Optional) An object containing properties which are used as parameters to the
12495      * request, a url encoded string or a function to call to get either.</li>
12496      * <li><b>method</b> {String} (Optional) The HTTP method to use for the request. Defaults to the configured method, or
12497      * if no method was configured, "GET" if no parameters are being sent, and "POST" if parameters are being sent.</li>
12498      * <li><b>callback</b> {Function} (Optional) The function to be called upon receipt of the HTTP response.
12499      * The callback is called regardless of success or failure and is passed the following parameters:<ul>
12500      * <li>options {Object} The parameter to the request call.</li>
12501      * <li>success {Boolean} True if the request succeeded.</li>
12502      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12503      * </ul></li>
12504      * <li><b>success</b> {Function} (Optional) The function to be called upon success of the request.
12505      * The callback is passed the following parameters:<ul>
12506      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12507      * <li>options {Object} The parameter to the request call.</li>
12508      * </ul></li>
12509      * <li><b>failure</b> {Function} (Optional) The function to be called upon failure of the request.
12510      * The callback is passed the following parameters:<ul>
12511      * <li>response {Object} The XMLHttpRequest object containing the response data.</li>
12512      * <li>options {Object} The parameter to the request call.</li>
12513      * </ul></li>
12514      * <li><b>scope</b> {Object} (Optional) The scope in which to execute the callbacks: The "this" object
12515      * for the callback function. Defaults to the browser window.</li>
12516      * <li><b>form</b> {Object/String} (Optional) A form object or id to pull parameters from.</li>
12517      * <li><b>isUpload</b> {Boolean} (Optional) True if the form object is a file upload (will usually be automatically detected).</li>
12518      * <li><b>headers</b> {Object} (Optional) Request headers to set for the request.</li>
12519      * <li><b>xmlData</b> {Object} (Optional) XML document to use for the post. Note: This will be used instead of
12520      * params for the post data. Any params will be appended to the URL.</li>
12521      * <li><b>disableCaching</b> {Boolean} (Optional) True to add a unique cache-buster param to GET requests.</li>
12522      * </ul>
12523      * @return {Number} transactionId
12524      */
12525     request : function(o){
12526         if(this.fireEvent("beforerequest", this, o) !== false){
12527             var p = o.params;
12528
12529             if(typeof p == "function"){
12530                 p = p.call(o.scope||window, o);
12531             }
12532             if(typeof p == "object"){
12533                 p = Roo.urlEncode(o.params);
12534             }
12535             if(this.extraParams){
12536                 var extras = Roo.urlEncode(this.extraParams);
12537                 p = p ? (p + '&' + extras) : extras;
12538             }
12539
12540             var url = o.url || this.url;
12541             if(typeof url == 'function'){
12542                 url = url.call(o.scope||window, o);
12543             }
12544
12545             if(o.form){
12546                 var form = Roo.getDom(o.form);
12547                 url = url || form.action;
12548
12549                 var enctype = form.getAttribute("enctype");
12550                 
12551                 if (o.formData) {
12552                     return this.doFormDataUpload(o, url);
12553                 }
12554                 
12555                 if(o.isUpload || (enctype && enctype.toLowerCase() == 'multipart/form-data')){
12556                     return this.doFormUpload(o, p, url);
12557                 }
12558                 var f = Roo.lib.Ajax.serializeForm(form);
12559                 p = p ? (p + '&' + f) : f;
12560             }
12561             
12562             if (!o.form && o.formData) {
12563                 o.formData = o.formData === true ? new FormData() : o.formData;
12564                 for (var k in o.params) {
12565                     o.formData.append(k,o.params[k]);
12566                 }
12567                     
12568                 return this.doFormDataUpload(o, url);
12569             }
12570             
12571
12572             var hs = o.headers;
12573             if(this.defaultHeaders){
12574                 hs = Roo.apply(hs || {}, this.defaultHeaders);
12575                 if(!o.headers){
12576                     o.headers = hs;
12577                 }
12578             }
12579
12580             var cb = {
12581                 success: this.handleResponse,
12582                 failure: this.handleFailure,
12583                 scope: this,
12584                 argument: {options: o},
12585                 timeout : o.timeout || this.timeout
12586             };
12587
12588             var method = o.method||this.method||(p ? "POST" : "GET");
12589
12590             if(method == 'GET' && (this.disableCaching && o.disableCaching !== false) || o.disableCaching === true){
12591                 url += (url.indexOf('?') != -1 ? '&' : '?') + '_dc=' + (new Date().getTime());
12592             }
12593
12594             if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12595                 if(o.autoAbort){
12596                     this.abort();
12597                 }
12598             }else if(this.autoAbort !== false){
12599                 this.abort();
12600             }
12601
12602             if((method == 'GET' && p) || o.xmlData){
12603                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
12604                 p = '';
12605             }
12606             Roo.lib.Ajax.useDefaultHeader = typeof(o.headers) == 'undefined' || typeof(o.headers['Content-Type']) == 'undefined';
12607             this.transId = Roo.lib.Ajax.request(method, url, cb, p, o);
12608             Roo.lib.Ajax.useDefaultHeader == true;
12609             return this.transId;
12610         }else{
12611             Roo.callback(o.callback, o.scope, [o, null, null]);
12612             return null;
12613         }
12614     },
12615
12616     /**
12617      * Determine whether this object has a request outstanding.
12618      * @param {Number} transactionId (Optional) defaults to the last transaction
12619      * @return {Boolean} True if there is an outstanding request.
12620      */
12621     isLoading : function(transId){
12622         if(transId){
12623             return Roo.lib.Ajax.isCallInProgress(transId);
12624         }else{
12625             return this.transId ? true : false;
12626         }
12627     },
12628
12629     /**
12630      * Aborts any outstanding request.
12631      * @param {Number} transactionId (Optional) defaults to the last transaction
12632      */
12633     abort : function(transId){
12634         if(transId || this.isLoading()){
12635             Roo.lib.Ajax.abort(transId || this.transId);
12636         }
12637     },
12638
12639     // private
12640     handleResponse : function(response){
12641         this.transId = false;
12642         var options = response.argument.options;
12643         response.argument = options ? options.argument : null;
12644         this.fireEvent("requestcomplete", this, response, options);
12645         Roo.callback(options.success, options.scope, [response, options]);
12646         Roo.callback(options.callback, options.scope, [options, true, response]);
12647     },
12648
12649     // private
12650     handleFailure : function(response, e){
12651         this.transId = false;
12652         var options = response.argument.options;
12653         response.argument = options ? options.argument : null;
12654         this.fireEvent("requestexception", this, response, options, e);
12655         Roo.callback(options.failure, options.scope, [response, options]);
12656         Roo.callback(options.callback, options.scope, [options, false, response]);
12657     },
12658
12659     // private
12660     doFormUpload : function(o, ps, url){
12661         var id = Roo.id();
12662         var frame = document.createElement('iframe');
12663         frame.id = id;
12664         frame.name = id;
12665         frame.className = 'x-hidden';
12666         if(Roo.isIE){
12667             frame.src = Roo.SSL_SECURE_URL;
12668         }
12669         document.body.appendChild(frame);
12670
12671         if(Roo.isIE){
12672            document.frames[id].name = id;
12673         }
12674
12675         var form = Roo.getDom(o.form);
12676         form.target = id;
12677         form.method = 'POST';
12678         form.enctype = form.encoding = 'multipart/form-data';
12679         if(url){
12680             form.action = url;
12681         }
12682
12683         var hiddens, hd;
12684         if(ps){ // add dynamic params
12685             hiddens = [];
12686             ps = Roo.urlDecode(ps, false);
12687             for(var k in ps){
12688                 if(ps.hasOwnProperty(k)){
12689                     hd = document.createElement('input');
12690                     hd.type = 'hidden';
12691                     hd.name = k;
12692                     hd.value = ps[k];
12693                     form.appendChild(hd);
12694                     hiddens.push(hd);
12695                 }
12696             }
12697         }
12698
12699         function cb(){
12700             var r = {  // bogus response object
12701                 responseText : '',
12702                 responseXML : null
12703             };
12704
12705             r.argument = o ? o.argument : null;
12706
12707             try { //
12708                 var doc;
12709                 if(Roo.isIE){
12710                     doc = frame.contentWindow.document;
12711                 }else {
12712                     doc = (frame.contentDocument || window.frames[id].document);
12713                 }
12714                 if(doc && doc.body){
12715                     r.responseText = doc.body.innerHTML;
12716                 }
12717                 if(doc && doc.XMLDocument){
12718                     r.responseXML = doc.XMLDocument;
12719                 }else {
12720                     r.responseXML = doc;
12721                 }
12722             }
12723             catch(e) {
12724                 // ignore
12725             }
12726
12727             Roo.EventManager.removeListener(frame, 'load', cb, this);
12728
12729             this.fireEvent("requestcomplete", this, r, o);
12730             Roo.callback(o.success, o.scope, [r, o]);
12731             Roo.callback(o.callback, o.scope, [o, true, r]);
12732
12733             setTimeout(function(){document.body.removeChild(frame);}, 100);
12734         }
12735
12736         Roo.EventManager.on(frame, 'load', cb, this);
12737         form.submit();
12738
12739         if(hiddens){ // remove dynamic params
12740             for(var i = 0, len = hiddens.length; i < len; i++){
12741                 form.removeChild(hiddens[i]);
12742             }
12743         }
12744     },
12745     // this is a 'formdata version???'
12746     
12747     
12748     doFormDataUpload : function(o,  url)
12749     {
12750         var formData;
12751         if (o.form) {
12752             var form =  Roo.getDom(o.form);
12753             form.enctype = form.encoding = 'multipart/form-data';
12754             formData = o.formData === true ? new FormData(form) : o.formData;
12755         } else {
12756             formData = o.formData === true ? new FormData() : o.formData;
12757         }
12758         
12759       
12760         var cb = {
12761             success: this.handleResponse,
12762             failure: this.handleFailure,
12763             scope: this,
12764             argument: {options: o},
12765             timeout : o.timeout || this.timeout
12766         };
12767  
12768         if(typeof o.autoAbort == 'boolean'){ // options gets top priority
12769             if(o.autoAbort){
12770                 this.abort();
12771             }
12772         }else if(this.autoAbort !== false){
12773             this.abort();
12774         }
12775
12776         //Roo.lib.Ajax.defaultPostHeader = null;
12777         Roo.lib.Ajax.useDefaultHeader = false;
12778         this.transId = Roo.lib.Ajax.request( "POST", url, cb,  formData, o);
12779         Roo.lib.Ajax.useDefaultHeader = true;
12780  
12781          
12782     }
12783     
12784 });
12785 /*
12786  * Based on:
12787  * Ext JS Library 1.1.1
12788  * Copyright(c) 2006-2007, Ext JS, LLC.
12789  *
12790  * Originally Released Under LGPL - original licence link has changed is not relivant.
12791  *
12792  * Fork - LGPL
12793  * <script type="text/javascript">
12794  */
12795  
12796 /**
12797  * Global Ajax request class.
12798  * 
12799  * @class Roo.Ajax
12800  * @extends Roo.data.Connection
12801  * @static
12802  * 
12803  * @cfg {String} url  The default URL to be used for requests to the server. (defaults to undefined)
12804  * @cfg {Object} extraParams  An object containing properties which are used as extra parameters to each request made by this object. (defaults to undefined)
12805  * @cfg {Object} defaultHeaders  An object containing request headers which are added to each request made by this object. (defaults to undefined)
12806  * @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)
12807  * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
12808  * @cfg {Boolean} autoAbort (Optional) Whether a new request should abort any pending requests. (defaults to false)
12809  * @cfg {Boolean} disableCaching (Optional)   True to add a unique cache-buster param to GET requests. (defaults to true)
12810  */
12811 Roo.Ajax = new Roo.data.Connection({
12812     // fix up the docs
12813     /**
12814      * @scope Roo.Ajax
12815      * @type {Boolear} 
12816      */
12817     autoAbort : false,
12818
12819     /**
12820      * Serialize the passed form into a url encoded string
12821      * @scope Roo.Ajax
12822      * @param {String/HTMLElement} form
12823      * @return {String}
12824      */
12825     serializeForm : function(form){
12826         return Roo.lib.Ajax.serializeForm(form);
12827     }
12828 });/*
12829  * Based on:
12830  * Ext JS Library 1.1.1
12831  * Copyright(c) 2006-2007, Ext JS, LLC.
12832  *
12833  * Originally Released Under LGPL - original licence link has changed is not relivant.
12834  *
12835  * Fork - LGPL
12836  * <script type="text/javascript">
12837  */
12838
12839  
12840 /**
12841  * @class Roo.UpdateManager
12842  * @extends Roo.util.Observable
12843  * Provides AJAX-style update for Element object.<br><br>
12844  * Usage:<br>
12845  * <pre><code>
12846  * // Get it from a Roo.Element object
12847  * var el = Roo.get("foo");
12848  * var mgr = el.getUpdateManager();
12849  * mgr.update("http://myserver.com/index.php", "param1=1&amp;param2=2");
12850  * ...
12851  * mgr.formUpdate("myFormId", "http://myserver.com/index.php");
12852  * <br>
12853  * // or directly (returns the same UpdateManager instance)
12854  * var mgr = new Roo.UpdateManager("myElementId");
12855  * mgr.startAutoRefresh(60, "http://myserver.com/index.php");
12856  * mgr.on("update", myFcnNeedsToKnow);
12857  * <br>
12858    // short handed call directly from the element object
12859    Roo.get("foo").load({
12860         url: "bar.php",
12861         scripts:true,
12862         params: "for=bar",
12863         text: "Loading Foo..."
12864    });
12865  * </code></pre>
12866  * @constructor
12867  * Create new UpdateManager directly.
12868  * @param {String/HTMLElement/Roo.Element} el The element to update
12869  * @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).
12870  */
12871 Roo.UpdateManager = function(el, forceNew){
12872     el = Roo.get(el);
12873     if(!forceNew && el.updateManager){
12874         return el.updateManager;
12875     }
12876     /**
12877      * The Element object
12878      * @type Roo.Element
12879      */
12880     this.el = el;
12881     /**
12882      * Cached url to use for refreshes. Overwritten every time update() is called unless "discardUrl" param is set to true.
12883      * @type String
12884      */
12885     this.defaultUrl = null;
12886
12887     this.addEvents({
12888         /**
12889          * @event beforeupdate
12890          * Fired before an update is made, return false from your handler and the update is cancelled.
12891          * @param {Roo.Element} el
12892          * @param {String/Object/Function} url
12893          * @param {String/Object} params
12894          */
12895         "beforeupdate": true,
12896         /**
12897          * @event update
12898          * Fired after successful update is made.
12899          * @param {Roo.Element} el
12900          * @param {Object} oResponseObject The response Object
12901          */
12902         "update": true,
12903         /**
12904          * @event failure
12905          * Fired on update failure.
12906          * @param {Roo.Element} el
12907          * @param {Object} oResponseObject The response Object
12908          */
12909         "failure": true
12910     });
12911     var d = Roo.UpdateManager.defaults;
12912     /**
12913      * Blank page URL to use with SSL file uploads (Defaults to Roo.UpdateManager.defaults.sslBlankUrl or "about:blank").
12914      * @type String
12915      */
12916     this.sslBlankUrl = d.sslBlankUrl;
12917     /**
12918      * Whether to append unique parameter on get request to disable caching (Defaults to Roo.UpdateManager.defaults.disableCaching or false).
12919      * @type Boolean
12920      */
12921     this.disableCaching = d.disableCaching;
12922     /**
12923      * Text for loading indicator (Defaults to Roo.UpdateManager.defaults.indicatorText or '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
12924      * @type String
12925      */
12926     this.indicatorText = d.indicatorText;
12927     /**
12928      * Whether to show indicatorText when loading (Defaults to Roo.UpdateManager.defaults.showLoadIndicator or true).
12929      * @type String
12930      */
12931     this.showLoadIndicator = d.showLoadIndicator;
12932     /**
12933      * Timeout for requests or form posts in seconds (Defaults to Roo.UpdateManager.defaults.timeout or 30 seconds).
12934      * @type Number
12935      */
12936     this.timeout = d.timeout;
12937
12938     /**
12939      * True to process scripts in the output (Defaults to Roo.UpdateManager.defaults.loadScripts (false)).
12940      * @type Boolean
12941      */
12942     this.loadScripts = d.loadScripts;
12943
12944     /**
12945      * Transaction object of current executing transaction
12946      */
12947     this.transaction = null;
12948
12949     /**
12950      * @private
12951      */
12952     this.autoRefreshProcId = null;
12953     /**
12954      * Delegate for refresh() prebound to "this", use myUpdater.refreshDelegate.createCallback(arg1, arg2) to bind arguments
12955      * @type Function
12956      */
12957     this.refreshDelegate = this.refresh.createDelegate(this);
12958     /**
12959      * Delegate for update() prebound to "this", use myUpdater.updateDelegate.createCallback(arg1, arg2) to bind arguments
12960      * @type Function
12961      */
12962     this.updateDelegate = this.update.createDelegate(this);
12963     /**
12964      * Delegate for formUpdate() prebound to "this", use myUpdater.formUpdateDelegate.createCallback(arg1, arg2) to bind arguments
12965      * @type Function
12966      */
12967     this.formUpdateDelegate = this.formUpdate.createDelegate(this);
12968     /**
12969      * @private
12970      */
12971     this.successDelegate = this.processSuccess.createDelegate(this);
12972     /**
12973      * @private
12974      */
12975     this.failureDelegate = this.processFailure.createDelegate(this);
12976
12977     if(!this.renderer){
12978      /**
12979       * The renderer for this UpdateManager. Defaults to {@link Roo.UpdateManager.BasicRenderer}.
12980       */
12981     this.renderer = new Roo.UpdateManager.BasicRenderer();
12982     }
12983     
12984     Roo.UpdateManager.superclass.constructor.call(this);
12985 };
12986
12987 Roo.extend(Roo.UpdateManager, Roo.util.Observable, {
12988     /**
12989      * Get the Element this UpdateManager is bound to
12990      * @return {Roo.Element} The element
12991      */
12992     getEl : function(){
12993         return this.el;
12994     },
12995     /**
12996      * Performs an async request, updating this element with the response. If params are specified it uses POST, otherwise it uses GET.
12997      * @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:
12998 <pre><code>
12999 um.update({<br/>
13000     url: "your-url.php",<br/>
13001     params: {param1: "foo", param2: "bar"}, // or a URL encoded string<br/>
13002     callback: yourFunction,<br/>
13003     scope: yourObject, //(optional scope)  <br/>
13004     discardUrl: false, <br/>
13005     nocache: false,<br/>
13006     text: "Loading...",<br/>
13007     timeout: 30,<br/>
13008     scripts: false<br/>
13009 });
13010 </code></pre>
13011      * The only required property is url. The optional properties nocache, text and scripts
13012      * are shorthand for disableCaching, indicatorText and loadScripts and are used to set their associated property on this UpdateManager instance.
13013      * @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}
13014      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13015      * @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.
13016      */
13017     update : function(url, params, callback, discardUrl){
13018         if(this.fireEvent("beforeupdate", this.el, url, params) !== false){
13019             var method = this.method,
13020                 cfg;
13021             if(typeof url == "object"){ // must be config object
13022                 cfg = url;
13023                 url = cfg.url;
13024                 params = params || cfg.params;
13025                 callback = callback || cfg.callback;
13026                 discardUrl = discardUrl || cfg.discardUrl;
13027                 if(callback && cfg.scope){
13028                     callback = callback.createDelegate(cfg.scope);
13029                 }
13030                 if(typeof cfg.method != "undefined"){method = cfg.method;};
13031                 if(typeof cfg.nocache != "undefined"){this.disableCaching = cfg.nocache;};
13032                 if(typeof cfg.text != "undefined"){this.indicatorText = '<div class="loading-indicator">'+cfg.text+"</div>";};
13033                 if(typeof cfg.scripts != "undefined"){this.loadScripts = cfg.scripts;};
13034                 if(typeof cfg.timeout != "undefined"){this.timeout = cfg.timeout;};
13035             }
13036             this.showLoading();
13037             if(!discardUrl){
13038                 this.defaultUrl = url;
13039             }
13040             if(typeof url == "function"){
13041                 url = url.call(this);
13042             }
13043
13044             method = method || (params ? "POST" : "GET");
13045             if(method == "GET"){
13046                 url = this.prepareUrl(url);
13047             }
13048
13049             var o = Roo.apply(cfg ||{}, {
13050                 url : url,
13051                 params: params,
13052                 success: this.successDelegate,
13053                 failure: this.failureDelegate,
13054                 callback: undefined,
13055                 timeout: (this.timeout*1000),
13056                 argument: {"url": url, "form": null, "callback": callback, "params": params}
13057             });
13058             Roo.log("updated manager called with timeout of " + o.timeout);
13059             this.transaction = Roo.Ajax.request(o);
13060         }
13061     },
13062
13063     /**
13064      * 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.
13065      * Uses this.sslBlankUrl for SSL file uploads to prevent IE security warning.
13066      * @param {String/HTMLElement} form The form Id or form element
13067      * @param {String} url (optional) The url to pass the form to. If omitted the action attribute on the form will be used.
13068      * @param {Boolean} reset (optional) Whether to try to reset the form after the update
13069      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess, oResponse)
13070      */
13071     formUpdate : function(form, url, reset, callback){
13072         if(this.fireEvent("beforeupdate", this.el, form, url) !== false){
13073             if(typeof url == "function"){
13074                 url = url.call(this);
13075             }
13076             form = Roo.getDom(form);
13077             this.transaction = Roo.Ajax.request({
13078                 form: form,
13079                 url:url,
13080                 success: this.successDelegate,
13081                 failure: this.failureDelegate,
13082                 timeout: (this.timeout*1000),
13083                 argument: {"url": url, "form": form, "callback": callback, "reset": reset}
13084             });
13085             this.showLoading.defer(1, this);
13086         }
13087     },
13088
13089     /**
13090      * Refresh the element with the last used url or defaultUrl. If there is no url, it returns immediately
13091      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13092      */
13093     refresh : function(callback){
13094         if(this.defaultUrl == null){
13095             return;
13096         }
13097         this.update(this.defaultUrl, null, callback, true);
13098     },
13099
13100     /**
13101      * Set this element to auto refresh.
13102      * @param {Number} interval How often to update (in seconds).
13103      * @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)
13104      * @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}
13105      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
13106      * @param {Boolean} refreshNow (optional) Whether to execute the refresh now, or wait the interval
13107      */
13108     startAutoRefresh : function(interval, url, params, callback, refreshNow){
13109         if(refreshNow){
13110             this.update(url || this.defaultUrl, params, callback, true);
13111         }
13112         if(this.autoRefreshProcId){
13113             clearInterval(this.autoRefreshProcId);
13114         }
13115         this.autoRefreshProcId = setInterval(this.update.createDelegate(this, [url || this.defaultUrl, params, callback, true]), interval*1000);
13116     },
13117
13118     /**
13119      * Stop auto refresh on this element.
13120      */
13121      stopAutoRefresh : function(){
13122         if(this.autoRefreshProcId){
13123             clearInterval(this.autoRefreshProcId);
13124             delete this.autoRefreshProcId;
13125         }
13126     },
13127
13128     isAutoRefreshing : function(){
13129        return this.autoRefreshProcId ? true : false;
13130     },
13131     /**
13132      * Called to update the element to "Loading" state. Override to perform custom action.
13133      */
13134     showLoading : function(){
13135         if(this.showLoadIndicator){
13136             this.el.update(this.indicatorText);
13137         }
13138     },
13139
13140     /**
13141      * Adds unique parameter to query string if disableCaching = true
13142      * @private
13143      */
13144     prepareUrl : function(url){
13145         if(this.disableCaching){
13146             var append = "_dc=" + (new Date().getTime());
13147             if(url.indexOf("?") !== -1){
13148                 url += "&" + append;
13149             }else{
13150                 url += "?" + append;
13151             }
13152         }
13153         return url;
13154     },
13155
13156     /**
13157      * @private
13158      */
13159     processSuccess : function(response){
13160         this.transaction = null;
13161         if(response.argument.form && response.argument.reset){
13162             try{ // put in try/catch since some older FF releases had problems with this
13163                 response.argument.form.reset();
13164             }catch(e){}
13165         }
13166         if(this.loadScripts){
13167             this.renderer.render(this.el, response, this,
13168                 this.updateComplete.createDelegate(this, [response]));
13169         }else{
13170             this.renderer.render(this.el, response, this);
13171             this.updateComplete(response);
13172         }
13173     },
13174
13175     updateComplete : function(response){
13176         this.fireEvent("update", this.el, response);
13177         if(typeof response.argument.callback == "function"){
13178             response.argument.callback(this.el, true, response);
13179         }
13180     },
13181
13182     /**
13183      * @private
13184      */
13185     processFailure : function(response){
13186         this.transaction = null;
13187         this.fireEvent("failure", this.el, response);
13188         if(typeof response.argument.callback == "function"){
13189             response.argument.callback(this.el, false, response);
13190         }
13191     },
13192
13193     /**
13194      * Set the content renderer for this UpdateManager. See {@link Roo.UpdateManager.BasicRenderer#render} for more details.
13195      * @param {Object} renderer The object implementing the render() method
13196      */
13197     setRenderer : function(renderer){
13198         this.renderer = renderer;
13199     },
13200
13201     getRenderer : function(){
13202        return this.renderer;
13203     },
13204
13205     /**
13206      * Set the defaultUrl used for updates
13207      * @param {String/Function} defaultUrl The url or a function to call to get the url
13208      */
13209     setDefaultUrl : function(defaultUrl){
13210         this.defaultUrl = defaultUrl;
13211     },
13212
13213     /**
13214      * Aborts the executing transaction
13215      */
13216     abort : function(){
13217         if(this.transaction){
13218             Roo.Ajax.abort(this.transaction);
13219         }
13220     },
13221
13222     /**
13223      * Returns true if an update is in progress
13224      * @return {Boolean}
13225      */
13226     isUpdating : function(){
13227         if(this.transaction){
13228             return Roo.Ajax.isLoading(this.transaction);
13229         }
13230         return false;
13231     }
13232 });
13233
13234 /**
13235  * @class Roo.UpdateManager.defaults
13236  * @static (not really - but it helps the doc tool)
13237  * The defaults collection enables customizing the default properties of UpdateManager
13238  */
13239    Roo.UpdateManager.defaults = {
13240        /**
13241          * Timeout for requests or form posts in seconds (Defaults 30 seconds).
13242          * @type Number
13243          */
13244          timeout : 30,
13245
13246          /**
13247          * True to process scripts by default (Defaults to false).
13248          * @type Boolean
13249          */
13250         loadScripts : false,
13251
13252         /**
13253         * Blank page URL to use with SSL file uploads (Defaults to "javascript:false").
13254         * @type String
13255         */
13256         sslBlankUrl : (Roo.SSL_SECURE_URL || "javascript:false"),
13257         /**
13258          * Whether to append unique parameter on get request to disable caching (Defaults to false).
13259          * @type Boolean
13260          */
13261         disableCaching : false,
13262         /**
13263          * Whether to show indicatorText when loading (Defaults to true).
13264          * @type Boolean
13265          */
13266         showLoadIndicator : true,
13267         /**
13268          * Text for loading indicator (Defaults to '&lt;div class="loading-indicator"&gt;Loading...&lt;/div&gt;').
13269          * @type String
13270          */
13271         indicatorText : '<div class="loading-indicator">Loading...</div>'
13272    };
13273
13274 /**
13275  * Static convenience method. This method is deprecated in favor of el.load({url:'foo.php', ...}).
13276  *Usage:
13277  * <pre><code>Roo.UpdateManager.updateElement("my-div", "stuff.php");</code></pre>
13278  * @param {String/HTMLElement/Roo.Element} el The element to update
13279  * @param {String} url The url
13280  * @param {String/Object} params (optional) Url encoded param string or an object of name/value pairs
13281  * @param {Object} options (optional) A config object with any of the UpdateManager properties you want to set - for example: {disableCaching:true, indicatorText: "Loading data..."}
13282  * @static
13283  * @deprecated
13284  * @member Roo.UpdateManager
13285  */
13286 Roo.UpdateManager.updateElement = function(el, url, params, options){
13287     var um = Roo.get(el, true).getUpdateManager();
13288     Roo.apply(um, options);
13289     um.update(url, params, options ? options.callback : null);
13290 };
13291 // alias for backwards compat
13292 Roo.UpdateManager.update = Roo.UpdateManager.updateElement;
13293 /**
13294  * @class Roo.UpdateManager.BasicRenderer
13295  * Default Content renderer. Updates the elements innerHTML with the responseText.
13296  */
13297 Roo.UpdateManager.BasicRenderer = function(){};
13298
13299 Roo.UpdateManager.BasicRenderer.prototype = {
13300     /**
13301      * This is called when the transaction is completed and it's time to update the element - The BasicRenderer
13302      * updates the elements innerHTML with the responseText - To perform a custom render (i.e. XML or JSON processing),
13303      * create an object with a "render(el, response)" method and pass it to setRenderer on the UpdateManager.
13304      * @param {Roo.Element} el The element being rendered
13305      * @param {Object} response The YUI Connect response object
13306      * @param {UpdateManager} updateManager The calling update manager
13307      * @param {Function} callback A callback that will need to be called if loadScripts is true on the UpdateManager
13308      */
13309      render : function(el, response, updateManager, callback){
13310         el.update(response.responseText, updateManager.loadScripts, callback);
13311     }
13312 };
13313 /*
13314  * Based on:
13315  * Roo JS
13316  * (c)) Alan Knowles
13317  * Licence : LGPL
13318  */
13319
13320
13321 /**
13322  * @class Roo.DomTemplate
13323  * @extends Roo.Template
13324  * An effort at a dom based template engine..
13325  *
13326  * Similar to XTemplate, except it uses dom parsing to create the template..
13327  *
13328  * Supported features:
13329  *
13330  *  Tags:
13331
13332 <pre><code>
13333       {a_variable} - output encoded.
13334       {a_variable.format:("Y-m-d")} - call a method on the variable
13335       {a_variable:raw} - unencoded output
13336       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
13337       {a_variable:this.method_on_template(...)} - call a method on the template object.
13338  
13339 </code></pre>
13340  *  The tpl tag:
13341 <pre><code>
13342         &lt;div roo-for="a_variable or condition.."&gt;&lt;/div&gt;
13343         &lt;div roo-if="a_variable or condition"&gt;&lt;/div&gt;
13344         &lt;div roo-exec="some javascript"&gt;&lt;/div&gt;
13345         &lt;div roo-name="named_template"&gt;&lt;/div&gt; 
13346   
13347 </code></pre>
13348  *      
13349  */
13350 Roo.DomTemplate = function()
13351 {
13352      Roo.DomTemplate.superclass.constructor.apply(this, arguments);
13353      if (this.html) {
13354         this.compile();
13355      }
13356 };
13357
13358
13359 Roo.extend(Roo.DomTemplate, Roo.Template, {
13360     /**
13361      * id counter for sub templates.
13362      */
13363     id : 0,
13364     /**
13365      * flag to indicate if dom parser is inside a pre,
13366      * it will strip whitespace if not.
13367      */
13368     inPre : false,
13369     
13370     /**
13371      * The various sub templates
13372      */
13373     tpls : false,
13374     
13375     
13376     
13377     /**
13378      *
13379      * basic tag replacing syntax
13380      * WORD:WORD()
13381      *
13382      * // you can fake an object call by doing this
13383      *  x.t:(test,tesT) 
13384      * 
13385      */
13386     re : /(\{|\%7B)([\w-\.]+)(?:\:([\w\.]*)(?:\(([^)]*?)?\))?)?(\}|\%7D)/g,
13387     //re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
13388     
13389     iterChild : function (node, method) {
13390         
13391         var oldPre = this.inPre;
13392         if (node.tagName == 'PRE') {
13393             this.inPre = true;
13394         }
13395         for( var i = 0; i < node.childNodes.length; i++) {
13396             method.call(this, node.childNodes[i]);
13397         }
13398         this.inPre = oldPre;
13399     },
13400     
13401     
13402     
13403     /**
13404      * compile the template
13405      *
13406      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
13407      *
13408      */
13409     compile: function()
13410     {
13411         var s = this.html;
13412         
13413         // covert the html into DOM...
13414         var doc = false;
13415         var div =false;
13416         try {
13417             doc = document.implementation.createHTMLDocument("");
13418             doc.documentElement.innerHTML =   this.html  ;
13419             div = doc.documentElement;
13420         } catch (e) {
13421             // old IE... - nasty -- it causes all sorts of issues.. with
13422             // images getting pulled from server..
13423             div = document.createElement('div');
13424             div.innerHTML = this.html;
13425         }
13426         //doc.documentElement.innerHTML = htmlBody
13427          
13428         
13429         
13430         this.tpls = [];
13431         var _t = this;
13432         this.iterChild(div, function(n) {_t.compileNode(n, true); });
13433         
13434         var tpls = this.tpls;
13435         
13436         // create a top level template from the snippet..
13437         
13438         //Roo.log(div.innerHTML);
13439         
13440         var tpl = {
13441             uid : 'master',
13442             id : this.id++,
13443             attr : false,
13444             value : false,
13445             body : div.innerHTML,
13446             
13447             forCall : false,
13448             execCall : false,
13449             dom : div,
13450             isTop : true
13451             
13452         };
13453         tpls.unshift(tpl);
13454         
13455         
13456         // compile them...
13457         this.tpls = [];
13458         Roo.each(tpls, function(tp){
13459             this.compileTpl(tp);
13460             this.tpls[tp.id] = tp;
13461         }, this);
13462         
13463         this.master = tpls[0];
13464         return this;
13465         
13466         
13467     },
13468     
13469     compileNode : function(node, istop) {
13470         // test for
13471         //Roo.log(node);
13472         
13473         
13474         // skip anything not a tag..
13475         if (node.nodeType != 1) {
13476             if (node.nodeType == 3 && !this.inPre) {
13477                 // reduce white space..
13478                 node.nodeValue = node.nodeValue.replace(/\s+/g, ' '); 
13479                 
13480             }
13481             return;
13482         }
13483         
13484         var tpl = {
13485             uid : false,
13486             id : false,
13487             attr : false,
13488             value : false,
13489             body : '',
13490             
13491             forCall : false,
13492             execCall : false,
13493             dom : false,
13494             isTop : istop
13495             
13496             
13497         };
13498         
13499         
13500         switch(true) {
13501             case (node.hasAttribute('roo-for')): tpl.attr = 'for'; break;
13502             case (node.hasAttribute('roo-if')): tpl.attr = 'if'; break;
13503             case (node.hasAttribute('roo-name')): tpl.attr = 'name'; break;
13504             case (node.hasAttribute('roo-exec')): tpl.attr = 'exec'; break;
13505             // no default..
13506         }
13507         
13508         
13509         if (!tpl.attr) {
13510             // just itterate children..
13511             this.iterChild(node,this.compileNode);
13512             return;
13513         }
13514         tpl.uid = this.id++;
13515         tpl.value = node.getAttribute('roo-' +  tpl.attr);
13516         node.removeAttribute('roo-'+ tpl.attr);
13517         if (tpl.attr != 'name') {
13518             var placeholder = document.createTextNode('{domtpl' + tpl.uid + '}');
13519             node.parentNode.replaceChild(placeholder,  node);
13520         } else {
13521             
13522             var placeholder =  document.createElement('span');
13523             placeholder.className = 'roo-tpl-' + tpl.value;
13524             node.parentNode.replaceChild(placeholder,  node);
13525         }
13526         
13527         // parent now sees '{domtplXXXX}
13528         this.iterChild(node,this.compileNode);
13529         
13530         // we should now have node body...
13531         var div = document.createElement('div');
13532         div.appendChild(node);
13533         tpl.dom = node;
13534         // this has the unfortunate side effect of converting tagged attributes
13535         // eg. href="{...}" into %7C...%7D
13536         // this has been fixed by searching for those combo's although it's a bit hacky..
13537         
13538         
13539         tpl.body = div.innerHTML;
13540         
13541         
13542          
13543         tpl.id = tpl.uid;
13544         switch(tpl.attr) {
13545             case 'for' :
13546                 switch (tpl.value) {
13547                     case '.':  tpl.forCall = new Function('values', 'parent', 'with(values){ return values; }'); break;
13548                     case '..': tpl.forCall= new Function('values', 'parent', 'with(values){ return parent; }'); break;
13549                     default:   tpl.forCall= new Function('values', 'parent', 'with(values){ return '+tpl.value+'; }');
13550                 }
13551                 break;
13552             
13553             case 'exec':
13554                 tpl.execCall = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13555                 break;
13556             
13557             case 'if':     
13558                 tpl.ifCall = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(tpl.value))+'; }');
13559                 break;
13560             
13561             case 'name':
13562                 tpl.id  = tpl.value; // replace non characters???
13563                 break;
13564             
13565         }
13566         
13567         
13568         this.tpls.push(tpl);
13569         
13570         
13571         
13572     },
13573     
13574     
13575     
13576     
13577     /**
13578      * Compile a segment of the template into a 'sub-template'
13579      *
13580      * 
13581      * 
13582      *
13583      */
13584     compileTpl : function(tpl)
13585     {
13586         var fm = Roo.util.Format;
13587         var useF = this.disableFormats !== true;
13588         
13589         var sep = Roo.isGecko ? "+\n" : ",\n";
13590         
13591         var undef = function(str) {
13592             Roo.debug && Roo.log("Property not found :"  + str);
13593             return '';
13594         };
13595           
13596         //Roo.log(tpl.body);
13597         
13598         
13599         
13600         var fn = function(m, lbrace, name, format, args)
13601         {
13602             //Roo.log("ARGS");
13603             //Roo.log(arguments);
13604             args = args ? args.replace(/\\'/g,"'") : args;
13605             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
13606             if (typeof(format) == 'undefined') {
13607                 format =  'htmlEncode'; 
13608             }
13609             if (format == 'raw' ) {
13610                 format = false;
13611             }
13612             
13613             if(name.substr(0, 6) == 'domtpl'){
13614                 return "'"+ sep +'this.applySubTemplate('+name.substr(6)+', values, parent)'+sep+"'";
13615             }
13616             
13617             // build an array of options to determine if value is undefined..
13618             
13619             // basically get 'xxxx.yyyy' then do
13620             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
13621             //    (function () { Roo.log("Property not found"); return ''; })() :
13622             //    ......
13623             
13624             var udef_ar = [];
13625             var lookfor = '';
13626             Roo.each(name.split('.'), function(st) {
13627                 lookfor += (lookfor.length ? '.': '') + st;
13628                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
13629             });
13630             
13631             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
13632             
13633             
13634             if(format && useF){
13635                 
13636                 args = args ? ',' + args : "";
13637                  
13638                 if(format.substr(0, 5) != "this."){
13639                     format = "fm." + format + '(';
13640                 }else{
13641                     format = 'this.call("'+ format.substr(5) + '", ';
13642                     args = ", values";
13643                 }
13644                 
13645                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
13646             }
13647              
13648             if (args && args.length) {
13649                 // called with xxyx.yuu:(test,test)
13650                 // change to ()
13651                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
13652             }
13653             // raw.. - :raw modifier..
13654             return "'"+ sep + udef_st  + name + ")"+sep+"'";
13655             
13656         };
13657         var body;
13658         // branched to use + in gecko and [].join() in others
13659         if(Roo.isGecko){
13660             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
13661                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
13662                     "';};};";
13663         }else{
13664             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
13665             body.push(tpl.body.replace(/(\r\n|\n)/g,
13666                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
13667             body.push("'].join('');};};");
13668             body = body.join('');
13669         }
13670         
13671         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
13672        
13673         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
13674         eval(body);
13675         
13676         return this;
13677     },
13678      
13679     /**
13680      * same as applyTemplate, except it's done to one of the subTemplates
13681      * when using named templates, you can do:
13682      *
13683      * var str = pl.applySubTemplate('your-name', values);
13684      *
13685      * 
13686      * @param {Number} id of the template
13687      * @param {Object} values to apply to template
13688      * @param {Object} parent (normaly the instance of this object)
13689      */
13690     applySubTemplate : function(id, values, parent)
13691     {
13692         
13693         
13694         var t = this.tpls[id];
13695         
13696         
13697         try { 
13698             if(t.ifCall && !t.ifCall.call(this, values, parent)){
13699                 Roo.debug && Roo.log('if call on ' + t.value + ' return false');
13700                 return '';
13701             }
13702         } catch(e) {
13703             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-if="' + t.value + '" - ' + e.toString());
13704             Roo.log(values);
13705           
13706             return '';
13707         }
13708         try { 
13709             
13710             if(t.execCall && t.execCall.call(this, values, parent)){
13711                 return '';
13712             }
13713         } catch(e) {
13714             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13715             Roo.log(values);
13716             return '';
13717         }
13718         
13719         try {
13720             var vs = t.forCall ? t.forCall.call(this, values, parent) : values;
13721             parent = t.target ? values : parent;
13722             if(t.forCall && vs instanceof Array){
13723                 var buf = [];
13724                 for(var i = 0, len = vs.length; i < len; i++){
13725                     try {
13726                         buf[buf.length] = t.compiled.call(this, vs[i], parent);
13727                     } catch (e) {
13728                         Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13729                         Roo.log(e.body);
13730                         //Roo.log(t.compiled);
13731                         Roo.log(vs[i]);
13732                     }   
13733                 }
13734                 return buf.join('');
13735             }
13736         } catch (e) {
13737             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on roo-for="' + t.value + '" - ' + e.toString());
13738             Roo.log(values);
13739             return '';
13740         }
13741         try {
13742             return t.compiled.call(this, vs, parent);
13743         } catch (e) {
13744             Roo.log('Xtemplate.applySubTemplate('+ id+ '): Exception thrown on body="' + t.value + '" - ' + e.toString());
13745             Roo.log(e.body);
13746             //Roo.log(t.compiled);
13747             Roo.log(values);
13748             return '';
13749         }
13750     },
13751
13752    
13753
13754     applyTemplate : function(values){
13755         return this.master.compiled.call(this, values, {});
13756         //var s = this.subs;
13757     },
13758
13759     apply : function(){
13760         return this.applyTemplate.apply(this, arguments);
13761     }
13762
13763  });
13764
13765 Roo.DomTemplate.from = function(el){
13766     el = Roo.getDom(el);
13767     return new Roo.Domtemplate(el.value || el.innerHTML);
13768 };/*
13769  * Based on:
13770  * Ext JS Library 1.1.1
13771  * Copyright(c) 2006-2007, Ext JS, LLC.
13772  *
13773  * Originally Released Under LGPL - original licence link has changed is not relivant.
13774  *
13775  * Fork - LGPL
13776  * <script type="text/javascript">
13777  */
13778
13779 /**
13780  * @class Roo.util.DelayedTask
13781  * Provides a convenient method of performing setTimeout where a new
13782  * timeout cancels the old timeout. An example would be performing validation on a keypress.
13783  * You can use this class to buffer
13784  * the keypress events for a certain number of milliseconds, and perform only if they stop
13785  * for that amount of time.
13786  * @constructor The parameters to this constructor serve as defaults and are not required.
13787  * @param {Function} fn (optional) The default function to timeout
13788  * @param {Object} scope (optional) The default scope of that timeout
13789  * @param {Array} args (optional) The default Array of arguments
13790  */
13791 Roo.util.DelayedTask = function(fn, scope, args){
13792     var id = null, d, t;
13793
13794     var call = function(){
13795         var now = new Date().getTime();
13796         if(now - t >= d){
13797             clearInterval(id);
13798             id = null;
13799             fn.apply(scope, args || []);
13800         }
13801     };
13802     /**
13803      * Cancels any pending timeout and queues a new one
13804      * @param {Number} delay The milliseconds to delay
13805      * @param {Function} newFn (optional) Overrides function passed to constructor
13806      * @param {Object} newScope (optional) Overrides scope passed to constructor
13807      * @param {Array} newArgs (optional) Overrides args passed to constructor
13808      */
13809     this.delay = function(delay, newFn, newScope, newArgs){
13810         if(id && delay != d){
13811             this.cancel();
13812         }
13813         d = delay;
13814         t = new Date().getTime();
13815         fn = newFn || fn;
13816         scope = newScope || scope;
13817         args = newArgs || args;
13818         if(!id){
13819             id = setInterval(call, d);
13820         }
13821     };
13822
13823     /**
13824      * Cancel the last queued timeout
13825      */
13826     this.cancel = function(){
13827         if(id){
13828             clearInterval(id);
13829             id = null;
13830         }
13831     };
13832 };/*
13833  * Based on:
13834  * Ext JS Library 1.1.1
13835  * Copyright(c) 2006-2007, Ext JS, LLC.
13836  *
13837  * Originally Released Under LGPL - original licence link has changed is not relivant.
13838  *
13839  * Fork - LGPL
13840  * <script type="text/javascript">
13841  */
13842 /**
13843  * @class Roo.util.TaskRunner
13844  * Manage background tasks - not sure why this is better that setInterval?
13845  * @static
13846  *
13847  */
13848  
13849 Roo.util.TaskRunner = function(interval){
13850     interval = interval || 10;
13851     var tasks = [], removeQueue = [];
13852     var id = 0;
13853     var running = false;
13854
13855     var stopThread = function(){
13856         running = false;
13857         clearInterval(id);
13858         id = 0;
13859     };
13860
13861     var startThread = function(){
13862         if(!running){
13863             running = true;
13864             id = setInterval(runTasks, interval);
13865         }
13866     };
13867
13868     var removeTask = function(task){
13869         removeQueue.push(task);
13870         if(task.onStop){
13871             task.onStop();
13872         }
13873     };
13874
13875     var runTasks = function(){
13876         if(removeQueue.length > 0){
13877             for(var i = 0, len = removeQueue.length; i < len; i++){
13878                 tasks.remove(removeQueue[i]);
13879             }
13880             removeQueue = [];
13881             if(tasks.length < 1){
13882                 stopThread();
13883                 return;
13884             }
13885         }
13886         var now = new Date().getTime();
13887         for(var i = 0, len = tasks.length; i < len; ++i){
13888             var t = tasks[i];
13889             var itime = now - t.taskRunTime;
13890             if(t.interval <= itime){
13891                 var rt = t.run.apply(t.scope || t, t.args || [++t.taskRunCount]);
13892                 t.taskRunTime = now;
13893                 if(rt === false || t.taskRunCount === t.repeat){
13894                     removeTask(t);
13895                     return;
13896                 }
13897             }
13898             if(t.duration && t.duration <= (now - t.taskStartTime)){
13899                 removeTask(t);
13900             }
13901         }
13902     };
13903
13904     /**
13905      * Queues a new task.
13906      * @param {Object} task
13907      *
13908      * Task property : interval = how frequent to run.
13909      * Task object should implement
13910      * function run()
13911      * Task object may implement
13912      * function onStop()
13913      */
13914     this.start = function(task){
13915         tasks.push(task);
13916         task.taskStartTime = new Date().getTime();
13917         task.taskRunTime = 0;
13918         task.taskRunCount = 0;
13919         startThread();
13920         return task;
13921     };
13922     /**
13923      * Stop  new task.
13924      * @param {Object} task
13925      */
13926     this.stop = function(task){
13927         removeTask(task);
13928         return task;
13929     };
13930     /**
13931      * Stop all Tasks
13932      */
13933     this.stopAll = function(){
13934         stopThread();
13935         for(var i = 0, len = tasks.length; i < len; i++){
13936             if(tasks[i].onStop){
13937                 tasks[i].onStop();
13938             }
13939         }
13940         tasks = [];
13941         removeQueue = [];
13942     };
13943 };
13944
13945 Roo.TaskMgr = new Roo.util.TaskRunner();/*
13946  * Based on:
13947  * Ext JS Library 1.1.1
13948  * Copyright(c) 2006-2007, Ext JS, LLC.
13949  *
13950  * Originally Released Under LGPL - original licence link has changed is not relivant.
13951  *
13952  * Fork - LGPL
13953  * <script type="text/javascript">
13954  */
13955
13956  
13957 /**
13958  * @class Roo.util.MixedCollection
13959  * @extends Roo.util.Observable
13960  * A Collection class that maintains both numeric indexes and keys and exposes events.
13961  * @constructor
13962  * @param {Boolean} allowFunctions True if the addAll function should add function references to the
13963  * collection (defaults to false)
13964  * @param {Function} keyFn A function that can accept an item of the type(s) stored in this MixedCollection
13965  * and return the key value for that item.  This is used when available to look up the key on items that
13966  * were passed without an explicit key parameter to a MixedCollection method.  Passing this parameter is
13967  * equivalent to providing an implementation for the {@link #getKey} method.
13968  */
13969 Roo.util.MixedCollection = function(allowFunctions, keyFn){
13970     this.items = [];
13971     this.map = {};
13972     this.keys = [];
13973     this.length = 0;
13974     this.addEvents({
13975         /**
13976          * @event clear
13977          * Fires when the collection is cleared.
13978          */
13979         "clear" : true,
13980         /**
13981          * @event add
13982          * Fires when an item is added to the collection.
13983          * @param {Number} index The index at which the item was added.
13984          * @param {Object} o The item added.
13985          * @param {String} key The key associated with the added item.
13986          */
13987         "add" : true,
13988         /**
13989          * @event replace
13990          * Fires when an item is replaced in the collection.
13991          * @param {String} key he key associated with the new added.
13992          * @param {Object} old The item being replaced.
13993          * @param {Object} new The new item.
13994          */
13995         "replace" : true,
13996         /**
13997          * @event remove
13998          * Fires when an item is removed from the collection.
13999          * @param {Object} o The item being removed.
14000          * @param {String} key (optional) The key associated with the removed item.
14001          */
14002         "remove" : true,
14003         "sort" : true
14004     });
14005     this.allowFunctions = allowFunctions === true;
14006     if(keyFn){
14007         this.getKey = keyFn;
14008     }
14009     Roo.util.MixedCollection.superclass.constructor.call(this);
14010 };
14011
14012 Roo.extend(Roo.util.MixedCollection, Roo.util.Observable, {
14013     allowFunctions : false,
14014     
14015 /**
14016  * Adds an item to the collection.
14017  * @param {String} key The key to associate with the item
14018  * @param {Object} o The item to add.
14019  * @return {Object} The item added.
14020  */
14021     add : function(key, o){
14022         if(arguments.length == 1){
14023             o = arguments[0];
14024             key = this.getKey(o);
14025         }
14026         if(typeof key == "undefined" || key === null){
14027             this.length++;
14028             this.items.push(o);
14029             this.keys.push(null);
14030         }else{
14031             var old = this.map[key];
14032             if(old){
14033                 return this.replace(key, o);
14034             }
14035             this.length++;
14036             this.items.push(o);
14037             this.map[key] = o;
14038             this.keys.push(key);
14039         }
14040         this.fireEvent("add", this.length-1, o, key);
14041         return o;
14042     },
14043        
14044 /**
14045   * MixedCollection has a generic way to fetch keys if you implement getKey.
14046 <pre><code>
14047 // normal way
14048 var mc = new Roo.util.MixedCollection();
14049 mc.add(someEl.dom.id, someEl);
14050 mc.add(otherEl.dom.id, otherEl);
14051 //and so on
14052
14053 // using getKey
14054 var mc = new Roo.util.MixedCollection();
14055 mc.getKey = function(el){
14056    return el.dom.id;
14057 };
14058 mc.add(someEl);
14059 mc.add(otherEl);
14060
14061 // or via the constructor
14062 var mc = new Roo.util.MixedCollection(false, function(el){
14063    return el.dom.id;
14064 });
14065 mc.add(someEl);
14066 mc.add(otherEl);
14067 </code></pre>
14068  * @param o {Object} The item for which to find the key.
14069  * @return {Object} The key for the passed item.
14070  */
14071     getKey : function(o){
14072          return o.id; 
14073     },
14074    
14075 /**
14076  * Replaces an item in the collection.
14077  * @param {String} key The key associated with the item to replace, or the item to replace.
14078  * @param o {Object} o (optional) If the first parameter passed was a key, the item to associate with that key.
14079  * @return {Object}  The new item.
14080  */
14081     replace : function(key, o){
14082         if(arguments.length == 1){
14083             o = arguments[0];
14084             key = this.getKey(o);
14085         }
14086         var old = this.item(key);
14087         if(typeof key == "undefined" || key === null || typeof old == "undefined"){
14088              return this.add(key, o);
14089         }
14090         var index = this.indexOfKey(key);
14091         this.items[index] = o;
14092         this.map[key] = o;
14093         this.fireEvent("replace", key, old, o);
14094         return o;
14095     },
14096    
14097 /**
14098  * Adds all elements of an Array or an Object to the collection.
14099  * @param {Object/Array} objs An Object containing properties which will be added to the collection, or
14100  * an Array of values, each of which are added to the collection.
14101  */
14102     addAll : function(objs){
14103         if(arguments.length > 1 || objs instanceof Array){
14104             var args = arguments.length > 1 ? arguments : objs;
14105             for(var i = 0, len = args.length; i < len; i++){
14106                 this.add(args[i]);
14107             }
14108         }else{
14109             for(var key in objs){
14110                 if(this.allowFunctions || typeof objs[key] != "function"){
14111                     this.add(key, objs[key]);
14112                 }
14113             }
14114         }
14115     },
14116    
14117 /**
14118  * Executes the specified function once for every item in the collection, passing each
14119  * item as the first and only parameter. returning false from the function will stop the iteration.
14120  * @param {Function} fn The function to execute for each item.
14121  * @param {Object} scope (optional) The scope in which to execute the function.
14122  */
14123     each : function(fn, scope){
14124         var items = [].concat(this.items); // each safe for removal
14125         for(var i = 0, len = items.length; i < len; i++){
14126             if(fn.call(scope || items[i], items[i], i, len) === false){
14127                 break;
14128             }
14129         }
14130     },
14131    
14132 /**
14133  * Executes the specified function once for every key in the collection, passing each
14134  * key, and its associated item as the first two parameters.
14135  * @param {Function} fn The function to execute for each item.
14136  * @param {Object} scope (optional) The scope in which to execute the function.
14137  */
14138     eachKey : function(fn, scope){
14139         for(var i = 0, len = this.keys.length; i < len; i++){
14140             fn.call(scope || window, this.keys[i], this.items[i], i, len);
14141         }
14142     },
14143    
14144 /**
14145  * Returns the first item in the collection which elicits a true return value from the
14146  * passed selection function.
14147  * @param {Function} fn The selection function to execute for each item.
14148  * @param {Object} scope (optional) The scope in which to execute the function.
14149  * @return {Object} The first item in the collection which returned true from the selection function.
14150  */
14151     find : function(fn, scope){
14152         for(var i = 0, len = this.items.length; i < len; i++){
14153             if(fn.call(scope || window, this.items[i], this.keys[i])){
14154                 return this.items[i];
14155             }
14156         }
14157         return null;
14158     },
14159    
14160 /**
14161  * Inserts an item at the specified index in the collection.
14162  * @param {Number} index The index to insert the item at.
14163  * @param {String} key The key to associate with the new item, or the item itself.
14164  * @param {Object} o  (optional) If the second parameter was a key, the new item.
14165  * @return {Object} The item inserted.
14166  */
14167     insert : function(index, key, o){
14168         if(arguments.length == 2){
14169             o = arguments[1];
14170             key = this.getKey(o);
14171         }
14172         if(index >= this.length){
14173             return this.add(key, o);
14174         }
14175         this.length++;
14176         this.items.splice(index, 0, o);
14177         if(typeof key != "undefined" && key != null){
14178             this.map[key] = o;
14179         }
14180         this.keys.splice(index, 0, key);
14181         this.fireEvent("add", index, o, key);
14182         return o;
14183     },
14184    
14185 /**
14186  * Removed an item from the collection.
14187  * @param {Object} o The item to remove.
14188  * @return {Object} The item removed.
14189  */
14190     remove : function(o){
14191         return this.removeAt(this.indexOf(o));
14192     },
14193    
14194 /**
14195  * Remove an item from a specified index in the collection.
14196  * @param {Number} index The index within the collection of the item to remove.
14197  */
14198     removeAt : function(index){
14199         if(index < this.length && index >= 0){
14200             this.length--;
14201             var o = this.items[index];
14202             this.items.splice(index, 1);
14203             var key = this.keys[index];
14204             if(typeof key != "undefined"){
14205                 delete this.map[key];
14206             }
14207             this.keys.splice(index, 1);
14208             this.fireEvent("remove", o, key);
14209         }
14210     },
14211    
14212 /**
14213  * Removed an item associated with the passed key fom the collection.
14214  * @param {String} key The key of the item to remove.
14215  */
14216     removeKey : function(key){
14217         return this.removeAt(this.indexOfKey(key));
14218     },
14219    
14220 /**
14221  * Returns the number of items in the collection.
14222  * @return {Number} the number of items in the collection.
14223  */
14224     getCount : function(){
14225         return this.length; 
14226     },
14227    
14228 /**
14229  * Returns index within the collection of the passed Object.
14230  * @param {Object} o The item to find the index of.
14231  * @return {Number} index of the item.
14232  */
14233     indexOf : function(o){
14234         if(!this.items.indexOf){
14235             for(var i = 0, len = this.items.length; i < len; i++){
14236                 if(this.items[i] == o) {
14237                     return i;
14238                 }
14239             }
14240             return -1;
14241         }else{
14242             return this.items.indexOf(o);
14243         }
14244     },
14245    
14246 /**
14247  * Returns index within the collection of the passed key.
14248  * @param {String} key The key to find the index of.
14249  * @return {Number} index of the key.
14250  */
14251     indexOfKey : function(key){
14252         if(!this.keys.indexOf){
14253             for(var i = 0, len = this.keys.length; i < len; i++){
14254                 if(this.keys[i] == key) {
14255                     return i;
14256                 }
14257             }
14258             return -1;
14259         }else{
14260             return this.keys.indexOf(key);
14261         }
14262     },
14263    
14264 /**
14265  * Returns the item associated with the passed key OR index. Key has priority over index.
14266  * @param {String/Number} key The key or index of the item.
14267  * @return {Object} The item associated with the passed key.
14268  */
14269     item : function(key){
14270         if (key === 'length') {
14271             return null;
14272         }
14273         var item = typeof this.map[key] != "undefined" ? this.map[key] : this.items[key];
14274         return typeof item != 'function' || this.allowFunctions ? item : null; // for prototype!
14275     },
14276     
14277 /**
14278  * Returns the item at the specified index.
14279  * @param {Number} index The index of the item.
14280  * @return {Object}
14281  */
14282     itemAt : function(index){
14283         return this.items[index];
14284     },
14285     
14286 /**
14287  * Returns the item associated with the passed key.
14288  * @param {String/Number} key The key of the item.
14289  * @return {Object} The item associated with the passed key.
14290  */
14291     key : function(key){
14292         return this.map[key];
14293     },
14294    
14295 /**
14296  * Returns true if the collection contains the passed Object as an item.
14297  * @param {Object} o  The Object to look for in the collection.
14298  * @return {Boolean} True if the collection contains the Object as an item.
14299  */
14300     contains : function(o){
14301         return this.indexOf(o) != -1;
14302     },
14303    
14304 /**
14305  * Returns true if the collection contains the passed Object as a key.
14306  * @param {String} key The key to look for in the collection.
14307  * @return {Boolean} True if the collection contains the Object as a key.
14308  */
14309     containsKey : function(key){
14310         return typeof this.map[key] != "undefined";
14311     },
14312    
14313 /**
14314  * Removes all items from the collection.
14315  */
14316     clear : function(){
14317         this.length = 0;
14318         this.items = [];
14319         this.keys = [];
14320         this.map = {};
14321         this.fireEvent("clear");
14322     },
14323    
14324 /**
14325  * Returns the first item in the collection.
14326  * @return {Object} the first item in the collection..
14327  */
14328     first : function(){
14329         return this.items[0]; 
14330     },
14331    
14332 /**
14333  * Returns the last item in the collection.
14334  * @return {Object} the last item in the collection..
14335  */
14336     last : function(){
14337         return this.items[this.length-1];   
14338     },
14339     
14340     _sort : function(property, dir, fn){
14341         var dsc = String(dir).toUpperCase() == "DESC" ? -1 : 1;
14342         fn = fn || function(a, b){
14343             return a-b;
14344         };
14345         var c = [], k = this.keys, items = this.items;
14346         for(var i = 0, len = items.length; i < len; i++){
14347             c[c.length] = {key: k[i], value: items[i], index: i};
14348         }
14349         c.sort(function(a, b){
14350             var v = fn(a[property], b[property]) * dsc;
14351             if(v == 0){
14352                 v = (a.index < b.index ? -1 : 1);
14353             }
14354             return v;
14355         });
14356         for(var i = 0, len = c.length; i < len; i++){
14357             items[i] = c[i].value;
14358             k[i] = c[i].key;
14359         }
14360         this.fireEvent("sort", this);
14361     },
14362     
14363     /**
14364      * Sorts this collection with the passed comparison function
14365      * @param {String} direction (optional) "ASC" or "DESC"
14366      * @param {Function} fn (optional) comparison function
14367      */
14368     sort : function(dir, fn){
14369         this._sort("value", dir, fn);
14370     },
14371     
14372     /**
14373      * Sorts this collection by keys
14374      * @param {String} direction (optional) "ASC" or "DESC"
14375      * @param {Function} fn (optional) a comparison function (defaults to case insensitive string)
14376      */
14377     keySort : function(dir, fn){
14378         this._sort("key", dir, fn || function(a, b){
14379             return String(a).toUpperCase()-String(b).toUpperCase();
14380         });
14381     },
14382     
14383     /**
14384      * Returns a range of items in this collection
14385      * @param {Number} startIndex (optional) defaults to 0
14386      * @param {Number} endIndex (optional) default to the last item
14387      * @return {Array} An array of items
14388      */
14389     getRange : function(start, end){
14390         var items = this.items;
14391         if(items.length < 1){
14392             return [];
14393         }
14394         start = start || 0;
14395         end = Math.min(typeof end == "undefined" ? this.length-1 : end, this.length-1);
14396         var r = [];
14397         if(start <= end){
14398             for(var i = start; i <= end; i++) {
14399                     r[r.length] = items[i];
14400             }
14401         }else{
14402             for(var i = start; i >= end; i--) {
14403                     r[r.length] = items[i];
14404             }
14405         }
14406         return r;
14407     },
14408         
14409     /**
14410      * Filter the <i>objects</i> in this collection by a specific property. 
14411      * Returns a new collection that has been filtered.
14412      * @param {String} property A property on your objects
14413      * @param {String/RegExp} value Either string that the property values 
14414      * should start with or a RegExp to test against the property
14415      * @return {MixedCollection} The new filtered collection
14416      */
14417     filter : function(property, value){
14418         if(!value.exec){ // not a regex
14419             value = String(value);
14420             if(value.length == 0){
14421                 return this.clone();
14422             }
14423             value = new RegExp("^" + Roo.escapeRe(value), "i");
14424         }
14425         return this.filterBy(function(o){
14426             return o && value.test(o[property]);
14427         });
14428         },
14429     
14430     /**
14431      * Filter by a function. * Returns a new collection that has been filtered.
14432      * The passed function will be called with each 
14433      * object in the collection. If the function returns true, the value is included 
14434      * otherwise it is filtered.
14435      * @param {Function} fn The function to be called, it will receive the args o (the object), k (the key)
14436      * @param {Object} scope (optional) The scope of the function (defaults to this) 
14437      * @return {MixedCollection} The new filtered collection
14438      */
14439     filterBy : function(fn, scope){
14440         var r = new Roo.util.MixedCollection();
14441         r.getKey = this.getKey;
14442         var k = this.keys, it = this.items;
14443         for(var i = 0, len = it.length; i < len; i++){
14444             if(fn.call(scope||this, it[i], k[i])){
14445                                 r.add(k[i], it[i]);
14446                         }
14447         }
14448         return r;
14449     },
14450     
14451     /**
14452      * Creates a duplicate of this collection
14453      * @return {MixedCollection}
14454      */
14455     clone : function(){
14456         var r = new Roo.util.MixedCollection();
14457         var k = this.keys, it = this.items;
14458         for(var i = 0, len = it.length; i < len; i++){
14459             r.add(k[i], it[i]);
14460         }
14461         r.getKey = this.getKey;
14462         return r;
14463     }
14464 });
14465 /**
14466  * Returns the item associated with the passed key or index.
14467  * @method
14468  * @param {String/Number} key The key or index of the item.
14469  * @return {Object} The item associated with the passed key.
14470  */
14471 Roo.util.MixedCollection.prototype.get = Roo.util.MixedCollection.prototype.item;/*
14472  * Based on:
14473  * Ext JS Library 1.1.1
14474  * Copyright(c) 2006-2007, Ext JS, LLC.
14475  *
14476  * Originally Released Under LGPL - original licence link has changed is not relivant.
14477  *
14478  * Fork - LGPL
14479  * <script type="text/javascript">
14480  */
14481 /**
14482  * @class Roo.util.JSON
14483  * Modified version of Douglas Crockford"s json.js that doesn"t
14484  * mess with the Object prototype 
14485  * http://www.json.org/js.html
14486  * @static
14487  */
14488 Roo.util.JSON = new (function(){
14489     var useHasOwn = {}.hasOwnProperty ? true : false;
14490     
14491     // crashes Safari in some instances
14492     //var validRE = /^("(\\.|[^"\\\n\r])*?"|[,:{}\[\]0-9.\-+Eaeflnr-u \n\r\t])+?$/;
14493     
14494     var pad = function(n) {
14495         return n < 10 ? "0" + n : n;
14496     };
14497     
14498     var m = {
14499         "\b": '\\b',
14500         "\t": '\\t',
14501         "\n": '\\n',
14502         "\f": '\\f',
14503         "\r": '\\r',
14504         '"' : '\\"',
14505         "\\": '\\\\'
14506     };
14507
14508     var encodeString = function(s){
14509         if (/["\\\x00-\x1f]/.test(s)) {
14510             return '"' + s.replace(/([\x00-\x1f\\"])/g, function(a, b) {
14511                 var c = m[b];
14512                 if(c){
14513                     return c;
14514                 }
14515                 c = b.charCodeAt();
14516                 return "\\u00" +
14517                     Math.floor(c / 16).toString(16) +
14518                     (c % 16).toString(16);
14519             }) + '"';
14520         }
14521         return '"' + s + '"';
14522     };
14523     
14524     var encodeArray = function(o){
14525         var a = ["["], b, i, l = o.length, v;
14526             for (i = 0; i < l; i += 1) {
14527                 v = o[i];
14528                 switch (typeof v) {
14529                     case "undefined":
14530                     case "function":
14531                     case "unknown":
14532                         break;
14533                     default:
14534                         if (b) {
14535                             a.push(',');
14536                         }
14537                         a.push(v === null ? "null" : Roo.util.JSON.encode(v));
14538                         b = true;
14539                 }
14540             }
14541             a.push("]");
14542             return a.join("");
14543     };
14544     
14545     var encodeDate = function(o){
14546         return '"' + o.getFullYear() + "-" +
14547                 pad(o.getMonth() + 1) + "-" +
14548                 pad(o.getDate()) + "T" +
14549                 pad(o.getHours()) + ":" +
14550                 pad(o.getMinutes()) + ":" +
14551                 pad(o.getSeconds()) + '"';
14552     };
14553     
14554     /**
14555      * Encodes an Object, Array or other value
14556      * @param {Mixed} o The variable to encode
14557      * @return {String} The JSON string
14558      */
14559     this.encode = function(o)
14560     {
14561         // should this be extended to fully wrap stringify..
14562         
14563         if(typeof o == "undefined" || o === null){
14564             return "null";
14565         }else if(o instanceof Array){
14566             return encodeArray(o);
14567         }else if(o instanceof Date){
14568             return encodeDate(o);
14569         }else if(typeof o == "string"){
14570             return encodeString(o);
14571         }else if(typeof o == "number"){
14572             return isFinite(o) ? String(o) : "null";
14573         }else if(typeof o == "boolean"){
14574             return String(o);
14575         }else {
14576             var a = ["{"], b, i, v;
14577             for (i in o) {
14578                 if(!useHasOwn || o.hasOwnProperty(i)) {
14579                     v = o[i];
14580                     switch (typeof v) {
14581                     case "undefined":
14582                     case "function":
14583                     case "unknown":
14584                         break;
14585                     default:
14586                         if(b){
14587                             a.push(',');
14588                         }
14589                         a.push(this.encode(i), ":",
14590                                 v === null ? "null" : this.encode(v));
14591                         b = true;
14592                     }
14593                 }
14594             }
14595             a.push("}");
14596             return a.join("");
14597         }
14598     };
14599     
14600     /**
14601      * Decodes (parses) a JSON string to an object. If the JSON is invalid, this function throws a SyntaxError.
14602      * @param {String} json The JSON string
14603      * @return {Object} The resulting object
14604      */
14605     this.decode = function(json){
14606         
14607         return  /** eval:var:json */ eval("(" + json + ')');
14608     };
14609 })();
14610 /** 
14611  * Shorthand for {@link Roo.util.JSON#encode}
14612  * @member Roo encode 
14613  * @method */
14614 Roo.encode = typeof(JSON) != 'undefined' && JSON.stringify ? JSON.stringify : Roo.util.JSON.encode;
14615 /** 
14616  * Shorthand for {@link Roo.util.JSON#decode}
14617  * @member Roo decode 
14618  * @method */
14619 Roo.decode = typeof(JSON) != 'undefined' && JSON.parse ? JSON.parse : Roo.util.JSON.decode;
14620 /*
14621  * Based on:
14622  * Ext JS Library 1.1.1
14623  * Copyright(c) 2006-2007, Ext JS, LLC.
14624  *
14625  * Originally Released Under LGPL - original licence link has changed is not relivant.
14626  *
14627  * Fork - LGPL
14628  * <script type="text/javascript">
14629  */
14630  
14631 /**
14632  * @class Roo.util.Format
14633  * Reusable data formatting functions
14634  * @static
14635  */
14636 Roo.util.Format = function(){
14637     var trimRe = /^\s+|\s+$/g;
14638     return {
14639         /**
14640          * Truncate a string and add an ellipsis ('...') to the end if it exceeds the specified length
14641          * @param {String} value The string to truncate
14642          * @param {Number} length The maximum length to allow before truncating
14643          * @return {String} The converted text
14644          */
14645         ellipsis : function(value, len){
14646             if(value && value.length > len){
14647                 return value.substr(0, len-3)+"...";
14648             }
14649             return value;
14650         },
14651
14652         /**
14653          * Checks a reference and converts it to empty string if it is undefined
14654          * @param {Mixed} value Reference to check
14655          * @return {Mixed} Empty string if converted, otherwise the original value
14656          */
14657         undef : function(value){
14658             return typeof value != "undefined" ? value : "";
14659         },
14660
14661         /**
14662          * Convert certain characters (&, <, >, and ') to their HTML character equivalents for literal display in web pages.
14663          * @param {String} value The string to encode
14664          * @return {String} The encoded text
14665          */
14666         htmlEncode : function(value){
14667             return !value ? value : String(value).replace(/&/g, "&amp;").replace(/>/g, "&gt;").replace(/</g, "&lt;").replace(/"/g, "&quot;");
14668         },
14669
14670         /**
14671          * Convert certain characters (&, <, >, and ') from their HTML character equivalents.
14672          * @param {String} value The string to decode
14673          * @return {String} The decoded text
14674          */
14675         htmlDecode : function(value){
14676             return !value ? value : String(value).replace(/&amp;/g, "&").replace(/&gt;/g, ">").replace(/&lt;/g, "<").replace(/&quot;/g, '"');
14677         },
14678
14679         /**
14680          * Trims any whitespace from either side of a string
14681          * @param {String} value The text to trim
14682          * @return {String} The trimmed text
14683          */
14684         trim : function(value){
14685             return String(value).replace(trimRe, "");
14686         },
14687
14688         /**
14689          * Returns a substring from within an original string
14690          * @param {String} value The original text
14691          * @param {Number} start The start index of the substring
14692          * @param {Number} length The length of the substring
14693          * @return {String} The substring
14694          */
14695         substr : function(value, start, length){
14696             return String(value).substr(start, length);
14697         },
14698
14699         /**
14700          * Converts a string to all lower case letters
14701          * @param {String} value The text to convert
14702          * @return {String} The converted text
14703          */
14704         lowercase : function(value){
14705             return String(value).toLowerCase();
14706         },
14707
14708         /**
14709          * Converts a string to all upper case letters
14710          * @param {String} value The text to convert
14711          * @return {String} The converted text
14712          */
14713         uppercase : function(value){
14714             return String(value).toUpperCase();
14715         },
14716
14717         /**
14718          * Converts the first character only of a string to upper case
14719          * @param {String} value The text to convert
14720          * @return {String} The converted text
14721          */
14722         capitalize : function(value){
14723             return !value ? value : value.charAt(0).toUpperCase() + value.substr(1).toLowerCase();
14724         },
14725
14726         // private
14727         call : function(value, fn){
14728             if(arguments.length > 2){
14729                 var args = Array.prototype.slice.call(arguments, 2);
14730                 args.unshift(value);
14731                  
14732                 return /** eval:var:value */  eval(fn).apply(window, args);
14733             }else{
14734                 /** eval:var:value */
14735                 return /** eval:var:value */ eval(fn).call(window, value);
14736             }
14737         },
14738
14739        
14740         /**
14741          * safer version of Math.toFixed..??/
14742          * @param {Number/String} value The numeric value to format
14743          * @param {Number/String} value Decimal places 
14744          * @return {String} The formatted currency string
14745          */
14746         toFixed : function(v, n)
14747         {
14748             // why not use to fixed - precision is buggered???
14749             if (!n) {
14750                 return Math.round(v-0);
14751             }
14752             var fact = Math.pow(10,n+1);
14753             v = (Math.round((v-0)*fact))/fact;
14754             var z = (''+fact).substring(2);
14755             if (v == Math.floor(v)) {
14756                 return Math.floor(v) + '.' + z;
14757             }
14758             
14759             // now just padd decimals..
14760             var ps = String(v).split('.');
14761             var fd = (ps[1] + z);
14762             var r = fd.substring(0,n); 
14763             var rm = fd.substring(n); 
14764             if (rm < 5) {
14765                 return ps[0] + '.' + r;
14766             }
14767             r*=1; // turn it into a number;
14768             r++;
14769             if (String(r).length != n) {
14770                 ps[0]*=1;
14771                 ps[0]++;
14772                 r = String(r).substring(1); // chop the end off.
14773             }
14774             
14775             return ps[0] + '.' + r;
14776              
14777         },
14778         
14779         /**
14780          * Format a number as US currency
14781          * @param {Number/String} value The numeric value to format
14782          * @return {String} The formatted currency string
14783          */
14784         usMoney : function(v){
14785             return '$' + Roo.util.Format.number(v);
14786         },
14787         
14788         /**
14789          * Format a number
14790          * eventually this should probably emulate php's number_format
14791          * @param {Number/String} value The numeric value to format
14792          * @param {Number} decimals number of decimal places
14793          * @param {String} delimiter for thousands (default comma)
14794          * @return {String} The formatted currency string
14795          */
14796         number : function(v, decimals, thousandsDelimiter)
14797         {
14798             // multiply and round.
14799             decimals = typeof(decimals) == 'undefined' ? 2 : decimals;
14800             thousandsDelimiter = typeof(thousandsDelimiter) == 'undefined' ? ',' : thousandsDelimiter;
14801             
14802             var mul = Math.pow(10, decimals);
14803             var zero = String(mul).substring(1);
14804             v = (Math.round((v-0)*mul))/mul;
14805             
14806             // if it's '0' number.. then
14807             
14808             //v = (v == Math.floor(v)) ? v + "." + zero : ((v*10 == Math.floor(v*10)) ? v + "0" : v);
14809             v = String(v);
14810             var ps = v.split('.');
14811             var whole = ps[0];
14812             
14813             var r = /(\d+)(\d{3})/;
14814             // add comma's
14815             
14816             if(thousandsDelimiter.length != 0) {
14817                 whole = whole.replace(/\B(?=(\d{3})+(?!\d))/g, thousandsDelimiter );
14818             } 
14819             
14820             var sub = ps[1] ?
14821                     // has decimals..
14822                     (decimals ?  ('.'+ ps[1] + zero.substring(ps[1].length)) : '') :
14823                     // does not have decimals
14824                     (decimals ? ('.' + zero) : '');
14825             
14826             
14827             return whole + sub ;
14828         },
14829         
14830         /**
14831          * Parse a value into a formatted date using the specified format pattern.
14832          * @param {Mixed} value The value to format
14833          * @param {String} format (optional) Any valid date format string (defaults to 'm/d/Y')
14834          * @return {String} The formatted date string
14835          */
14836         date : function(v, format){
14837             if(!v){
14838                 return "";
14839             }
14840             if(!(v instanceof Date)){
14841                 v = new Date(Date.parse(v));
14842             }
14843             return v.dateFormat(format || Roo.util.Format.defaults.date);
14844         },
14845
14846         /**
14847          * Returns a date rendering function that can be reused to apply a date format multiple times efficiently
14848          * @param {String} format Any valid date format string
14849          * @return {Function} The date formatting function
14850          */
14851         dateRenderer : function(format){
14852             return function(v){
14853                 return Roo.util.Format.date(v, format);  
14854             };
14855         },
14856
14857         // private
14858         stripTagsRE : /<\/?[^>]+>/gi,
14859         
14860         /**
14861          * Strips all HTML tags
14862          * @param {Mixed} value The text from which to strip tags
14863          * @return {String} The stripped text
14864          */
14865         stripTags : function(v){
14866             return !v ? v : String(v).replace(this.stripTagsRE, "");
14867         },
14868         
14869         /**
14870          * Size in Mb,Gb etc.
14871          * @param {Number} value The number to be formated
14872          * @param {number} decimals how many decimal places
14873          * @return {String} the formated string
14874          */
14875         size : function(value, decimals)
14876         {
14877             var sizes = ['b', 'k', 'M', 'G', 'T'];
14878             if (value == 0) {
14879                 return 0;
14880             }
14881             var i = parseInt(Math.floor(Math.log(value) / Math.log(1024)));
14882             return Roo.util.Format.number(value/ Math.pow(1024, i) ,decimals)   + sizes[i];
14883         }
14884         
14885         
14886         
14887     };
14888 }();
14889 Roo.util.Format.defaults = {
14890     date : 'd/M/Y'
14891 };/*
14892  * Based on:
14893  * Ext JS Library 1.1.1
14894  * Copyright(c) 2006-2007, Ext JS, LLC.
14895  *
14896  * Originally Released Under LGPL - original licence link has changed is not relivant.
14897  *
14898  * Fork - LGPL
14899  * <script type="text/javascript">
14900  */
14901
14902
14903  
14904
14905 /**
14906  * @class Roo.MasterTemplate
14907  * @extends Roo.Template
14908  * Provides a template that can have child templates. The syntax is:
14909 <pre><code>
14910 var t = new Roo.MasterTemplate(
14911         '&lt;select name="{name}"&gt;',
14912                 '&lt;tpl name="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
14913         '&lt;/select&gt;'
14914 );
14915 t.add('options', {value: 'foo', text: 'bar'});
14916 // or you can add multiple child elements in one shot
14917 t.addAll('options', [
14918     {value: 'foo', text: 'bar'},
14919     {value: 'foo2', text: 'bar2'},
14920     {value: 'foo3', text: 'bar3'}
14921 ]);
14922 // then append, applying the master template values
14923 t.append('my-form', {name: 'my-select'});
14924 </code></pre>
14925 * A name attribute for the child template is not required if you have only one child
14926 * template or you want to refer to them by index.
14927  */
14928 Roo.MasterTemplate = function(){
14929     Roo.MasterTemplate.superclass.constructor.apply(this, arguments);
14930     this.originalHtml = this.html;
14931     var st = {};
14932     var m, re = this.subTemplateRe;
14933     re.lastIndex = 0;
14934     var subIndex = 0;
14935     while(m = re.exec(this.html)){
14936         var name = m[1], content = m[2];
14937         st[subIndex] = {
14938             name: name,
14939             index: subIndex,
14940             buffer: [],
14941             tpl : new Roo.Template(content)
14942         };
14943         if(name){
14944             st[name] = st[subIndex];
14945         }
14946         st[subIndex].tpl.compile();
14947         st[subIndex].tpl.call = this.call.createDelegate(this);
14948         subIndex++;
14949     }
14950     this.subCount = subIndex;
14951     this.subs = st;
14952 };
14953 Roo.extend(Roo.MasterTemplate, Roo.Template, {
14954     /**
14955     * The regular expression used to match sub templates
14956     * @type RegExp
14957     * @property
14958     */
14959     subTemplateRe : /<tpl(?:\sname="([\w-]+)")?>((?:.|\n)*?)<\/tpl>/gi,
14960
14961     /**
14962      * Applies the passed values to a child template.
14963      * @param {String/Number} name (optional) The name or index of the child template
14964      * @param {Array/Object} values The values to be applied to the template
14965      * @return {MasterTemplate} this
14966      */
14967      add : function(name, values){
14968         if(arguments.length == 1){
14969             values = arguments[0];
14970             name = 0;
14971         }
14972         var s = this.subs[name];
14973         s.buffer[s.buffer.length] = s.tpl.apply(values);
14974         return this;
14975     },
14976
14977     /**
14978      * Applies all the passed values to a child template.
14979      * @param {String/Number} name (optional) The name or index of the child template
14980      * @param {Array} values The values to be applied to the template, this should be an array of objects.
14981      * @param {Boolean} reset (optional) True to reset the template first
14982      * @return {MasterTemplate} this
14983      */
14984     fill : function(name, values, reset){
14985         var a = arguments;
14986         if(a.length == 1 || (a.length == 2 && typeof a[1] == "boolean")){
14987             values = a[0];
14988             name = 0;
14989             reset = a[1];
14990         }
14991         if(reset){
14992             this.reset();
14993         }
14994         for(var i = 0, len = values.length; i < len; i++){
14995             this.add(name, values[i]);
14996         }
14997         return this;
14998     },
14999
15000     /**
15001      * Resets the template for reuse
15002      * @return {MasterTemplate} this
15003      */
15004      reset : function(){
15005         var s = this.subs;
15006         for(var i = 0; i < this.subCount; i++){
15007             s[i].buffer = [];
15008         }
15009         return this;
15010     },
15011
15012     applyTemplate : function(values){
15013         var s = this.subs;
15014         var replaceIndex = -1;
15015         this.html = this.originalHtml.replace(this.subTemplateRe, function(m, name){
15016             return s[++replaceIndex].buffer.join("");
15017         });
15018         return Roo.MasterTemplate.superclass.applyTemplate.call(this, values);
15019     },
15020
15021     apply : function(){
15022         return this.applyTemplate.apply(this, arguments);
15023     },
15024
15025     compile : function(){return this;}
15026 });
15027
15028 /**
15029  * Alias for fill().
15030  * @method
15031  */
15032 Roo.MasterTemplate.prototype.addAll = Roo.MasterTemplate.prototype.fill;
15033  /**
15034  * Creates a template from the passed element's value (display:none textarea, preferred) or innerHTML. e.g.
15035  * var tpl = Roo.MasterTemplate.from('element-id');
15036  * @param {String/HTMLElement} el
15037  * @param {Object} config
15038  * @static
15039  */
15040 Roo.MasterTemplate.from = function(el, config){
15041     el = Roo.getDom(el);
15042     return new Roo.MasterTemplate(el.value || el.innerHTML, config || '');
15043 };/*
15044  * Based on:
15045  * Ext JS Library 1.1.1
15046  * Copyright(c) 2006-2007, Ext JS, LLC.
15047  *
15048  * Originally Released Under LGPL - original licence link has changed is not relivant.
15049  *
15050  * Fork - LGPL
15051  * <script type="text/javascript">
15052  */
15053
15054  
15055 /**
15056  * @class Roo.util.CSS
15057  * Utility class for manipulating CSS rules
15058  * @static
15059
15060  */
15061 Roo.util.CSS = function(){
15062         var rules = null;
15063         var doc = document;
15064
15065     var camelRe = /(-[a-z])/gi;
15066     var camelFn = function(m, a){ return a.charAt(1).toUpperCase(); };
15067
15068    return {
15069    /**
15070     * Very simple dynamic creation of stylesheets from a text blob of rules.  The text will wrapped in a style
15071     * tag and appended to the HEAD of the document.
15072     * @param {String|Object} cssText The text containing the css rules
15073     * @param {String} id An id to add to the stylesheet for later removal
15074     * @return {StyleSheet}
15075     */
15076     createStyleSheet : function(cssText, id){
15077         var ss;
15078         var head = doc.getElementsByTagName("head")[0];
15079         var nrules = doc.createElement("style");
15080         nrules.setAttribute("type", "text/css");
15081         if(id){
15082             nrules.setAttribute("id", id);
15083         }
15084         if (typeof(cssText) != 'string') {
15085             // support object maps..
15086             // not sure if this a good idea.. 
15087             // perhaps it should be merged with the general css handling
15088             // and handle js style props.
15089             var cssTextNew = [];
15090             for(var n in cssText) {
15091                 var citems = [];
15092                 for(var k in cssText[n]) {
15093                     citems.push( k + ' : ' +cssText[n][k] + ';' );
15094                 }
15095                 cssTextNew.push( n + ' { ' + citems.join(' ') + '} ');
15096                 
15097             }
15098             cssText = cssTextNew.join("\n");
15099             
15100         }
15101        
15102        
15103        if(Roo.isIE){
15104            head.appendChild(nrules);
15105            ss = nrules.styleSheet;
15106            ss.cssText = cssText;
15107        }else{
15108            try{
15109                 nrules.appendChild(doc.createTextNode(cssText));
15110            }catch(e){
15111                nrules.cssText = cssText; 
15112            }
15113            head.appendChild(nrules);
15114            ss = nrules.styleSheet ? nrules.styleSheet : (nrules.sheet || doc.styleSheets[doc.styleSheets.length-1]);
15115        }
15116        this.cacheStyleSheet(ss);
15117        return ss;
15118    },
15119
15120    /**
15121     * Removes a style or link tag by id
15122     * @param {String} id The id of the tag
15123     */
15124    removeStyleSheet : function(id){
15125        var existing = doc.getElementById(id);
15126        if(existing){
15127            existing.parentNode.removeChild(existing);
15128        }
15129    },
15130
15131    /**
15132     * Dynamically swaps an existing stylesheet reference for a new one
15133     * @param {String} id The id of an existing link tag to remove
15134     * @param {String} url The href of the new stylesheet to include
15135     */
15136    swapStyleSheet : function(id, url){
15137        this.removeStyleSheet(id);
15138        var ss = doc.createElement("link");
15139        ss.setAttribute("rel", "stylesheet");
15140        ss.setAttribute("type", "text/css");
15141        ss.setAttribute("id", id);
15142        ss.setAttribute("href", url);
15143        doc.getElementsByTagName("head")[0].appendChild(ss);
15144    },
15145    
15146    /**
15147     * Refresh the rule cache if you have dynamically added stylesheets
15148     * @return {Object} An object (hash) of rules indexed by selector
15149     */
15150    refreshCache : function(){
15151        return this.getRules(true);
15152    },
15153
15154    // private
15155    cacheStyleSheet : function(stylesheet){
15156        if(!rules){
15157            rules = {};
15158        }
15159        try{// try catch for cross domain access issue
15160            var ssRules = stylesheet.cssRules || stylesheet.rules;
15161            for(var j = ssRules.length-1; j >= 0; --j){
15162                rules[ssRules[j].selectorText] = ssRules[j];
15163            }
15164        }catch(e){}
15165    },
15166    
15167    /**
15168     * Gets all css rules for the document
15169     * @param {Boolean} refreshCache true to refresh the internal cache
15170     * @return {Object} An object (hash) of rules indexed by selector
15171     */
15172    getRules : function(refreshCache){
15173                 if(rules == null || refreshCache){
15174                         rules = {};
15175                         var ds = doc.styleSheets;
15176                         for(var i =0, len = ds.length; i < len; i++){
15177                             try{
15178                         this.cacheStyleSheet(ds[i]);
15179                     }catch(e){} 
15180                 }
15181                 }
15182                 return rules;
15183         },
15184         
15185         /**
15186     * Gets an an individual CSS rule by selector(s)
15187     * @param {String/Array} selector The CSS selector or an array of selectors to try. The first selector that is found is returned.
15188     * @param {Boolean} refreshCache true to refresh the internal cache if you have recently updated any rules or added styles dynamically
15189     * @return {CSSRule} The CSS rule or null if one is not found
15190     */
15191    getRule : function(selector, refreshCache){
15192                 var rs = this.getRules(refreshCache);
15193                 if(!(selector instanceof Array)){
15194                     return rs[selector];
15195                 }
15196                 for(var i = 0; i < selector.length; i++){
15197                         if(rs[selector[i]]){
15198                                 return rs[selector[i]];
15199                         }
15200                 }
15201                 return null;
15202         },
15203         
15204         
15205         /**
15206     * Updates a rule property
15207     * @param {String/Array} selector If it's an array it tries each selector until it finds one. Stops immediately once one is found.
15208     * @param {String} property The css property
15209     * @param {String} value The new value for the property
15210     * @return {Boolean} true If a rule was found and updated
15211     */
15212    updateRule : function(selector, property, value){
15213                 if(!(selector instanceof Array)){
15214                         var rule = this.getRule(selector);
15215                         if(rule){
15216                                 rule.style[property.replace(camelRe, camelFn)] = value;
15217                                 return true;
15218                         }
15219                 }else{
15220                         for(var i = 0; i < selector.length; i++){
15221                                 if(this.updateRule(selector[i], property, value)){
15222                                         return true;
15223                                 }
15224                         }
15225                 }
15226                 return false;
15227         }
15228    };   
15229 }();/*
15230  * Based on:
15231  * Ext JS Library 1.1.1
15232  * Copyright(c) 2006-2007, Ext JS, LLC.
15233  *
15234  * Originally Released Under LGPL - original licence link has changed is not relivant.
15235  *
15236  * Fork - LGPL
15237  * <script type="text/javascript">
15238  */
15239
15240  
15241
15242 /**
15243  * @class Roo.util.ClickRepeater
15244  * @extends Roo.util.Observable
15245  * 
15246  * A wrapper class which can be applied to any element. Fires a "click" event while the
15247  * mouse is pressed. The interval between firings may be specified in the config but
15248  * defaults to 10 milliseconds.
15249  * 
15250  * Optionally, a CSS class may be applied to the element during the time it is pressed.
15251  * 
15252  * @cfg {String/HTMLElement/Element} el The element to act as a button.
15253  * @cfg {Number} delay The initial delay before the repeating event begins firing.
15254  * Similar to an autorepeat key delay.
15255  * @cfg {Number} interval The interval between firings of the "click" event. Default 10 ms.
15256  * @cfg {String} pressClass A CSS class name to be applied to the element while pressed.
15257  * @cfg {Boolean} accelerate True if autorepeating should start slowly and accelerate.
15258  *           "interval" and "delay" are ignored. "immediate" is honored.
15259  * @cfg {Boolean} preventDefault True to prevent the default click event
15260  * @cfg {Boolean} stopDefault True to stop the default click event
15261  * 
15262  * @history
15263  *     2007-02-02 jvs Original code contributed by Nige "Animal" White
15264  *     2007-02-02 jvs Renamed to ClickRepeater
15265  *   2007-02-03 jvs Modifications for FF Mac and Safari 
15266  *
15267  *  @constructor
15268  * @param {String/HTMLElement/Element} el The element to listen on
15269  * @param {Object} config
15270  **/
15271 Roo.util.ClickRepeater = function(el, config)
15272 {
15273     this.el = Roo.get(el);
15274     this.el.unselectable();
15275
15276     Roo.apply(this, config);
15277
15278     this.addEvents({
15279     /**
15280      * @event mousedown
15281      * Fires when the mouse button is depressed.
15282      * @param {Roo.util.ClickRepeater} this
15283      */
15284         "mousedown" : true,
15285     /**
15286      * @event click
15287      * Fires on a specified interval during the time the element is pressed.
15288      * @param {Roo.util.ClickRepeater} this
15289      */
15290         "click" : true,
15291     /**
15292      * @event mouseup
15293      * Fires when the mouse key is released.
15294      * @param {Roo.util.ClickRepeater} this
15295      */
15296         "mouseup" : true
15297     });
15298
15299     this.el.on("mousedown", this.handleMouseDown, this);
15300     if(this.preventDefault || this.stopDefault){
15301         this.el.on("click", function(e){
15302             if(this.preventDefault){
15303                 e.preventDefault();
15304             }
15305             if(this.stopDefault){
15306                 e.stopEvent();
15307             }
15308         }, this);
15309     }
15310
15311     // allow inline handler
15312     if(this.handler){
15313         this.on("click", this.handler,  this.scope || this);
15314     }
15315
15316     Roo.util.ClickRepeater.superclass.constructor.call(this);
15317 };
15318
15319 Roo.extend(Roo.util.ClickRepeater, Roo.util.Observable, {
15320     interval : 20,
15321     delay: 250,
15322     preventDefault : true,
15323     stopDefault : false,
15324     timer : 0,
15325
15326     // private
15327     handleMouseDown : function(){
15328         clearTimeout(this.timer);
15329         this.el.blur();
15330         if(this.pressClass){
15331             this.el.addClass(this.pressClass);
15332         }
15333         this.mousedownTime = new Date();
15334
15335         Roo.get(document).on("mouseup", this.handleMouseUp, this);
15336         this.el.on("mouseout", this.handleMouseOut, this);
15337
15338         this.fireEvent("mousedown", this);
15339         this.fireEvent("click", this);
15340         
15341         this.timer = this.click.defer(this.delay || this.interval, this);
15342     },
15343
15344     // private
15345     click : function(){
15346         this.fireEvent("click", this);
15347         this.timer = this.click.defer(this.getInterval(), this);
15348     },
15349
15350     // private
15351     getInterval: function(){
15352         if(!this.accelerate){
15353             return this.interval;
15354         }
15355         var pressTime = this.mousedownTime.getElapsed();
15356         if(pressTime < 500){
15357             return 400;
15358         }else if(pressTime < 1700){
15359             return 320;
15360         }else if(pressTime < 2600){
15361             return 250;
15362         }else if(pressTime < 3500){
15363             return 180;
15364         }else if(pressTime < 4400){
15365             return 140;
15366         }else if(pressTime < 5300){
15367             return 80;
15368         }else if(pressTime < 6200){
15369             return 50;
15370         }else{
15371             return 10;
15372         }
15373     },
15374
15375     // private
15376     handleMouseOut : function(){
15377         clearTimeout(this.timer);
15378         if(this.pressClass){
15379             this.el.removeClass(this.pressClass);
15380         }
15381         this.el.on("mouseover", this.handleMouseReturn, this);
15382     },
15383
15384     // private
15385     handleMouseReturn : function(){
15386         this.el.un("mouseover", this.handleMouseReturn);
15387         if(this.pressClass){
15388             this.el.addClass(this.pressClass);
15389         }
15390         this.click();
15391     },
15392
15393     // private
15394     handleMouseUp : function(){
15395         clearTimeout(this.timer);
15396         this.el.un("mouseover", this.handleMouseReturn);
15397         this.el.un("mouseout", this.handleMouseOut);
15398         Roo.get(document).un("mouseup", this.handleMouseUp);
15399         this.el.removeClass(this.pressClass);
15400         this.fireEvent("mouseup", this);
15401     }
15402 });/**
15403  * @class Roo.util.Clipboard
15404  * @static
15405  * 
15406  * Clipboard UTILS
15407  * 
15408  **/
15409 Roo.util.Clipboard = {
15410     /**
15411      * Writes a string to the clipboard - using the Clipboard API if https, otherwise using text area.
15412      * @param {String} text to copy to clipboard
15413      */
15414     write : function(text) {
15415         // navigator clipboard api needs a secure context (https)
15416         if (navigator.clipboard && window.isSecureContext) {
15417             // navigator clipboard api method'
15418             navigator.clipboard.writeText(text);
15419             return ;
15420         } 
15421         // text area method
15422         var ta = document.createElement("textarea");
15423         ta.value = text;
15424         // make the textarea out of viewport
15425         ta.style.position = "fixed";
15426         ta.style.left = "-999999px";
15427         ta.style.top = "-999999px";
15428         document.body.appendChild(ta);
15429         ta.focus();
15430         ta.select();
15431         document.execCommand('copy');
15432         (function() {
15433             ta.remove();
15434         }).defer(100);
15435         
15436     }
15437         
15438 }
15439     /*
15440  * Based on:
15441  * Ext JS Library 1.1.1
15442  * Copyright(c) 2006-2007, Ext JS, LLC.
15443  *
15444  * Originally Released Under LGPL - original licence link has changed is not relivant.
15445  *
15446  * Fork - LGPL
15447  * <script type="text/javascript">
15448  */
15449
15450  
15451 /**
15452  * @class Roo.KeyNav
15453  * <p>Provides a convenient wrapper for normalized keyboard navigation.  KeyNav allows you to bind
15454  * navigation keys to function calls that will get called when the keys are pressed, providing an easy
15455  * way to implement custom navigation schemes for any UI component.</p>
15456  * <p>The following are all of the possible keys that can be implemented: enter, left, right, up, down, tab, esc,
15457  * pageUp, pageDown, del, home, end.  Usage:</p>
15458  <pre><code>
15459 var nav = new Roo.KeyNav("my-element", {
15460     "left" : function(e){
15461         this.moveLeft(e.ctrlKey);
15462     },
15463     "right" : function(e){
15464         this.moveRight(e.ctrlKey);
15465     },
15466     "enter" : function(e){
15467         this.save();
15468     },
15469     scope : this
15470 });
15471 </code></pre>
15472  * @constructor
15473  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15474  * @param {Object} config The config
15475  */
15476 Roo.KeyNav = function(el, config){
15477     this.el = Roo.get(el);
15478     Roo.apply(this, config);
15479     if(!this.disabled){
15480         this.disabled = true;
15481         this.enable();
15482     }
15483 };
15484
15485 Roo.KeyNav.prototype = {
15486     /**
15487      * @cfg {Boolean} disabled
15488      * True to disable this KeyNav instance (defaults to false)
15489      */
15490     disabled : false,
15491     /**
15492      * @cfg {String} defaultEventAction
15493      * The method to call on the {@link Roo.EventObject} after this KeyNav intercepts a key.  Valid values are
15494      * {@link Roo.EventObject#stopEvent}, {@link Roo.EventObject#preventDefault} and
15495      * {@link Roo.EventObject#stopPropagation} (defaults to 'stopEvent')
15496      */
15497     defaultEventAction: "stopEvent",
15498     /**
15499      * @cfg {Boolean} forceKeyDown
15500      * Handle the keydown event instead of keypress (defaults to false).  KeyNav automatically does this for IE since
15501      * IE does not propagate special keys on keypress, but setting this to true will force other browsers to also
15502      * handle keydown instead of keypress.
15503      */
15504     forceKeyDown : false,
15505
15506     // private
15507     prepareEvent : function(e){
15508         var k = e.getKey();
15509         var h = this.keyToHandler[k];
15510         //if(h && this[h]){
15511         //    e.stopPropagation();
15512         //}
15513         if(Roo.isSafari && h && k >= 37 && k <= 40){
15514             e.stopEvent();
15515         }
15516     },
15517
15518     // private
15519     relay : function(e){
15520         var k = e.getKey();
15521         var h = this.keyToHandler[k];
15522         if(h && this[h]){
15523             if(this.doRelay(e, this[h], h) !== true){
15524                 e[this.defaultEventAction]();
15525             }
15526         }
15527     },
15528
15529     // private
15530     doRelay : function(e, h, hname){
15531         return h.call(this.scope || this, e);
15532     },
15533
15534     // possible handlers
15535     enter : false,
15536     left : false,
15537     right : false,
15538     up : false,
15539     down : false,
15540     tab : false,
15541     esc : false,
15542     pageUp : false,
15543     pageDown : false,
15544     del : false,
15545     home : false,
15546     end : false,
15547
15548     // quick lookup hash
15549     keyToHandler : {
15550         37 : "left",
15551         39 : "right",
15552         38 : "up",
15553         40 : "down",
15554         33 : "pageUp",
15555         34 : "pageDown",
15556         46 : "del",
15557         36 : "home",
15558         35 : "end",
15559         13 : "enter",
15560         27 : "esc",
15561         9  : "tab"
15562     },
15563
15564         /**
15565          * Enable this KeyNav
15566          */
15567         enable: function(){
15568                 if(this.disabled){
15569             // ie won't do special keys on keypress, no one else will repeat keys with keydown
15570             // the EventObject will normalize Safari automatically
15571             if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15572                 this.el.on("keydown", this.relay,  this);
15573             }else{
15574                 this.el.on("keydown", this.prepareEvent,  this);
15575                 this.el.on("keypress", this.relay,  this);
15576             }
15577                     this.disabled = false;
15578                 }
15579         },
15580
15581         /**
15582          * Disable this KeyNav
15583          */
15584         disable: function(){
15585                 if(!this.disabled){
15586                     if(this.forceKeyDown || Roo.isIE || Roo.isAir){
15587                 this.el.un("keydown", this.relay);
15588             }else{
15589                 this.el.un("keydown", this.prepareEvent);
15590                 this.el.un("keypress", this.relay);
15591             }
15592                     this.disabled = true;
15593                 }
15594         }
15595 };/*
15596  * Based on:
15597  * Ext JS Library 1.1.1
15598  * Copyright(c) 2006-2007, Ext JS, LLC.
15599  *
15600  * Originally Released Under LGPL - original licence link has changed is not relivant.
15601  *
15602  * Fork - LGPL
15603  * <script type="text/javascript">
15604  */
15605
15606  
15607 /**
15608  * @class Roo.KeyMap
15609  * Handles mapping keys to actions for an element. One key map can be used for multiple actions.
15610  * The constructor accepts the same config object as defined by {@link #addBinding}.
15611  * If you bind a callback function to a KeyMap, anytime the KeyMap handles an expected key
15612  * combination it will call the function with this signature (if the match is a multi-key
15613  * combination the callback will still be called only once): (String key, Roo.EventObject e)
15614  * A KeyMap can also handle a string representation of keys.<br />
15615  * Usage:
15616  <pre><code>
15617 // map one key by key code
15618 var map = new Roo.KeyMap("my-element", {
15619     key: 13, // or Roo.EventObject.ENTER
15620     fn: myHandler,
15621     scope: myObject
15622 });
15623
15624 // map multiple keys to one action by string
15625 var map = new Roo.KeyMap("my-element", {
15626     key: "a\r\n\t",
15627     fn: myHandler,
15628     scope: myObject
15629 });
15630
15631 // map multiple keys to multiple actions by strings and array of codes
15632 var map = new Roo.KeyMap("my-element", [
15633     {
15634         key: [10,13],
15635         fn: function(){ alert("Return was pressed"); }
15636     }, {
15637         key: "abc",
15638         fn: function(){ alert('a, b or c was pressed'); }
15639     }, {
15640         key: "\t",
15641         ctrl:true,
15642         shift:true,
15643         fn: function(){ alert('Control + shift + tab was pressed.'); }
15644     }
15645 ]);
15646 </code></pre>
15647  * <b>Note: A KeyMap starts enabled</b>
15648  * @constructor
15649  * @param {String/HTMLElement/Roo.Element} el The element to bind to
15650  * @param {Object} config The config (see {@link #addBinding})
15651  * @param {String} eventName (optional) The event to bind to (defaults to "keydown")
15652  */
15653 Roo.KeyMap = function(el, config, eventName){
15654     this.el  = Roo.get(el);
15655     this.eventName = eventName || "keydown";
15656     this.bindings = [];
15657     if(config){
15658         this.addBinding(config);
15659     }
15660     this.enable();
15661 };
15662
15663 Roo.KeyMap.prototype = {
15664     /**
15665      * True to stop the event from bubbling and prevent the default browser action if the
15666      * key was handled by the KeyMap (defaults to false)
15667      * @type Boolean
15668      */
15669     stopEvent : false,
15670
15671     /**
15672      * Add a new binding to this KeyMap. The following config object properties are supported:
15673      * <pre>
15674 Property    Type             Description
15675 ----------  ---------------  ----------------------------------------------------------------------
15676 key         String/Array     A single keycode or an array of keycodes to handle
15677 shift       Boolean          True to handle key only when shift is pressed (defaults to false)
15678 ctrl        Boolean          True to handle key only when ctrl is pressed (defaults to false)
15679 alt         Boolean          True to handle key only when alt is pressed (defaults to false)
15680 fn          Function         The function to call when KeyMap finds the expected key combination
15681 scope       Object           The scope of the callback function
15682 </pre>
15683      *
15684      * Usage:
15685      * <pre><code>
15686 // Create a KeyMap
15687 var map = new Roo.KeyMap(document, {
15688     key: Roo.EventObject.ENTER,
15689     fn: handleKey,
15690     scope: this
15691 });
15692
15693 //Add a new binding to the existing KeyMap later
15694 map.addBinding({
15695     key: 'abc',
15696     shift: true,
15697     fn: handleKey,
15698     scope: this
15699 });
15700 </code></pre>
15701      * @param {Object/Array} config A single KeyMap config or an array of configs
15702      */
15703         addBinding : function(config){
15704         if(config instanceof Array){
15705             for(var i = 0, len = config.length; i < len; i++){
15706                 this.addBinding(config[i]);
15707             }
15708             return;
15709         }
15710         var keyCode = config.key,
15711             shift = config.shift, 
15712             ctrl = config.ctrl, 
15713             alt = config.alt,
15714             fn = config.fn,
15715             scope = config.scope;
15716         if(typeof keyCode == "string"){
15717             var ks = [];
15718             var keyString = keyCode.toUpperCase();
15719             for(var j = 0, len = keyString.length; j < len; j++){
15720                 ks.push(keyString.charCodeAt(j));
15721             }
15722             keyCode = ks;
15723         }
15724         var keyArray = keyCode instanceof Array;
15725         var handler = function(e){
15726             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
15727                 var k = e.getKey();
15728                 if(keyArray){
15729                     for(var i = 0, len = keyCode.length; i < len; i++){
15730                         if(keyCode[i] == k){
15731                           if(this.stopEvent){
15732                               e.stopEvent();
15733                           }
15734                           fn.call(scope || window, k, e);
15735                           return;
15736                         }
15737                     }
15738                 }else{
15739                     if(k == keyCode){
15740                         if(this.stopEvent){
15741                            e.stopEvent();
15742                         }
15743                         fn.call(scope || window, k, e);
15744                     }
15745                 }
15746             }
15747         };
15748         this.bindings.push(handler);  
15749         },
15750
15751     /**
15752      * Shorthand for adding a single key listener
15753      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the
15754      * following options:
15755      * {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
15756      * @param {Function} fn The function to call
15757      * @param {Object} scope (optional) The scope of the function
15758      */
15759     on : function(key, fn, scope){
15760         var keyCode, shift, ctrl, alt;
15761         if(typeof key == "object" && !(key instanceof Array)){
15762             keyCode = key.key;
15763             shift = key.shift;
15764             ctrl = key.ctrl;
15765             alt = key.alt;
15766         }else{
15767             keyCode = key;
15768         }
15769         this.addBinding({
15770             key: keyCode,
15771             shift: shift,
15772             ctrl: ctrl,
15773             alt: alt,
15774             fn: fn,
15775             scope: scope
15776         })
15777     },
15778
15779     // private
15780     handleKeyDown : function(e){
15781             if(this.enabled){ //just in case
15782             var b = this.bindings;
15783             for(var i = 0, len = b.length; i < len; i++){
15784                 b[i].call(this, e);
15785             }
15786             }
15787         },
15788         
15789         /**
15790          * Returns true if this KeyMap is enabled
15791          * @return {Boolean} 
15792          */
15793         isEnabled : function(){
15794             return this.enabled;  
15795         },
15796         
15797         /**
15798          * Enables this KeyMap
15799          */
15800         enable: function(){
15801                 if(!this.enabled){
15802                     this.el.on(this.eventName, this.handleKeyDown, this);
15803                     this.enabled = true;
15804                 }
15805         },
15806
15807         /**
15808          * Disable this KeyMap
15809          */
15810         disable: function(){
15811                 if(this.enabled){
15812                     this.el.removeListener(this.eventName, this.handleKeyDown, this);
15813                     this.enabled = false;
15814                 }
15815         }
15816 };/*
15817  * Based on:
15818  * Ext JS Library 1.1.1
15819  * Copyright(c) 2006-2007, Ext JS, LLC.
15820  *
15821  * Originally Released Under LGPL - original licence link has changed is not relivant.
15822  *
15823  * Fork - LGPL
15824  * <script type="text/javascript">
15825  */
15826
15827  
15828 /**
15829  * @class Roo.util.TextMetrics
15830  * Provides precise pixel measurements for blocks of text so that you can determine exactly how high and
15831  * wide, in pixels, a given block of text will be.
15832  * @static
15833  */
15834 Roo.util.TextMetrics = function(){
15835     var shared;
15836     return {
15837         /**
15838          * Measures the size of the specified text
15839          * @param {String/HTMLElement} el The element, dom node or id from which to copy existing CSS styles
15840          * that can affect the size of the rendered text
15841          * @param {String} text The text to measure
15842          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15843          * in order to accurately measure the text height
15844          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15845          */
15846         measure : function(el, text, fixedWidth){
15847             if(!shared){
15848                 shared = Roo.util.TextMetrics.Instance(el, fixedWidth);
15849             }
15850             shared.bind(el);
15851             shared.setFixedWidth(fixedWidth || 'auto');
15852             return shared.getSize(text);
15853         },
15854
15855         /**
15856          * Return a unique TextMetrics instance that can be bound directly to an element and reused.  This reduces
15857          * the overhead of multiple calls to initialize the style properties on each measurement.
15858          * @param {String/HTMLElement} el The element, dom node or id that the instance will be bound to
15859          * @param {Number} fixedWidth (optional) If the text will be multiline, you have to set a fixed width
15860          * in order to accurately measure the text height
15861          * @return {Roo.util.TextMetrics.Instance} instance The new instance
15862          */
15863         createInstance : function(el, fixedWidth){
15864             return Roo.util.TextMetrics.Instance(el, fixedWidth);
15865         }
15866     };
15867 }();
15868
15869 /**
15870  * @class Roo.util.TextMetrics.Instance
15871  * Instance of  TextMetrics Calcuation
15872  * @constructor
15873  * Create a new TextMetrics Instance
15874  * @param {Object} bindto
15875  * @param {Boolean} fixedWidth
15876  */
15877
15878 Roo.util.TextMetrics.Instance = function(bindTo, fixedWidth)
15879 {
15880     var ml = new Roo.Element(document.createElement('div'));
15881     document.body.appendChild(ml.dom);
15882     ml.position('absolute');
15883     ml.setLeftTop(-1000, -1000);
15884     ml.hide();
15885
15886     if(fixedWidth){
15887         ml.setWidth(fixedWidth);
15888     }
15889      
15890     var instance = {
15891         /**
15892          * Returns the size of the specified text based on the internal element's style and width properties
15893          * @param {String} text The text to measure
15894          * @return {Object} An object containing the text's size {width: (width), height: (height)}
15895          */
15896         getSize : function(text){
15897             ml.update(text);
15898             var s = ml.getSize();
15899             ml.update('');
15900             return s;
15901         },
15902
15903         /**
15904          * Binds this TextMetrics instance to an element from which to copy existing CSS styles
15905          * that can affect the size of the rendered text
15906          * @param {String/HTMLElement} el The element, dom node or id
15907          */
15908         bind : function(el){
15909             ml.setStyle(
15910                 Roo.fly(el).getStyles('font-size','font-style', 'font-weight', 'font-family','line-height')
15911             );
15912         },
15913
15914         /**
15915          * Sets a fixed width on the internal measurement element.  If the text will be multiline, you have
15916          * to set a fixed width in order to accurately measure the text height.
15917          * @param {Number} width The width to set on the element
15918          */
15919         setFixedWidth : function(width){
15920             ml.setWidth(width);
15921         },
15922
15923         /**
15924          * Returns the measured width of the specified text
15925          * @param {String} text The text to measure
15926          * @return {Number} width The width in pixels
15927          */
15928         getWidth : function(text){
15929             ml.dom.style.width = 'auto';
15930             return this.getSize(text).width;
15931         },
15932
15933         /**
15934          * Returns the measured height of the specified text.  For multiline text, be sure to call
15935          * {@link #setFixedWidth} if necessary.
15936          * @param {String} text The text to measure
15937          * @return {Number} height The height in pixels
15938          */
15939         getHeight : function(text){
15940             return this.getSize(text).height;
15941         }
15942     };
15943
15944     instance.bind(bindTo);
15945
15946     return instance;
15947 };
15948
15949 // backwards compat
15950 Roo.Element.measureText = Roo.util.TextMetrics.measure;/*
15951  * Based on:
15952  * Ext JS Library 1.1.1
15953  * Copyright(c) 2006-2007, Ext JS, LLC.
15954  *
15955  * Originally Released Under LGPL - original licence link has changed is not relivant.
15956  *
15957  * Fork - LGPL
15958  * <script type="text/javascript">
15959  */
15960
15961 /**
15962  * @class Roo.state.Provider
15963  * Abstract base class for state provider implementations. This class provides methods
15964  * for encoding and decoding <b>typed</b> variables including dates and defines the 
15965  * Provider interface.
15966  */
15967 Roo.state.Provider = function(){
15968     /**
15969      * @event statechange
15970      * Fires when a state change occurs.
15971      * @param {Provider} this This state provider
15972      * @param {String} key The state key which was changed
15973      * @param {String} value The encoded value for the state
15974      */
15975     this.addEvents({
15976         "statechange": true
15977     });
15978     this.state = {};
15979     Roo.state.Provider.superclass.constructor.call(this);
15980 };
15981 Roo.extend(Roo.state.Provider, Roo.util.Observable, {
15982     /**
15983      * Returns the current value for a key
15984      * @param {String} name The key name
15985      * @param {Mixed} defaultValue A default value to return if the key's value is not found
15986      * @return {Mixed} The state data
15987      */
15988     get : function(name, defaultValue){
15989         return typeof this.state[name] == "undefined" ?
15990             defaultValue : this.state[name];
15991     },
15992     
15993     /**
15994      * Clears a value from the state
15995      * @param {String} name The key name
15996      */
15997     clear : function(name){
15998         delete this.state[name];
15999         this.fireEvent("statechange", this, name, null);
16000     },
16001     
16002     /**
16003      * Sets the value for a key
16004      * @param {String} name The key name
16005      * @param {Mixed} value The value to set
16006      */
16007     set : function(name, value){
16008         this.state[name] = value;
16009         this.fireEvent("statechange", this, name, value);
16010     },
16011     
16012     /**
16013      * Decodes a string previously encoded with {@link #encodeValue}.
16014      * @param {String} value The value to decode
16015      * @return {Mixed} The decoded value
16016      */
16017     decodeValue : function(cookie){
16018         var re = /^(a|n|d|b|s|o)\:(.*)$/;
16019         var matches = re.exec(unescape(cookie));
16020         if(!matches || !matches[1]) {
16021             return; // non state cookie
16022         }
16023         var type = matches[1];
16024         var v = matches[2];
16025         switch(type){
16026             case "n":
16027                 return parseFloat(v);
16028             case "d":
16029                 return new Date(Date.parse(v));
16030             case "b":
16031                 return (v == "1");
16032             case "a":
16033                 var all = [];
16034                 var values = v.split("^");
16035                 for(var i = 0, len = values.length; i < len; i++){
16036                     all.push(this.decodeValue(values[i]));
16037                 }
16038                 return all;
16039            case "o":
16040                 var all = {};
16041                 var values = v.split("^");
16042                 for(var i = 0, len = values.length; i < len; i++){
16043                     var kv = values[i].split("=");
16044                     all[kv[0]] = this.decodeValue(kv[1]);
16045                 }
16046                 return all;
16047            default:
16048                 return v;
16049         }
16050     },
16051     
16052     /**
16053      * Encodes a value including type information.  Decode with {@link #decodeValue}.
16054      * @param {Mixed} value The value to encode
16055      * @return {String} The encoded value
16056      */
16057     encodeValue : function(v){
16058         var enc;
16059         if(typeof v == "number"){
16060             enc = "n:" + v;
16061         }else if(typeof v == "boolean"){
16062             enc = "b:" + (v ? "1" : "0");
16063         }else if(v instanceof Date){
16064             enc = "d:" + v.toGMTString();
16065         }else if(v instanceof Array){
16066             var flat = "";
16067             for(var i = 0, len = v.length; i < len; i++){
16068                 flat += this.encodeValue(v[i]);
16069                 if(i != len-1) {
16070                     flat += "^";
16071                 }
16072             }
16073             enc = "a:" + flat;
16074         }else if(typeof v == "object"){
16075             var flat = "";
16076             for(var key in v){
16077                 if(typeof v[key] != "function"){
16078                     flat += key + "=" + this.encodeValue(v[key]) + "^";
16079                 }
16080             }
16081             enc = "o:" + flat.substring(0, flat.length-1);
16082         }else{
16083             enc = "s:" + v;
16084         }
16085         return escape(enc);        
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.Manager
16101  * This is the global state manager. By default all components that are "state aware" check this class
16102  * for state information if you don't pass them a custom state provider. In order for this class
16103  * to be useful, it must be initialized with a provider when your application initializes.
16104  <pre><code>
16105 // in your initialization function
16106 init : function(){
16107    Roo.state.Manager.setProvider(new Roo.state.CookieProvider());
16108    ...
16109    // supposed you have a {@link Roo.BorderLayout}
16110    var layout = new Roo.BorderLayout(...);
16111    layout.restoreState();
16112    // or a {Roo.BasicDialog}
16113    var dialog = new Roo.BasicDialog(...);
16114    dialog.restoreState();
16115  </code></pre>
16116  * @static
16117  */
16118 Roo.state.Manager = function(){
16119     var provider = new Roo.state.Provider();
16120     
16121     return {
16122         /**
16123          * Configures the default state provider for your application
16124          * @param {Provider} stateProvider The state provider to set
16125          */
16126         setProvider : function(stateProvider){
16127             provider = stateProvider;
16128         },
16129         
16130         /**
16131          * Returns the current value for a key
16132          * @param {String} name The key name
16133          * @param {Mixed} defaultValue The default value to return if the key lookup does not match
16134          * @return {Mixed} The state data
16135          */
16136         get : function(key, defaultValue){
16137             return provider.get(key, defaultValue);
16138         },
16139         
16140         /**
16141          * Sets the value for a key
16142          * @param {String} name The key name
16143          * @param {Mixed} value The state data
16144          */
16145          set : function(key, value){
16146             provider.set(key, value);
16147         },
16148         
16149         /**
16150          * Clears a value from the state
16151          * @param {String} name The key name
16152          */
16153         clear : function(key){
16154             provider.clear(key);
16155         },
16156         
16157         /**
16158          * Gets the currently configured state provider
16159          * @return {Provider} The state provider
16160          */
16161         getProvider : function(){
16162             return provider;
16163         }
16164     };
16165 }();
16166 /*
16167  * Based on:
16168  * Ext JS Library 1.1.1
16169  * Copyright(c) 2006-2007, Ext JS, LLC.
16170  *
16171  * Originally Released Under LGPL - original licence link has changed is not relivant.
16172  *
16173  * Fork - LGPL
16174  * <script type="text/javascript">
16175  */
16176 /**
16177  * @class Roo.state.CookieProvider
16178  * @extends Roo.state.Provider
16179  * The default Provider implementation which saves state via cookies.
16180  * <br />Usage:
16181  <pre><code>
16182    var cp = new Roo.state.CookieProvider({
16183        path: "/cgi-bin/",
16184        expires: new Date(new Date().getTime()+(1000*60*60*24*30)); //30 days
16185        domain: "roojs.com"
16186    })
16187    Roo.state.Manager.setProvider(cp);
16188  </code></pre>
16189  * @cfg {String} path The path for which the cookie is active (defaults to root '/' which makes it active for all pages in the site)
16190  * @cfg {Date} expires The cookie expiration date (defaults to 7 days from now)
16191  * @cfg {String} domain The domain to save the cookie for.  Note that you cannot specify a different domain than
16192  * your page is on, but you can specify a sub-domain, or simply the domain itself like 'roojs.com' to include
16193  * all sub-domains if you need to access cookies across different sub-domains (defaults to null which uses the same
16194  * domain the page is running on including the 'www' like 'www.roojs.com')
16195  * @cfg {Boolean} secure True if the site is using SSL (defaults to false)
16196  * @constructor
16197  * Create a new CookieProvider
16198  * @param {Object} config The configuration object
16199  */
16200 Roo.state.CookieProvider = function(config){
16201     Roo.state.CookieProvider.superclass.constructor.call(this);
16202     this.path = "/";
16203     this.expires = new Date(new Date().getTime()+(1000*60*60*24*7)); //7 days
16204     this.domain = null;
16205     this.secure = false;
16206     Roo.apply(this, config);
16207     this.state = this.readCookies();
16208 };
16209
16210 Roo.extend(Roo.state.CookieProvider, Roo.state.Provider, {
16211     // private
16212     set : function(name, value){
16213         if(typeof value == "undefined" || value === null){
16214             this.clear(name);
16215             return;
16216         }
16217         this.setCookie(name, value);
16218         Roo.state.CookieProvider.superclass.set.call(this, name, value);
16219     },
16220
16221     // private
16222     clear : function(name){
16223         this.clearCookie(name);
16224         Roo.state.CookieProvider.superclass.clear.call(this, name);
16225     },
16226
16227     // private
16228     readCookies : function(){
16229         var cookies = {};
16230         var c = document.cookie + ";";
16231         var re = /\s?(.*?)=(.*?);/g;
16232         var matches;
16233         while((matches = re.exec(c)) != null){
16234             var name = matches[1];
16235             var value = matches[2];
16236             if(name && name.substring(0,3) == "ys-"){
16237                 cookies[name.substr(3)] = this.decodeValue(value);
16238             }
16239         }
16240         return cookies;
16241     },
16242
16243     // private
16244     setCookie : function(name, value){
16245         document.cookie = "ys-"+ name + "=" + this.encodeValue(value) +
16246            ((this.expires == null) ? "" : ("; expires=" + this.expires.toGMTString())) +
16247            ((this.path == null) ? "" : ("; path=" + this.path)) +
16248            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16249            ((this.secure == true) ? "; secure" : "");
16250     },
16251
16252     // private
16253     clearCookie : function(name){
16254         document.cookie = "ys-" + name + "=null; expires=Thu, 01-Jan-70 00:00:01 GMT" +
16255            ((this.path == null) ? "" : ("; path=" + this.path)) +
16256            ((this.domain == null) ? "" : ("; domain=" + this.domain)) +
16257            ((this.secure == true) ? "; secure" : "");
16258     }
16259 });/*
16260  * Based on:
16261  * Ext JS Library 1.1.1
16262  * Copyright(c) 2006-2007, Ext JS, LLC.
16263  *
16264  * Originally Released Under LGPL - original licence link has changed is not relivant.
16265  *
16266  * Fork - LGPL
16267  * <script type="text/javascript">
16268  */
16269  
16270
16271 /**
16272  * @class Roo.ComponentMgr
16273  * Provides a common registry of all components on a page so that they can be easily accessed by component id (see {@link Roo.getCmp}).
16274  * @static
16275  */
16276 Roo.ComponentMgr = function(){
16277     var all = new Roo.util.MixedCollection();
16278
16279     return {
16280         /**
16281          * Registers a component.
16282          * @param {Roo.Component} c The component
16283          */
16284         register : function(c){
16285             all.add(c);
16286         },
16287
16288         /**
16289          * Unregisters a component.
16290          * @param {Roo.Component} c The component
16291          */
16292         unregister : function(c){
16293             all.remove(c);
16294         },
16295
16296         /**
16297          * Returns a component by id
16298          * @param {String} id The component id
16299          */
16300         get : function(id){
16301             return all.get(id);
16302         },
16303
16304         /**
16305          * Registers a function that will be called when a specified component is added to ComponentMgr
16306          * @param {String} id The component id
16307          * @param {Funtction} fn The callback function
16308          * @param {Object} scope The scope of the callback
16309          */
16310         onAvailable : function(id, fn, scope){
16311             all.on("add", function(index, o){
16312                 if(o.id == id){
16313                     fn.call(scope || o, o);
16314                     all.un("add", fn, scope);
16315                 }
16316             });
16317         }
16318     };
16319 }();/*
16320  * Based on:
16321  * Ext JS Library 1.1.1
16322  * Copyright(c) 2006-2007, Ext JS, LLC.
16323  *
16324  * Originally Released Under LGPL - original licence link has changed is not relivant.
16325  *
16326  * Fork - LGPL
16327  * <script type="text/javascript">
16328  */
16329  
16330 /**
16331  * @class Roo.Component
16332  * @extends Roo.util.Observable
16333  * Base class for all major Roo components.  All subclasses of Component can automatically participate in the standard
16334  * Roo component lifecycle of creation, rendering and destruction.  They also have automatic support for basic hide/show
16335  * and enable/disable behavior.  Component allows any subclass to be lazy-rendered into any {@link Roo.Container} and
16336  * to be automatically registered with the {@link Roo.ComponentMgr} so that it can be referenced at any time via {@link Roo.getCmp}.
16337  * All visual components (widgets) that require rendering into a layout should subclass Component.
16338  * @constructor
16339  * @param {Roo.Element/String/Object} config The configuration options.  If an element is passed, it is set as the internal
16340  * 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
16341  * and is used as the component id.  Otherwise, it is assumed to be a standard config object and is applied to the component.
16342  */
16343 Roo.Component = function(config){
16344     config = config || {};
16345     if(config.tagName || config.dom || typeof config == "string"){ // element object
16346         config = {el: config, id: config.id || config};
16347     }
16348     this.initialConfig = config;
16349
16350     Roo.apply(this, config);
16351     this.addEvents({
16352         /**
16353          * @event disable
16354          * Fires after the component is disabled.
16355              * @param {Roo.Component} this
16356              */
16357         disable : true,
16358         /**
16359          * @event enable
16360          * Fires after the component is enabled.
16361              * @param {Roo.Component} this
16362              */
16363         enable : true,
16364         /**
16365          * @event beforeshow
16366          * Fires before the component is shown.  Return false to stop the show.
16367              * @param {Roo.Component} this
16368              */
16369         beforeshow : true,
16370         /**
16371          * @event show
16372          * Fires after the component is shown.
16373              * @param {Roo.Component} this
16374              */
16375         show : true,
16376         /**
16377          * @event beforehide
16378          * Fires before the component is hidden. Return false to stop the hide.
16379              * @param {Roo.Component} this
16380              */
16381         beforehide : true,
16382         /**
16383          * @event hide
16384          * Fires after the component is hidden.
16385              * @param {Roo.Component} this
16386              */
16387         hide : true,
16388         /**
16389          * @event beforerender
16390          * Fires before the component is rendered. Return false to stop the render.
16391              * @param {Roo.Component} this
16392              */
16393         beforerender : true,
16394         /**
16395          * @event render
16396          * Fires after the component is rendered.
16397              * @param {Roo.Component} this
16398              */
16399         render : true,
16400         /**
16401          * @event beforedestroy
16402          * Fires before the component is destroyed. Return false to stop the destroy.
16403              * @param {Roo.Component} this
16404              */
16405         beforedestroy : true,
16406         /**
16407          * @event destroy
16408          * Fires after the component is destroyed.
16409              * @param {Roo.Component} this
16410              */
16411         destroy : true
16412     });
16413     if(!this.id){
16414         this.id = "roo-comp-" + (++Roo.Component.AUTO_ID);
16415     }
16416     Roo.ComponentMgr.register(this);
16417     Roo.Component.superclass.constructor.call(this);
16418     this.initComponent();
16419     if(this.renderTo){ // not supported by all components yet. use at your own risk!
16420         this.render(this.renderTo);
16421         delete this.renderTo;
16422     }
16423 };
16424
16425 /** @private */
16426 Roo.Component.AUTO_ID = 1000;
16427
16428 Roo.extend(Roo.Component, Roo.util.Observable, {
16429     /**
16430      * @scope Roo.Component.prototype
16431      * @type {Boolean}
16432      * true if this component is hidden. Read-only.
16433      */
16434     hidden : false,
16435     /**
16436      * @type {Boolean}
16437      * true if this component is disabled. Read-only.
16438      */
16439     disabled : false,
16440     /**
16441      * @type {Boolean}
16442      * true if this component has been rendered. Read-only.
16443      */
16444     rendered : false,
16445     
16446     /** @cfg {String} disableClass
16447      * CSS class added to the component when it is disabled (defaults to "x-item-disabled").
16448      */
16449     disabledClass : "x-item-disabled",
16450         /** @cfg {Boolean} allowDomMove
16451          * Whether the component can move the Dom node when rendering (defaults to true).
16452          */
16453     allowDomMove : true,
16454     /** @cfg {String} hideMode (display|visibility)
16455      * How this component should hidden. Supported values are
16456      * "visibility" (css visibility), "offsets" (negative offset position) and
16457      * "display" (css display) - defaults to "display".
16458      */
16459     hideMode: 'display',
16460
16461     /** @private */
16462     ctype : "Roo.Component",
16463
16464     /**
16465      * @cfg {String} actionMode 
16466      * which property holds the element that used for  hide() / show() / disable() / enable()
16467      * default is 'el' for forms you probably want to set this to fieldEl 
16468      */
16469     actionMode : "el",
16470
16471     /** @private */
16472     getActionEl : function(){
16473         return this[this.actionMode];
16474     },
16475
16476     initComponent : Roo.emptyFn,
16477     /**
16478      * If this is a lazy rendering component, render it to its container element.
16479      * @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.
16480      */
16481     render : function(container, position){
16482         
16483         if(this.rendered){
16484             return this;
16485         }
16486         
16487         if(this.fireEvent("beforerender", this) === false){
16488             return false;
16489         }
16490         
16491         if(!container && this.el){
16492             this.el = Roo.get(this.el);
16493             container = this.el.dom.parentNode;
16494             this.allowDomMove = false;
16495         }
16496         this.container = Roo.get(container);
16497         this.rendered = true;
16498         if(position !== undefined){
16499             if(typeof position == 'number'){
16500                 position = this.container.dom.childNodes[position];
16501             }else{
16502                 position = Roo.getDom(position);
16503             }
16504         }
16505         this.onRender(this.container, position || null);
16506         if(this.cls){
16507             this.el.addClass(this.cls);
16508             delete this.cls;
16509         }
16510         if(this.style){
16511             this.el.applyStyles(this.style);
16512             delete this.style;
16513         }
16514         this.fireEvent("render", this);
16515         this.afterRender(this.container);
16516         if(this.hidden){
16517             this.hide();
16518         }
16519         if(this.disabled){
16520             this.disable();
16521         }
16522
16523         return this;
16524         
16525     },
16526
16527     /** @private */
16528     // default function is not really useful
16529     onRender : function(ct, position){
16530         if(this.el){
16531             this.el = Roo.get(this.el);
16532             if(this.allowDomMove !== false){
16533                 ct.dom.insertBefore(this.el.dom, position);
16534             }
16535         }
16536     },
16537
16538     /** @private */
16539     getAutoCreate : function(){
16540         var cfg = typeof this.autoCreate == "object" ?
16541                       this.autoCreate : Roo.apply({}, this.defaultAutoCreate);
16542         if(this.id && !cfg.id){
16543             cfg.id = this.id;
16544         }
16545         return cfg;
16546     },
16547
16548     /** @private */
16549     afterRender : Roo.emptyFn,
16550
16551     /**
16552      * Destroys this component by purging any event listeners, removing the component's element from the DOM,
16553      * removing the component from its {@link Roo.Container} (if applicable) and unregistering it from {@link Roo.ComponentMgr}.
16554      */
16555     destroy : function(){
16556         if(this.fireEvent("beforedestroy", this) !== false){
16557             this.purgeListeners();
16558             this.beforeDestroy();
16559             if(this.rendered){
16560                 this.el.removeAllListeners();
16561                 this.el.remove();
16562                 if(this.actionMode == "container"){
16563                     this.container.remove();
16564                 }
16565             }
16566             this.onDestroy();
16567             Roo.ComponentMgr.unregister(this);
16568             this.fireEvent("destroy", this);
16569         }
16570     },
16571
16572         /** @private */
16573     beforeDestroy : function(){
16574
16575     },
16576
16577         /** @private */
16578         onDestroy : function(){
16579
16580     },
16581
16582     /**
16583      * Returns the underlying {@link Roo.Element}.
16584      * @return {Roo.Element} The element
16585      */
16586     getEl : function(){
16587         return this.el;
16588     },
16589
16590     /**
16591      * Returns the id of this component.
16592      * @return {String}
16593      */
16594     getId : function(){
16595         return this.id;
16596     },
16597
16598     /**
16599      * Try to focus this component.
16600      * @param {Boolean} selectText True to also select the text in this component (if applicable)
16601      * @return {Roo.Component} this
16602      */
16603     focus : function(selectText){
16604         if(this.rendered){
16605             this.el.focus();
16606             if(selectText === true){
16607                 this.el.dom.select();
16608             }
16609         }
16610         return this;
16611     },
16612
16613     /** @private */
16614     blur : function(){
16615         if(this.rendered){
16616             this.el.blur();
16617         }
16618         return this;
16619     },
16620
16621     /**
16622      * Disable this component.
16623      * @return {Roo.Component} this
16624      */
16625     disable : function(){
16626         if(this.rendered){
16627             this.onDisable();
16628         }
16629         this.disabled = true;
16630         this.fireEvent("disable", this);
16631         return this;
16632     },
16633
16634         // private
16635     onDisable : function(){
16636         this.getActionEl().addClass(this.disabledClass);
16637         this.el.dom.disabled = true;
16638     },
16639
16640     /**
16641      * Enable this component.
16642      * @return {Roo.Component} this
16643      */
16644     enable : function(){
16645         if(this.rendered){
16646             this.onEnable();
16647         }
16648         this.disabled = false;
16649         this.fireEvent("enable", this);
16650         return this;
16651     },
16652
16653         // private
16654     onEnable : function(){
16655         this.getActionEl().removeClass(this.disabledClass);
16656         this.el.dom.disabled = false;
16657     },
16658
16659     /**
16660      * Convenience function for setting disabled/enabled by boolean.
16661      * @param {Boolean} disabled
16662      */
16663     setDisabled : function(disabled){
16664         this[disabled ? "disable" : "enable"]();
16665     },
16666
16667     /**
16668      * Show this component.
16669      * @return {Roo.Component} this
16670      */
16671     show: function(){
16672         if(this.fireEvent("beforeshow", this) !== false){
16673             this.hidden = false;
16674             if(this.rendered){
16675                 this.onShow();
16676             }
16677             this.fireEvent("show", this);
16678         }
16679         return this;
16680     },
16681
16682     // private
16683     onShow : function(){
16684         var ae = this.getActionEl();
16685         if(this.hideMode == 'visibility'){
16686             ae.dom.style.visibility = "visible";
16687         }else if(this.hideMode == 'offsets'){
16688             ae.removeClass('x-hidden');
16689         }else{
16690             ae.dom.style.display = "";
16691         }
16692     },
16693
16694     /**
16695      * Hide this component.
16696      * @return {Roo.Component} this
16697      */
16698     hide: function(){
16699         if(this.fireEvent("beforehide", this) !== false){
16700             this.hidden = true;
16701             if(this.rendered){
16702                 this.onHide();
16703             }
16704             this.fireEvent("hide", this);
16705         }
16706         return this;
16707     },
16708
16709     // private
16710     onHide : function(){
16711         var ae = this.getActionEl();
16712         if(this.hideMode == 'visibility'){
16713             ae.dom.style.visibility = "hidden";
16714         }else if(this.hideMode == 'offsets'){
16715             ae.addClass('x-hidden');
16716         }else{
16717             ae.dom.style.display = "none";
16718         }
16719     },
16720
16721     /**
16722      * Convenience function to hide or show this component by boolean.
16723      * @param {Boolean} visible True to show, false to hide
16724      * @return {Roo.Component} this
16725      */
16726     setVisible: function(visible){
16727         if(visible) {
16728             this.show();
16729         }else{
16730             this.hide();
16731         }
16732         return this;
16733     },
16734
16735     /**
16736      * Returns true if this component is visible.
16737      */
16738     isVisible : function(){
16739         return this.getActionEl().isVisible();
16740     },
16741
16742     cloneConfig : function(overrides){
16743         overrides = overrides || {};
16744         var id = overrides.id || Roo.id();
16745         var cfg = Roo.applyIf(overrides, this.initialConfig);
16746         cfg.id = id; // prevent dup id
16747         return new this.constructor(cfg);
16748     }
16749 });/*
16750  * Based on:
16751  * Ext JS Library 1.1.1
16752  * Copyright(c) 2006-2007, Ext JS, LLC.
16753  *
16754  * Originally Released Under LGPL - original licence link has changed is not relivant.
16755  *
16756  * Fork - LGPL
16757  * <script type="text/javascript">
16758  */
16759
16760 /**
16761  * @class Roo.BoxComponent
16762  * @extends Roo.Component
16763  * Base class for any visual {@link Roo.Component} that uses a box container.  BoxComponent provides automatic box
16764  * model adjustments for sizing and positioning and will work correctly withnin the Component rendering model.  All
16765  * container classes should subclass BoxComponent so that they will work consistently when nested within other Roo
16766  * layout containers.
16767  * @constructor
16768  * @param {Roo.Element/String/Object} config The configuration options.
16769  */
16770 Roo.BoxComponent = function(config){
16771     Roo.Component.call(this, config);
16772     this.addEvents({
16773         /**
16774          * @event resize
16775          * Fires after the component is resized.
16776              * @param {Roo.Component} this
16777              * @param {Number} adjWidth The box-adjusted width that was set
16778              * @param {Number} adjHeight The box-adjusted height that was set
16779              * @param {Number} rawWidth The width that was originally specified
16780              * @param {Number} rawHeight The height that was originally specified
16781              */
16782         resize : true,
16783         /**
16784          * @event move
16785          * Fires after the component is moved.
16786              * @param {Roo.Component} this
16787              * @param {Number} x The new x position
16788              * @param {Number} y The new y position
16789              */
16790         move : true
16791     });
16792 };
16793
16794 Roo.extend(Roo.BoxComponent, Roo.Component, {
16795     // private, set in afterRender to signify that the component has been rendered
16796     boxReady : false,
16797     // private, used to defer height settings to subclasses
16798     deferHeight: false,
16799     /** @cfg {Number} width
16800      * width (optional) size of component
16801      */
16802      /** @cfg {Number} height
16803      * height (optional) size of component
16804      */
16805      
16806     /**
16807      * Sets the width and height of the component.  This method fires the resize event.  This method can accept
16808      * either width and height as separate numeric arguments, or you can pass a size object like {width:10, height:20}.
16809      * @param {Number/Object} width The new width to set, or a size object in the format {width, height}
16810      * @param {Number} height The new height to set (not required if a size object is passed as the first arg)
16811      * @return {Roo.BoxComponent} this
16812      */
16813     setSize : function(w, h){
16814         // support for standard size objects
16815         if(typeof w == 'object'){
16816             h = w.height;
16817             w = w.width;
16818         }
16819         // not rendered
16820         if(!this.boxReady){
16821             this.width = w;
16822             this.height = h;
16823             return this;
16824         }
16825
16826         // prevent recalcs when not needed
16827         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
16828             return this;
16829         }
16830         this.lastSize = {width: w, height: h};
16831
16832         var adj = this.adjustSize(w, h);
16833         var aw = adj.width, ah = adj.height;
16834         if(aw !== undefined || ah !== undefined){ // this code is nasty but performs better with floaters
16835             var rz = this.getResizeEl();
16836             if(!this.deferHeight && aw !== undefined && ah !== undefined){
16837                 rz.setSize(aw, ah);
16838             }else if(!this.deferHeight && ah !== undefined){
16839                 rz.setHeight(ah);
16840             }else if(aw !== undefined){
16841                 rz.setWidth(aw);
16842             }
16843             this.onResize(aw, ah, w, h);
16844             this.fireEvent('resize', this, aw, ah, w, h);
16845         }
16846         return this;
16847     },
16848
16849     /**
16850      * Gets the current size of the component's underlying element.
16851      * @return {Object} An object containing the element's size {width: (element width), height: (element height)}
16852      */
16853     getSize : function(){
16854         return this.el.getSize();
16855     },
16856
16857     /**
16858      * Gets the current XY position of the component's underlying element.
16859      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16860      * @return {Array} The XY position of the element (e.g., [100, 200])
16861      */
16862     getPosition : function(local){
16863         if(local === true){
16864             return [this.el.getLeft(true), this.el.getTop(true)];
16865         }
16866         return this.xy || this.el.getXY();
16867     },
16868
16869     /**
16870      * Gets the current box measurements of the component's underlying element.
16871      * @param {Boolean} local (optional) If true the element's left and top are returned instead of page XY (defaults to false)
16872      * @returns {Object} box An object in the format {x, y, width, height}
16873      */
16874     getBox : function(local){
16875         var s = this.el.getSize();
16876         if(local){
16877             s.x = this.el.getLeft(true);
16878             s.y = this.el.getTop(true);
16879         }else{
16880             var xy = this.xy || this.el.getXY();
16881             s.x = xy[0];
16882             s.y = xy[1];
16883         }
16884         return s;
16885     },
16886
16887     /**
16888      * Sets the current box measurements of the component's underlying element.
16889      * @param {Object} box An object in the format {x, y, width, height}
16890      * @returns {Roo.BoxComponent} this
16891      */
16892     updateBox : function(box){
16893         this.setSize(box.width, box.height);
16894         this.setPagePosition(box.x, box.y);
16895         return this;
16896     },
16897
16898     // protected
16899     getResizeEl : function(){
16900         return this.resizeEl || this.el;
16901     },
16902
16903     // protected
16904     getPositionEl : function(){
16905         return this.positionEl || this.el;
16906     },
16907
16908     /**
16909      * Sets the left and top of the component.  To set the page XY position instead, use {@link #setPagePosition}.
16910      * This method fires the move event.
16911      * @param {Number} left The new left
16912      * @param {Number} top The new top
16913      * @returns {Roo.BoxComponent} this
16914      */
16915     setPosition : function(x, y){
16916         this.x = x;
16917         this.y = y;
16918         if(!this.boxReady){
16919             return this;
16920         }
16921         var adj = this.adjustPosition(x, y);
16922         var ax = adj.x, ay = adj.y;
16923
16924         var el = this.getPositionEl();
16925         if(ax !== undefined || ay !== undefined){
16926             if(ax !== undefined && ay !== undefined){
16927                 el.setLeftTop(ax, ay);
16928             }else if(ax !== undefined){
16929                 el.setLeft(ax);
16930             }else if(ay !== undefined){
16931                 el.setTop(ay);
16932             }
16933             this.onPosition(ax, ay);
16934             this.fireEvent('move', this, ax, ay);
16935         }
16936         return this;
16937     },
16938
16939     /**
16940      * Sets the page XY position of the component.  To set the left and top instead, use {@link #setPosition}.
16941      * This method fires the move event.
16942      * @param {Number} x The new x position
16943      * @param {Number} y The new y position
16944      * @returns {Roo.BoxComponent} this
16945      */
16946     setPagePosition : function(x, y){
16947         this.pageX = x;
16948         this.pageY = y;
16949         if(!this.boxReady){
16950             return;
16951         }
16952         if(x === undefined || y === undefined){ // cannot translate undefined points
16953             return;
16954         }
16955         var p = this.el.translatePoints(x, y);
16956         this.setPosition(p.left, p.top);
16957         return this;
16958     },
16959
16960     // private
16961     onRender : function(ct, position){
16962         Roo.BoxComponent.superclass.onRender.call(this, ct, position);
16963         if(this.resizeEl){
16964             this.resizeEl = Roo.get(this.resizeEl);
16965         }
16966         if(this.positionEl){
16967             this.positionEl = Roo.get(this.positionEl);
16968         }
16969     },
16970
16971     // private
16972     afterRender : function(){
16973         Roo.BoxComponent.superclass.afterRender.call(this);
16974         this.boxReady = true;
16975         this.setSize(this.width, this.height);
16976         if(this.x || this.y){
16977             this.setPosition(this.x, this.y);
16978         }
16979         if(this.pageX || this.pageY){
16980             this.setPagePosition(this.pageX, this.pageY);
16981         }
16982     },
16983
16984     /**
16985      * Force the component's size to recalculate based on the underlying element's current height and width.
16986      * @returns {Roo.BoxComponent} this
16987      */
16988     syncSize : function(){
16989         delete this.lastSize;
16990         this.setSize(this.el.getWidth(), this.el.getHeight());
16991         return this;
16992     },
16993
16994     /**
16995      * Called after the component is resized, this method is empty by default but can be implemented by any
16996      * subclass that needs to perform custom logic after a resize occurs.
16997      * @param {Number} adjWidth The box-adjusted width that was set
16998      * @param {Number} adjHeight The box-adjusted height that was set
16999      * @param {Number} rawWidth The width that was originally specified
17000      * @param {Number} rawHeight The height that was originally specified
17001      */
17002     onResize : function(adjWidth, adjHeight, rawWidth, rawHeight){
17003
17004     },
17005
17006     /**
17007      * Called after the component is moved, this method is empty by default but can be implemented by any
17008      * subclass that needs to perform custom logic after a move occurs.
17009      * @param {Number} x The new x position
17010      * @param {Number} y The new y position
17011      */
17012     onPosition : function(x, y){
17013
17014     },
17015
17016     // private
17017     adjustSize : function(w, h){
17018         if(this.autoWidth){
17019             w = 'auto';
17020         }
17021         if(this.autoHeight){
17022             h = 'auto';
17023         }
17024         return {width : w, height: h};
17025     },
17026
17027     // private
17028     adjustPosition : function(x, y){
17029         return {x : x, y: y};
17030     }
17031 });/*
17032  * Based on:
17033  * Ext JS Library 1.1.1
17034  * Copyright(c) 2006-2007, Ext JS, LLC.
17035  *
17036  * Originally Released Under LGPL - original licence link has changed is not relivant.
17037  *
17038  * Fork - LGPL
17039  * <script type="text/javascript">
17040  */
17041  (function(){ 
17042 /**
17043  * @class Roo.Layer
17044  * @extends Roo.Element
17045  * An extended {@link Roo.Element} object that supports a shadow and shim, constrain to viewport and
17046  * automatic maintaining of shadow/shim positions.
17047  * @cfg {Boolean} shim False to disable the iframe shim in browsers which need one (defaults to true)
17048  * @cfg {String/Boolean} shadow True to create a shadow element with default class "x-layer-shadow", or
17049  * you can pass a string with a CSS class name. False turns off the shadow.
17050  * @cfg {Object} dh DomHelper object config to create element with (defaults to {tag: "div", cls: "x-layer"}).
17051  * @cfg {Boolean} constrain False to disable constrain to viewport (defaults to true)
17052  * @cfg {String} cls CSS class to add to the element
17053  * @cfg {Number} zindex Starting z-index (defaults to 11000)
17054  * @cfg {Number} shadowOffset Number of pixels to offset the shadow (defaults to 3)
17055  * @constructor
17056  * @param {Object} config An object with config options.
17057  * @param {String/HTMLElement} existingEl (optional) Uses an existing DOM element. If the element is not found it creates it.
17058  */
17059
17060 Roo.Layer = function(config, existingEl){
17061     config = config || {};
17062     var dh = Roo.DomHelper;
17063     var cp = config.parentEl, pel = cp ? Roo.getDom(cp) : document.body;
17064     if(existingEl){
17065         this.dom = Roo.getDom(existingEl);
17066     }
17067     if(!this.dom){
17068         var o = config.dh || {tag: "div", cls: "x-layer"};
17069         this.dom = dh.append(pel, o);
17070     }
17071     if(config.cls){
17072         this.addClass(config.cls);
17073     }
17074     this.constrain = config.constrain !== false;
17075     this.visibilityMode = Roo.Element.VISIBILITY;
17076     if(config.id){
17077         this.id = this.dom.id = config.id;
17078     }else{
17079         this.id = Roo.id(this.dom);
17080     }
17081     this.zindex = config.zindex || this.getZIndex();
17082     this.position("absolute", this.zindex);
17083     if(config.shadow){
17084         this.shadowOffset = config.shadowOffset || 4;
17085         this.shadow = new Roo.Shadow({
17086             offset : this.shadowOffset,
17087             mode : config.shadow
17088         });
17089     }else{
17090         this.shadowOffset = 0;
17091     }
17092     this.useShim = config.shim !== false && Roo.useShims;
17093     this.useDisplay = config.useDisplay;
17094     this.hide();
17095 };
17096
17097 var supr = Roo.Element.prototype;
17098
17099 // shims are shared among layer to keep from having 100 iframes
17100 var shims = [];
17101
17102 Roo.extend(Roo.Layer, Roo.Element, {
17103
17104     getZIndex : function(){
17105         return this.zindex || parseInt(this.getStyle("z-index"), 10) || 11000;
17106     },
17107
17108     getShim : function(){
17109         if(!this.useShim){
17110             return null;
17111         }
17112         if(this.shim){
17113             return this.shim;
17114         }
17115         var shim = shims.shift();
17116         if(!shim){
17117             shim = this.createShim();
17118             shim.enableDisplayMode('block');
17119             shim.dom.style.display = 'none';
17120             shim.dom.style.visibility = 'visible';
17121         }
17122         var pn = this.dom.parentNode;
17123         if(shim.dom.parentNode != pn){
17124             pn.insertBefore(shim.dom, this.dom);
17125         }
17126         shim.setStyle('z-index', this.getZIndex()-2);
17127         this.shim = shim;
17128         return shim;
17129     },
17130
17131     hideShim : function(){
17132         if(this.shim){
17133             this.shim.setDisplayed(false);
17134             shims.push(this.shim);
17135             delete this.shim;
17136         }
17137     },
17138
17139     disableShadow : function(){
17140         if(this.shadow){
17141             this.shadowDisabled = true;
17142             this.shadow.hide();
17143             this.lastShadowOffset = this.shadowOffset;
17144             this.shadowOffset = 0;
17145         }
17146     },
17147
17148     enableShadow : function(show){
17149         if(this.shadow){
17150             this.shadowDisabled = false;
17151             this.shadowOffset = this.lastShadowOffset;
17152             delete this.lastShadowOffset;
17153             if(show){
17154                 this.sync(true);
17155             }
17156         }
17157     },
17158
17159     // private
17160     // this code can execute repeatedly in milliseconds (i.e. during a drag) so
17161     // code size was sacrificed for effeciency (e.g. no getBox/setBox, no XY calls)
17162     sync : function(doShow){
17163         var sw = this.shadow;
17164         if(!this.updating && this.isVisible() && (sw || this.useShim)){
17165             var sh = this.getShim();
17166
17167             var w = this.getWidth(),
17168                 h = this.getHeight();
17169
17170             var l = this.getLeft(true),
17171                 t = this.getTop(true);
17172
17173             if(sw && !this.shadowDisabled){
17174                 if(doShow && !sw.isVisible()){
17175                     sw.show(this);
17176                 }else{
17177                     sw.realign(l, t, w, h);
17178                 }
17179                 if(sh){
17180                     if(doShow){
17181                        sh.show();
17182                     }
17183                     // fit the shim behind the shadow, so it is shimmed too
17184                     var a = sw.adjusts, s = sh.dom.style;
17185                     s.left = (Math.min(l, l+a.l))+"px";
17186                     s.top = (Math.min(t, t+a.t))+"px";
17187                     s.width = (w+a.w)+"px";
17188                     s.height = (h+a.h)+"px";
17189                 }
17190             }else if(sh){
17191                 if(doShow){
17192                    sh.show();
17193                 }
17194                 sh.setSize(w, h);
17195                 sh.setLeftTop(l, t);
17196             }
17197             
17198         }
17199     },
17200
17201     // private
17202     destroy : function(){
17203         this.hideShim();
17204         if(this.shadow){
17205             this.shadow.hide();
17206         }
17207         this.removeAllListeners();
17208         var pn = this.dom.parentNode;
17209         if(pn){
17210             pn.removeChild(this.dom);
17211         }
17212         Roo.Element.uncache(this.id);
17213     },
17214
17215     remove : function(){
17216         this.destroy();
17217     },
17218
17219     // private
17220     beginUpdate : function(){
17221         this.updating = true;
17222     },
17223
17224     // private
17225     endUpdate : function(){
17226         this.updating = false;
17227         this.sync(true);
17228     },
17229
17230     // private
17231     hideUnders : function(negOffset){
17232         if(this.shadow){
17233             this.shadow.hide();
17234         }
17235         this.hideShim();
17236     },
17237
17238     // private
17239     constrainXY : function(){
17240         if(this.constrain){
17241             var vw = Roo.lib.Dom.getViewWidth(),
17242                 vh = Roo.lib.Dom.getViewHeight();
17243             var s = Roo.get(document).getScroll();
17244
17245             var xy = this.getXY();
17246             var x = xy[0], y = xy[1];   
17247             var w = this.dom.offsetWidth+this.shadowOffset, h = this.dom.offsetHeight+this.shadowOffset;
17248             // only move it if it needs it
17249             var moved = false;
17250             // first validate right/bottom
17251             if((x + w) > vw+s.left){
17252                 x = vw - w - this.shadowOffset;
17253                 moved = true;
17254             }
17255             if((y + h) > vh+s.top){
17256                 y = vh - h - this.shadowOffset;
17257                 moved = true;
17258             }
17259             // then make sure top/left isn't negative
17260             if(x < s.left){
17261                 x = s.left;
17262                 moved = true;
17263             }
17264             if(y < s.top){
17265                 y = s.top;
17266                 moved = true;
17267             }
17268             if(moved){
17269                 if(this.avoidY){
17270                     var ay = this.avoidY;
17271                     if(y <= ay && (y+h) >= ay){
17272                         y = ay-h-5;   
17273                     }
17274                 }
17275                 xy = [x, y];
17276                 this.storeXY(xy);
17277                 supr.setXY.call(this, xy);
17278                 this.sync();
17279             }
17280         }
17281     },
17282
17283     isVisible : function(){
17284         return this.visible;    
17285     },
17286
17287     // private
17288     showAction : function(){
17289         this.visible = true; // track visibility to prevent getStyle calls
17290         if(this.useDisplay === true){
17291             this.setDisplayed("");
17292         }else if(this.lastXY){
17293             supr.setXY.call(this, this.lastXY);
17294         }else if(this.lastLT){
17295             supr.setLeftTop.call(this, this.lastLT[0], this.lastLT[1]);
17296         }
17297     },
17298
17299     // private
17300     hideAction : function(){
17301         this.visible = false;
17302         if(this.useDisplay === true){
17303             this.setDisplayed(false);
17304         }else{
17305             this.setLeftTop(-10000,-10000);
17306         }
17307     },
17308
17309     // overridden Element method
17310     setVisible : function(v, a, d, c, e){
17311         if(v){
17312             this.showAction();
17313         }
17314         if(a && v){
17315             var cb = function(){
17316                 this.sync(true);
17317                 if(c){
17318                     c();
17319                 }
17320             }.createDelegate(this);
17321             supr.setVisible.call(this, true, true, d, cb, e);
17322         }else{
17323             if(!v){
17324                 this.hideUnders(true);
17325             }
17326             var cb = c;
17327             if(a){
17328                 cb = function(){
17329                     this.hideAction();
17330                     if(c){
17331                         c();
17332                     }
17333                 }.createDelegate(this);
17334             }
17335             supr.setVisible.call(this, v, a, d, cb, e);
17336             if(v){
17337                 this.sync(true);
17338             }else if(!a){
17339                 this.hideAction();
17340             }
17341         }
17342     },
17343
17344     storeXY : function(xy){
17345         delete this.lastLT;
17346         this.lastXY = xy;
17347     },
17348
17349     storeLeftTop : function(left, top){
17350         delete this.lastXY;
17351         this.lastLT = [left, top];
17352     },
17353
17354     // private
17355     beforeFx : function(){
17356         this.beforeAction();
17357         return Roo.Layer.superclass.beforeFx.apply(this, arguments);
17358     },
17359
17360     // private
17361     afterFx : function(){
17362         Roo.Layer.superclass.afterFx.apply(this, arguments);
17363         this.sync(this.isVisible());
17364     },
17365
17366     // private
17367     beforeAction : function(){
17368         if(!this.updating && this.shadow){
17369             this.shadow.hide();
17370         }
17371     },
17372
17373     // overridden Element method
17374     setLeft : function(left){
17375         this.storeLeftTop(left, this.getTop(true));
17376         supr.setLeft.apply(this, arguments);
17377         this.sync();
17378     },
17379
17380     setTop : function(top){
17381         this.storeLeftTop(this.getLeft(true), top);
17382         supr.setTop.apply(this, arguments);
17383         this.sync();
17384     },
17385
17386     setLeftTop : function(left, top){
17387         this.storeLeftTop(left, top);
17388         supr.setLeftTop.apply(this, arguments);
17389         this.sync();
17390     },
17391
17392     setXY : function(xy, a, d, c, e){
17393         this.fixDisplay();
17394         this.beforeAction();
17395         this.storeXY(xy);
17396         var cb = this.createCB(c);
17397         supr.setXY.call(this, xy, a, d, cb, e);
17398         if(!a){
17399             cb();
17400         }
17401     },
17402
17403     // private
17404     createCB : function(c){
17405         var el = this;
17406         return function(){
17407             el.constrainXY();
17408             el.sync(true);
17409             if(c){
17410                 c();
17411             }
17412         };
17413     },
17414
17415     // overridden Element method
17416     setX : function(x, a, d, c, e){
17417         this.setXY([x, this.getY()], a, d, c, e);
17418     },
17419
17420     // overridden Element method
17421     setY : function(y, a, d, c, e){
17422         this.setXY([this.getX(), y], a, d, c, e);
17423     },
17424
17425     // overridden Element method
17426     setSize : function(w, h, a, d, c, e){
17427         this.beforeAction();
17428         var cb = this.createCB(c);
17429         supr.setSize.call(this, w, h, a, d, cb, e);
17430         if(!a){
17431             cb();
17432         }
17433     },
17434
17435     // overridden Element method
17436     setWidth : function(w, a, d, c, e){
17437         this.beforeAction();
17438         var cb = this.createCB(c);
17439         supr.setWidth.call(this, w, a, d, cb, e);
17440         if(!a){
17441             cb();
17442         }
17443     },
17444
17445     // overridden Element method
17446     setHeight : function(h, a, d, c, e){
17447         this.beforeAction();
17448         var cb = this.createCB(c);
17449         supr.setHeight.call(this, h, a, d, cb, e);
17450         if(!a){
17451             cb();
17452         }
17453     },
17454
17455     // overridden Element method
17456     setBounds : function(x, y, w, h, a, d, c, e){
17457         this.beforeAction();
17458         var cb = this.createCB(c);
17459         if(!a){
17460             this.storeXY([x, y]);
17461             supr.setXY.call(this, [x, y]);
17462             supr.setSize.call(this, w, h, a, d, cb, e);
17463             cb();
17464         }else{
17465             supr.setBounds.call(this, x, y, w, h, a, d, cb, e);
17466         }
17467         return this;
17468     },
17469     
17470     /**
17471      * Sets the z-index of this layer and adjusts any shadow and shim z-indexes. The layer z-index is automatically
17472      * incremented by two more than the value passed in so that it always shows above any shadow or shim (the shadow
17473      * element, if any, will be assigned z-index + 1, and the shim element, if any, will be assigned the unmodified z-index).
17474      * @param {Number} zindex The new z-index to set
17475      * @return {this} The Layer
17476      */
17477     setZIndex : function(zindex){
17478         this.zindex = zindex;
17479         this.setStyle("z-index", zindex + 2);
17480         if(this.shadow){
17481             this.shadow.setZIndex(zindex + 1);
17482         }
17483         if(this.shim){
17484             this.shim.setStyle("z-index", zindex);
17485         }
17486     }
17487 });
17488 })();/*
17489  * Original code for Roojs - LGPL
17490  * <script type="text/javascript">
17491  */
17492  
17493 /**
17494  * @class Roo.XComponent
17495  * A delayed Element creator...
17496  * Or a way to group chunks of interface together.
17497  * technically this is a wrapper around a tree of Roo elements (which defines a 'module'),
17498  *  used in conjunction with XComponent.build() it will create an instance of each element,
17499  *  then call addxtype() to build the User interface.
17500  * 
17501  * Mypart.xyx = new Roo.XComponent({
17502
17503     parent : 'Mypart.xyz', // empty == document.element.!!
17504     order : '001',
17505     name : 'xxxx'
17506     region : 'xxxx'
17507     disabled : function() {} 
17508      
17509     tree : function() { // return an tree of xtype declared components
17510         var MODULE = this;
17511         return 
17512         {
17513             xtype : 'NestedLayoutPanel',
17514             // technicall
17515         }
17516      ]
17517  *})
17518  *
17519  *
17520  * It can be used to build a big heiracy, with parent etc.
17521  * or you can just use this to render a single compoent to a dom element
17522  * MYPART.render(Roo.Element | String(id) | dom_element )
17523  *
17524  *
17525  * Usage patterns.
17526  *
17527  * Classic Roo
17528  *
17529  * Roo is designed primarily as a single page application, so the UI build for a standard interface will
17530  * expect a single 'TOP' level module normally indicated by the 'parent' of the XComponent definition being defined as false.
17531  *
17532  * Each sub module is expected to have a parent pointing to the class name of it's parent module.
17533  *
17534  * When the top level is false, a 'Roo.BorderLayout' is created and the element is flagged as 'topModule'
17535  * - if mulitple topModules exist, the last one is defined as the top module.
17536  *
17537  * Embeded Roo
17538  * 
17539  * When the top level or multiple modules are to embedded into a existing HTML page,
17540  * the parent element can container '#id' of the element where the module will be drawn.
17541  *
17542  * Bootstrap Roo
17543  *
17544  * Unlike classic Roo, the bootstrap tends not to be used as a single page.
17545  * it relies more on a include mechanism, where sub modules are included into an outer page.
17546  * This is normally managed by the builder tools using Roo.apply( options, Included.Sub.Module )
17547  * 
17548  * Bootstrap Roo Included elements
17549  *
17550  * Our builder application needs the ability to preview these sub compoennts. They will normally have parent=false set,
17551  * hence confusing the component builder as it thinks there are multiple top level elements. 
17552  *
17553  * String Over-ride & Translations
17554  *
17555  * Our builder application writes all the strings as _strings and _named_strings. This is to enable the translation of elements,
17556  * and also the 'overlaying of string values - needed when different versions of the same application with different text content
17557  * are needed. @see Roo.XComponent.overlayString  
17558  * 
17559  * 
17560  * 
17561  * @extends Roo.util.Observable
17562  * @constructor
17563  * @param cfg {Object} configuration of component
17564  * 
17565  */
17566 Roo.XComponent = function(cfg) {
17567     Roo.apply(this, cfg);
17568     this.addEvents({ 
17569         /**
17570              * @event built
17571              * Fires when this the componnt is built
17572              * @param {Roo.XComponent} c the component
17573              */
17574         'built' : true
17575         
17576     });
17577     this.region = this.region || 'center'; // default..
17578     Roo.XComponent.register(this);
17579     this.modules = false;
17580     this.el = false; // where the layout goes..
17581     
17582     
17583 }
17584 Roo.extend(Roo.XComponent, Roo.util.Observable, {
17585     /**
17586      * @property el
17587      * The created element (with Roo.factory())
17588      * @type {Roo.Layout}
17589      */
17590     el  : false,
17591     
17592     /**
17593      * @property el
17594      * for BC  - use el in new code
17595      * @type {Roo.Layout}
17596      */
17597     panel : false,
17598     
17599     /**
17600      * @property layout
17601      * for BC  - use el in new code
17602      * @type {Roo.Layout}
17603      */
17604     layout : false,
17605     
17606      /**
17607      * @cfg {Function|boolean} disabled
17608      * If this module is disabled by some rule, return true from the funtion
17609      */
17610     disabled : false,
17611     
17612     /**
17613      * @cfg {String} parent 
17614      * Name of parent element which it get xtype added to..
17615      */
17616     parent: false,
17617     
17618     /**
17619      * @cfg {String} order
17620      * Used to set the order in which elements are created (usefull for multiple tabs)
17621      */
17622     
17623     order : false,
17624     /**
17625      * @cfg {String} name
17626      * String to display while loading.
17627      */
17628     name : false,
17629     /**
17630      * @cfg {String} region
17631      * Region to render component to (defaults to center)
17632      */
17633     region : 'center',
17634     
17635     /**
17636      * @cfg {Array} items
17637      * A single item array - the first element is the root of the tree..
17638      * It's done this way to stay compatible with the Xtype system...
17639      */
17640     items : false,
17641     
17642     /**
17643      * @property _tree
17644      * The method that retuns the tree of parts that make up this compoennt 
17645      * @type {function}
17646      */
17647     _tree  : false,
17648     
17649      /**
17650      * render
17651      * render element to dom or tree
17652      * @param {Roo.Element|String|DomElement} optional render to if parent is not set.
17653      */
17654     
17655     render : function(el)
17656     {
17657         
17658         el = el || false;
17659         var hp = this.parent ? 1 : 0;
17660         Roo.debug &&  Roo.log(this);
17661         
17662         var tree = this._tree ? this._tree() : this.tree();
17663
17664         
17665         if (!el && typeof(this.parent) == 'string' && this.parent.substring(0,1) == '#') {
17666             // if parent is a '#.....' string, then let's use that..
17667             var ename = this.parent.substr(1);
17668             this.parent = false;
17669             Roo.debug && Roo.log(ename);
17670             switch (ename) {
17671                 case 'bootstrap-body':
17672                     if (typeof(tree.el) != 'undefined' && tree.el == document.body)  {
17673                         // this is the BorderLayout standard?
17674                        this.parent = { el : true };
17675                        break;
17676                     }
17677                     if (["Nest", "Content", "Grid", "Tree"].indexOf(tree.xtype)  > -1)  {
17678                         // need to insert stuff...
17679                         this.parent =  {
17680                              el : new Roo.bootstrap.layout.Border({
17681                                  el : document.body, 
17682                      
17683                                  center: {
17684                                     titlebar: false,
17685                                     autoScroll:false,
17686                                     closeOnTab: true,
17687                                     tabPosition: 'top',
17688                                       //resizeTabs: true,
17689                                     alwaysShowTabs: true,
17690                                     hideTabs: false
17691                                      //minTabWidth: 140
17692                                  }
17693                              })
17694                         
17695                          };
17696                          break;
17697                     }
17698                          
17699                     if (typeof(Roo.bootstrap.Body) != 'undefined' ) {
17700                         this.parent = { el :  new  Roo.bootstrap.Body() };
17701                         Roo.debug && Roo.log("setting el to doc body");
17702                          
17703                     } else {
17704                         throw "Container is bootstrap body, but Roo.bootstrap.Body is not defined";
17705                     }
17706                     break;
17707                 case 'bootstrap':
17708                     this.parent = { el : true};
17709                     // fall through
17710                 default:
17711                     el = Roo.get(ename);
17712                     if (typeof(Roo.bootstrap) != 'undefined' && tree['|xns'] == 'Roo.bootstrap') {
17713                         this.parent = { el : true};
17714                     }
17715                     
17716                     break;
17717             }
17718                 
17719             
17720             if (!el && !this.parent) {
17721                 Roo.debug && Roo.log("Warning - element can not be found :#" + ename );
17722                 return;
17723             }
17724         }
17725         
17726         Roo.debug && Roo.log("EL:");
17727         Roo.debug && Roo.log(el);
17728         Roo.debug && Roo.log("this.parent.el:");
17729         Roo.debug && Roo.log(this.parent.el);
17730         
17731
17732         // altertive root elements ??? - we need a better way to indicate these.
17733         var is_alt = Roo.XComponent.is_alt ||
17734                     (typeof(tree.el) != 'undefined' && tree.el == document.body) ||
17735                     (typeof(Roo.bootstrap) != 'undefined' && tree.xns == Roo.bootstrap) ||
17736                     (typeof(Roo.mailer) != 'undefined' && tree.xns == Roo.mailer) ;
17737         
17738         
17739         
17740         if (!this.parent && is_alt) {
17741             //el = Roo.get(document.body);
17742             this.parent = { el : true };
17743         }
17744             
17745             
17746         
17747         if (!this.parent) {
17748             
17749             Roo.debug && Roo.log("no parent - creating one");
17750             
17751             el = el ? Roo.get(el) : false;      
17752             
17753             if (typeof(Roo.BorderLayout) == 'undefined' ) {
17754                 
17755                 this.parent =  {
17756                     el : new Roo.bootstrap.layout.Border({
17757                         el: el || document.body,
17758                     
17759                         center: {
17760                             titlebar: false,
17761                             autoScroll:false,
17762                             closeOnTab: true,
17763                             tabPosition: 'top',
17764                              //resizeTabs: true,
17765                             alwaysShowTabs: false,
17766                             hideTabs: true,
17767                             minTabWidth: 140,
17768                             overflow: 'visible'
17769                          }
17770                      })
17771                 };
17772             } else {
17773             
17774                 // it's a top level one..
17775                 this.parent =  {
17776                     el : new Roo.BorderLayout(el || document.body, {
17777                         center: {
17778                             titlebar: false,
17779                             autoScroll:false,
17780                             closeOnTab: true,
17781                             tabPosition: 'top',
17782                              //resizeTabs: true,
17783                             alwaysShowTabs: el && hp? false :  true,
17784                             hideTabs: el || !hp ? true :  false,
17785                             minTabWidth: 140
17786                          }
17787                     })
17788                 };
17789             }
17790         }
17791         
17792         if (!this.parent.el) {
17793                 // probably an old style ctor, which has been disabled.
17794                 return;
17795
17796         }
17797                 // The 'tree' method is  '_tree now' 
17798             
17799         tree.region = tree.region || this.region;
17800         var is_body = false;
17801         if (this.parent.el === true) {
17802             // bootstrap... - body..
17803             if (el) {
17804                 tree.el = el;
17805             }
17806             this.parent.el = Roo.factory(tree);
17807             is_body = true;
17808         }
17809         
17810         this.el = this.parent.el.addxtype(tree, undefined, is_body);
17811         this.fireEvent('built', this);
17812         
17813         this.panel = this.el;
17814         this.layout = this.panel.layout;
17815         this.parentLayout = this.parent.layout  || false;  
17816          
17817     }
17818     
17819 });
17820
17821 Roo.apply(Roo.XComponent, {
17822     /**
17823      * @property  hideProgress
17824      * true to disable the building progress bar.. usefull on single page renders.
17825      * @type Boolean
17826      */
17827     hideProgress : false,
17828     /**
17829      * @property  buildCompleted
17830      * True when the builder has completed building the interface.
17831      * @type Boolean
17832      */
17833     buildCompleted : false,
17834      
17835     /**
17836      * @property  topModule
17837      * the upper most module - uses document.element as it's constructor.
17838      * @type Object
17839      */
17840      
17841     topModule  : false,
17842       
17843     /**
17844      * @property  modules
17845      * array of modules to be created by registration system.
17846      * @type {Array} of Roo.XComponent
17847      */
17848     
17849     modules : [],
17850     /**
17851      * @property  elmodules
17852      * array of modules to be created by which use #ID 
17853      * @type {Array} of Roo.XComponent
17854      */
17855      
17856     elmodules : [],
17857
17858      /**
17859      * @property  is_alt
17860      * Is an alternative Root - normally used by bootstrap or other systems,
17861      *    where the top element in the tree can wrap 'body' 
17862      * @type {boolean}  (default false)
17863      */
17864      
17865     is_alt : false,
17866     /**
17867      * @property  build_from_html
17868      * Build elements from html - used by bootstrap HTML stuff 
17869      *    - this is cleared after build is completed
17870      * @type {boolean}    (default false)
17871      */
17872      
17873     build_from_html : false,
17874     /**
17875      * Register components to be built later.
17876      *
17877      * This solves the following issues
17878      * - Building is not done on page load, but after an authentication process has occured.
17879      * - Interface elements are registered on page load
17880      * - Parent Interface elements may not be loaded before child, so this handles that..
17881      * 
17882      *
17883      * example:
17884      * 
17885      * MyApp.register({
17886           order : '000001',
17887           module : 'Pman.Tab.projectMgr',
17888           region : 'center',
17889           parent : 'Pman.layout',
17890           disabled : false,  // or use a function..
17891         })
17892      
17893      * * @param {Object} details about module
17894      */
17895     register : function(obj) {
17896                 
17897         Roo.XComponent.event.fireEvent('register', obj);
17898         switch(typeof(obj.disabled) ) {
17899                 
17900             case 'undefined':
17901                 break;
17902             
17903             case 'function':
17904                 if ( obj.disabled() ) {
17905                         return;
17906                 }
17907                 break;
17908             
17909             default:
17910                 if (obj.disabled || obj.region == '#disabled') {
17911                         return;
17912                 }
17913                 break;
17914         }
17915                 
17916         this.modules.push(obj);
17917          
17918     },
17919     /**
17920      * convert a string to an object..
17921      * eg. 'AAA.BBB' -> finds AAA.BBB
17922
17923      */
17924     
17925     toObject : function(str)
17926     {
17927         if (!str || typeof(str) == 'object') {
17928             return str;
17929         }
17930         if (str.substring(0,1) == '#') {
17931             return str;
17932         }
17933
17934         var ar = str.split('.');
17935         var rt, o;
17936         rt = ar.shift();
17937             /** eval:var:o */
17938         try {
17939             eval('if (typeof ' + rt + ' == "undefined"){ o = false;} o = ' + rt + ';');
17940         } catch (e) {
17941             throw "Module not found : " + str;
17942         }
17943         
17944         if (o === false) {
17945             throw "Module not found : " + str;
17946         }
17947         Roo.each(ar, function(e) {
17948             if (typeof(o[e]) == 'undefined') {
17949                 throw "Module not found : " + str;
17950             }
17951             o = o[e];
17952         });
17953         
17954         return o;
17955         
17956     },
17957     
17958     
17959     /**
17960      * move modules into their correct place in the tree..
17961      * 
17962      */
17963     preBuild : function ()
17964     {
17965         var _t = this;
17966         Roo.each(this.modules , function (obj)
17967         {
17968             Roo.XComponent.event.fireEvent('beforebuild', obj);
17969             
17970             var opar = obj.parent;
17971             try { 
17972                 obj.parent = this.toObject(opar);
17973             } catch(e) {
17974                 Roo.debug && Roo.log("parent:toObject failed: " + e.toString());
17975                 return;
17976             }
17977             
17978             if (!obj.parent) {
17979                 Roo.debug && Roo.log("GOT top level module");
17980                 Roo.debug && Roo.log(obj);
17981                 obj.modules = new Roo.util.MixedCollection(false, 
17982                     function(o) { return o.order + '' }
17983                 );
17984                 this.topModule = obj;
17985                 return;
17986             }
17987                         // parent is a string (usually a dom element name..)
17988             if (typeof(obj.parent) == 'string') {
17989                 this.elmodules.push(obj);
17990                 return;
17991             }
17992             if (obj.parent.constructor != Roo.XComponent) {
17993                 Roo.debug && Roo.log("Warning : Object Parent is not instance of XComponent:" + obj.name)
17994             }
17995             if (!obj.parent.modules) {
17996                 obj.parent.modules = new Roo.util.MixedCollection(false, 
17997                     function(o) { return o.order + '' }
17998                 );
17999             }
18000             if (obj.parent.disabled) {
18001                 obj.disabled = true;
18002             }
18003             obj.parent.modules.add(obj);
18004         }, this);
18005     },
18006     
18007      /**
18008      * make a list of modules to build.
18009      * @return {Array} list of modules. 
18010      */ 
18011     
18012     buildOrder : function()
18013     {
18014         var _this = this;
18015         var cmp = function(a,b) {   
18016             return String(a).toUpperCase() > String(b).toUpperCase() ? 1 : -1;
18017         };
18018         if ((!this.topModule || !this.topModule.modules) && !this.elmodules.length) {
18019             throw "No top level modules to build";
18020         }
18021         
18022         // make a flat list in order of modules to build.
18023         var mods = this.topModule ? [ this.topModule ] : [];
18024                 
18025         
18026         // elmodules (is a list of DOM based modules )
18027         Roo.each(this.elmodules, function(e) {
18028             mods.push(e);
18029             if (!this.topModule &&
18030                 typeof(e.parent) == 'string' &&
18031                 e.parent.substring(0,1) == '#' &&
18032                 Roo.get(e.parent.substr(1))
18033                ) {
18034                 
18035                 _this.topModule = e;
18036             }
18037             
18038         });
18039
18040         
18041         // add modules to their parents..
18042         var addMod = function(m) {
18043             Roo.debug && Roo.log("build Order: add: " + m.name);
18044                 
18045             mods.push(m);
18046             if (m.modules && !m.disabled) {
18047                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules");
18048                 m.modules.keySort('ASC',  cmp );
18049                 Roo.debug && Roo.log("build Order: " + m.modules.length + " child modules (after sort)");
18050     
18051                 m.modules.each(addMod);
18052             } else {
18053                 Roo.debug && Roo.log("build Order: no child modules");
18054             }
18055             // not sure if this is used any more..
18056             if (m.finalize) {
18057                 m.finalize.name = m.name + " (clean up) ";
18058                 mods.push(m.finalize);
18059             }
18060             
18061         }
18062         if (this.topModule && this.topModule.modules) { 
18063             this.topModule.modules.keySort('ASC',  cmp );
18064             this.topModule.modules.each(addMod);
18065         } 
18066         return mods;
18067     },
18068     
18069      /**
18070      * Build the registered modules.
18071      * @param {Object} parent element.
18072      * @param {Function} optional method to call after module has been added.
18073      * 
18074      */ 
18075    
18076     build : function(opts) 
18077     {
18078         
18079         if (typeof(opts) != 'undefined') {
18080             Roo.apply(this,opts);
18081         }
18082         
18083         this.preBuild();
18084         var mods = this.buildOrder();
18085       
18086         //this.allmods = mods;
18087         //Roo.debug && Roo.log(mods);
18088         //return;
18089         if (!mods.length) { // should not happen
18090             throw "NO modules!!!";
18091         }
18092         
18093         
18094         var msg = "Building Interface...";
18095         // flash it up as modal - so we store the mask!?
18096         if (!this.hideProgress && Roo.MessageBox) {
18097             Roo.MessageBox.show({ title: 'loading' });
18098             Roo.MessageBox.show({
18099                title: "Please wait...",
18100                msg: msg,
18101                width:450,
18102                progress:true,
18103                buttons : false,
18104                closable:false,
18105                modal: false
18106               
18107             });
18108         }
18109         var total = mods.length;
18110         
18111         var _this = this;
18112         var progressRun = function() {
18113             if (!mods.length) {
18114                 Roo.debug && Roo.log('hide?');
18115                 if (!this.hideProgress && Roo.MessageBox) {
18116                     Roo.MessageBox.hide();
18117                 }
18118                 Roo.XComponent.build_from_html = false; // reset, so dialogs will be build from javascript
18119                 
18120                 Roo.XComponent.event.fireEvent('buildcomplete', _this.topModule);
18121                 
18122                 // THE END...
18123                 return false;   
18124             }
18125             
18126             var m = mods.shift();
18127             
18128             
18129             Roo.debug && Roo.log(m);
18130             // not sure if this is supported any more.. - modules that are are just function
18131             if (typeof(m) == 'function') { 
18132                 m.call(this);
18133                 return progressRun.defer(10, _this);
18134             } 
18135             
18136             
18137             msg = "Building Interface " + (total  - mods.length) + 
18138                     " of " + total + 
18139                     (m.name ? (' - ' + m.name) : '');
18140                         Roo.debug && Roo.log(msg);
18141             if (!_this.hideProgress &&  Roo.MessageBox) { 
18142                 Roo.MessageBox.updateProgress(  (total  - mods.length)/total, msg  );
18143             }
18144             
18145          
18146             // is the module disabled?
18147             var disabled = (typeof(m.disabled) == 'function') ?
18148                 m.disabled.call(m.module.disabled) : m.disabled;    
18149             
18150             
18151             if (disabled) {
18152                 return progressRun(); // we do not update the display!
18153             }
18154             
18155             // now build 
18156             
18157                         
18158                         
18159             m.render();
18160             // it's 10 on top level, and 1 on others??? why...
18161             return progressRun.defer(10, _this);
18162              
18163         }
18164         progressRun.defer(1, _this);
18165      
18166         
18167         
18168     },
18169     /**
18170      * Overlay a set of modified strings onto a component
18171      * This is dependant on our builder exporting the strings and 'named strings' elements.
18172      * 
18173      * @param {Object} element to overlay on - eg. Pman.Dialog.Login
18174      * @param {Object} associative array of 'named' string and it's new value.
18175      * 
18176      */
18177         overlayStrings : function( component, strings )
18178     {
18179         if (typeof(component['_named_strings']) == 'undefined') {
18180             throw "ERROR: component does not have _named_strings";
18181         }
18182         for ( var k in strings ) {
18183             var md = typeof(component['_named_strings'][k]) == 'undefined' ? false : component['_named_strings'][k];
18184             if (md !== false) {
18185                 component['_strings'][md] = strings[k];
18186             } else {
18187                 Roo.log('could not find named string: ' + k + ' in');
18188                 Roo.log(component);
18189             }
18190             
18191         }
18192         
18193     },
18194     
18195         
18196         /**
18197          * Event Object.
18198          *
18199          *
18200          */
18201         event: false, 
18202     /**
18203          * wrapper for event.on - aliased later..  
18204          * Typically use to register a event handler for register:
18205          *
18206          * eg. Roo.XComponent.on('register', function(comp) { comp.disable = true } );
18207          *
18208          */
18209     on : false
18210    
18211     
18212     
18213 });
18214
18215 Roo.XComponent.event = new Roo.util.Observable({
18216                 events : { 
18217                         /**
18218                          * @event register
18219                          * Fires when an Component is registered,
18220                          * set the disable property on the Component to stop registration.
18221                          * @param {Roo.XComponent} c the component being registerd.
18222                          * 
18223                          */
18224                         'register' : true,
18225             /**
18226                          * @event beforebuild
18227                          * Fires before each Component is built
18228                          * can be used to apply permissions.
18229                          * @param {Roo.XComponent} c the component being registerd.
18230                          * 
18231                          */
18232                         'beforebuild' : true,
18233                         /**
18234                          * @event buildcomplete
18235                          * Fires on the top level element when all elements have been built
18236                          * @param {Roo.XComponent} the top level component.
18237                          */
18238                         'buildcomplete' : true
18239                         
18240                 }
18241 });
18242
18243 Roo.XComponent.on = Roo.XComponent.event.on.createDelegate(Roo.XComponent.event); 
18244  //
18245  /**
18246  * marked - a markdown parser
18247  * Copyright (c) 2011-2014, Christopher Jeffrey. (MIT Licensed)
18248  * https://github.com/chjj/marked
18249  */
18250
18251
18252 /**
18253  *
18254  * Roo.Markdown - is a very crude wrapper around marked..
18255  *
18256  * usage:
18257  * 
18258  * alert( Roo.Markdown.toHtml("Markdown *rocks*.") );
18259  * 
18260  * Note: move the sample code to the bottom of this
18261  * file before uncommenting it.
18262  *
18263  */
18264
18265 Roo.Markdown = {};
18266 Roo.Markdown.toHtml = function(text) {
18267     
18268     var c = new Roo.Markdown.marked.setOptions({
18269             renderer: new Roo.Markdown.marked.Renderer(),
18270             gfm: true,
18271             tables: true,
18272             breaks: false,
18273             pedantic: false,
18274             sanitize: false,
18275             smartLists: true,
18276             smartypants: false
18277           });
18278     // A FEW HACKS!!?
18279     
18280     text = text.replace(/\\\n/g,' ');
18281     return Roo.Markdown.marked(text);
18282 };
18283 //
18284 // converter
18285 //
18286 // Wraps all "globals" so that the only thing
18287 // exposed is makeHtml().
18288 //
18289 (function() {
18290     
18291      /**
18292          * eval:var:escape
18293          * eval:var:unescape
18294          * eval:var:replace
18295          */
18296       
18297     /**
18298      * Helpers
18299      */
18300     
18301     var escape = function (html, encode) {
18302       return html
18303         .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&amp;')
18304         .replace(/</g, '&lt;')
18305         .replace(/>/g, '&gt;')
18306         .replace(/"/g, '&quot;')
18307         .replace(/'/g, '&#39;');
18308     }
18309     
18310     var unescape = function (html) {
18311         // explicitly match decimal, hex, and named HTML entities 
18312       return html.replace(/&(#(?:\d+)|(?:#x[0-9A-Fa-f]+)|(?:\w+));?/g, function(_, n) {
18313         n = n.toLowerCase();
18314         if (n === 'colon') { return ':'; }
18315         if (n.charAt(0) === '#') {
18316           return n.charAt(1) === 'x'
18317             ? String.fromCharCode(parseInt(n.substring(2), 16))
18318             : String.fromCharCode(+n.substring(1));
18319         }
18320         return '';
18321       });
18322     }
18323     
18324     var replace = function (regex, opt) {
18325       regex = regex.source;
18326       opt = opt || '';
18327       return function self(name, val) {
18328         if (!name) { return new RegExp(regex, opt); }
18329         val = val.source || val;
18330         val = val.replace(/(^|[^\[])\^/g, '$1');
18331         regex = regex.replace(name, val);
18332         return self;
18333       };
18334     }
18335
18336
18337          /**
18338          * eval:var:noop
18339     */
18340     var noop = function () {}
18341     noop.exec = noop;
18342     
18343          /**
18344          * eval:var:merge
18345     */
18346     var merge = function (obj) {
18347       var i = 1
18348         , target
18349         , key;
18350     
18351       for (; i < arguments.length; i++) {
18352         target = arguments[i];
18353         for (key in target) {
18354           if (Object.prototype.hasOwnProperty.call(target, key)) {
18355             obj[key] = target[key];
18356           }
18357         }
18358       }
18359     
18360       return obj;
18361     }
18362     
18363     
18364     /**
18365      * Block-Level Grammar
18366      */
18367     
18368     
18369     
18370     
18371     var block = {
18372       newline: /^\n+/,
18373       code: /^( {4}[^\n]+\n*)+/,
18374       fences: noop,
18375       hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18376       heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
18377       nptable: noop,
18378       lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
18379       blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
18380       list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
18381       html: /^ *(?:comment *(?:\n|\s*$)|closed *(?:\n{2,}|\s*$)|closing *(?:\n{2,}|\s*$))/,
18382       def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
18383       table: noop,
18384       paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
18385       text: /^[^\n]+/
18386     };
18387     
18388     block.bullet = /(?:[*+-]|\d+\.)/;
18389     block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
18390     block.item = replace(block.item, 'gm')
18391       (/bull/g, block.bullet)
18392       ();
18393     
18394     block.list = replace(block.list)
18395       (/bull/g, block.bullet)
18396       ('hr', '\\n+(?=\\1?(?:[-*_] *){3,}(?:\\n+|$))')
18397       ('def', '\\n+(?=' + block.def.source + ')')
18398       ();
18399     
18400     block.blockquote = replace(block.blockquote)
18401       ('def', block.def)
18402       ();
18403     
18404     block._tag = '(?!(?:'
18405       + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
18406       + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
18407       + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|[^\\w\\s@]*@)\\b';
18408     
18409     block.html = replace(block.html)
18410       ('comment', /<!--[\s\S]*?-->/)
18411       ('closed', /<(tag)[\s\S]+?<\/\1>/)
18412       ('closing', /<tag(?:"[^"]*"|'[^']*'|[^'">])*?>/)
18413       (/tag/g, block._tag)
18414       ();
18415     
18416     block.paragraph = replace(block.paragraph)
18417       ('hr', block.hr)
18418       ('heading', block.heading)
18419       ('lheading', block.lheading)
18420       ('blockquote', block.blockquote)
18421       ('tag', '<' + block._tag)
18422       ('def', block.def)
18423       ();
18424     
18425     /**
18426      * Normal Block Grammar
18427      */
18428     
18429     block.normal = merge({}, block);
18430     
18431     /**
18432      * GFM Block Grammar
18433      */
18434     
18435     block.gfm = merge({}, block.normal, {
18436       fences: /^ *(`{3,}|~{3,})[ \.]*(\S+)? *\n([\s\S]*?)\s*\1 *(?:\n+|$)/,
18437       paragraph: /^/,
18438       heading: /^ *(#{1,6}) +([^\n]+?) *#* *(?:\n+|$)/
18439     });
18440     
18441     block.gfm.paragraph = replace(block.paragraph)
18442       ('(?!', '(?!'
18443         + block.gfm.fences.source.replace('\\1', '\\2') + '|'
18444         + block.list.source.replace('\\1', '\\3') + '|')
18445       ();
18446     
18447     /**
18448      * GFM + Tables Block Grammar
18449      */
18450     
18451     block.tables = merge({}, block.gfm, {
18452       nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
18453       table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
18454     });
18455     
18456     /**
18457      * Block Lexer
18458      */
18459     
18460     var Lexer = function (options) {
18461       this.tokens = [];
18462       this.tokens.links = {};
18463       this.options = options || marked.defaults;
18464       this.rules = block.normal;
18465     
18466       if (this.options.gfm) {
18467         if (this.options.tables) {
18468           this.rules = block.tables;
18469         } else {
18470           this.rules = block.gfm;
18471         }
18472       }
18473     }
18474     
18475     /**
18476      * Expose Block Rules
18477      */
18478     
18479     Lexer.rules = block;
18480     
18481     /**
18482      * Static Lex Method
18483      */
18484     
18485     Lexer.lex = function(src, options) {
18486       var lexer = new Lexer(options);
18487       return lexer.lex(src);
18488     };
18489     
18490     /**
18491      * Preprocessing
18492      */
18493     
18494     Lexer.prototype.lex = function(src) {
18495       src = src
18496         .replace(/\r\n|\r/g, '\n')
18497         .replace(/\t/g, '    ')
18498         .replace(/\u00a0/g, ' ')
18499         .replace(/\u2424/g, '\n');
18500     
18501       return this.token(src, true);
18502     };
18503     
18504     /**
18505      * Lexing
18506      */
18507     
18508     Lexer.prototype.token = function(src, top, bq) {
18509       var src = src.replace(/^ +$/gm, '')
18510         , next
18511         , loose
18512         , cap
18513         , bull
18514         , b
18515         , item
18516         , space
18517         , i
18518         , l;
18519     
18520       while (src) {
18521         // newline
18522         if (cap = this.rules.newline.exec(src)) {
18523           src = src.substring(cap[0].length);
18524           if (cap[0].length > 1) {
18525             this.tokens.push({
18526               type: 'space'
18527             });
18528           }
18529         }
18530     
18531         // code
18532         if (cap = this.rules.code.exec(src)) {
18533           src = src.substring(cap[0].length);
18534           cap = cap[0].replace(/^ {4}/gm, '');
18535           this.tokens.push({
18536             type: 'code',
18537             text: !this.options.pedantic
18538               ? cap.replace(/\n+$/, '')
18539               : cap
18540           });
18541           continue;
18542         }
18543     
18544         // fences (gfm)
18545         if (cap = this.rules.fences.exec(src)) {
18546           src = src.substring(cap[0].length);
18547           this.tokens.push({
18548             type: 'code',
18549             lang: cap[2],
18550             text: cap[3] || ''
18551           });
18552           continue;
18553         }
18554     
18555         // heading
18556         if (cap = this.rules.heading.exec(src)) {
18557           src = src.substring(cap[0].length);
18558           this.tokens.push({
18559             type: 'heading',
18560             depth: cap[1].length,
18561             text: cap[2]
18562           });
18563           continue;
18564         }
18565     
18566         // table no leading pipe (gfm)
18567         if (top && (cap = this.rules.nptable.exec(src))) {
18568           src = src.substring(cap[0].length);
18569     
18570           item = {
18571             type: 'table',
18572             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18573             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18574             cells: cap[3].replace(/\n$/, '').split('\n')
18575           };
18576     
18577           for (i = 0; i < item.align.length; i++) {
18578             if (/^ *-+: *$/.test(item.align[i])) {
18579               item.align[i] = 'right';
18580             } else if (/^ *:-+: *$/.test(item.align[i])) {
18581               item.align[i] = 'center';
18582             } else if (/^ *:-+ *$/.test(item.align[i])) {
18583               item.align[i] = 'left';
18584             } else {
18585               item.align[i] = null;
18586             }
18587           }
18588     
18589           for (i = 0; i < item.cells.length; i++) {
18590             item.cells[i] = item.cells[i].split(/ *\| */);
18591           }
18592     
18593           this.tokens.push(item);
18594     
18595           continue;
18596         }
18597     
18598         // lheading
18599         if (cap = this.rules.lheading.exec(src)) {
18600           src = src.substring(cap[0].length);
18601           this.tokens.push({
18602             type: 'heading',
18603             depth: cap[2] === '=' ? 1 : 2,
18604             text: cap[1]
18605           });
18606           continue;
18607         }
18608     
18609         // hr
18610         if (cap = this.rules.hr.exec(src)) {
18611           src = src.substring(cap[0].length);
18612           this.tokens.push({
18613             type: 'hr'
18614           });
18615           continue;
18616         }
18617     
18618         // blockquote
18619         if (cap = this.rules.blockquote.exec(src)) {
18620           src = src.substring(cap[0].length);
18621     
18622           this.tokens.push({
18623             type: 'blockquote_start'
18624           });
18625     
18626           cap = cap[0].replace(/^ *> ?/gm, '');
18627     
18628           // Pass `top` to keep the current
18629           // "toplevel" state. This is exactly
18630           // how markdown.pl works.
18631           this.token(cap, top, true);
18632     
18633           this.tokens.push({
18634             type: 'blockquote_end'
18635           });
18636     
18637           continue;
18638         }
18639     
18640         // list
18641         if (cap = this.rules.list.exec(src)) {
18642           src = src.substring(cap[0].length);
18643           bull = cap[2];
18644     
18645           this.tokens.push({
18646             type: 'list_start',
18647             ordered: bull.length > 1
18648           });
18649     
18650           // Get each top-level item.
18651           cap = cap[0].match(this.rules.item);
18652     
18653           next = false;
18654           l = cap.length;
18655           i = 0;
18656     
18657           for (; i < l; i++) {
18658             item = cap[i];
18659     
18660             // Remove the list item's bullet
18661             // so it is seen as the next token.
18662             space = item.length;
18663             item = item.replace(/^ *([*+-]|\d+\.) +/, '');
18664     
18665             // Outdent whatever the
18666             // list item contains. Hacky.
18667             if (~item.indexOf('\n ')) {
18668               space -= item.length;
18669               item = !this.options.pedantic
18670                 ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
18671                 : item.replace(/^ {1,4}/gm, '');
18672             }
18673     
18674             // Determine whether the next list item belongs here.
18675             // Backpedal if it does not belong in this list.
18676             if (this.options.smartLists && i !== l - 1) {
18677               b = block.bullet.exec(cap[i + 1])[0];
18678               if (bull !== b && !(bull.length > 1 && b.length > 1)) {
18679                 src = cap.slice(i + 1).join('\n') + src;
18680                 i = l - 1;
18681               }
18682             }
18683     
18684             // Determine whether item is loose or not.
18685             // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
18686             // for discount behavior.
18687             loose = next || /\n\n(?!\s*$)/.test(item);
18688             if (i !== l - 1) {
18689               next = item.charAt(item.length - 1) === '\n';
18690               if (!loose) { loose = next; }
18691             }
18692     
18693             this.tokens.push({
18694               type: loose
18695                 ? 'loose_item_start'
18696                 : 'list_item_start'
18697             });
18698     
18699             // Recurse.
18700             this.token(item, false, bq);
18701     
18702             this.tokens.push({
18703               type: 'list_item_end'
18704             });
18705           }
18706     
18707           this.tokens.push({
18708             type: 'list_end'
18709           });
18710     
18711           continue;
18712         }
18713     
18714         // html
18715         if (cap = this.rules.html.exec(src)) {
18716           src = src.substring(cap[0].length);
18717           this.tokens.push({
18718             type: this.options.sanitize
18719               ? 'paragraph'
18720               : 'html',
18721             pre: !this.options.sanitizer
18722               && (cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style'),
18723             text: cap[0]
18724           });
18725           continue;
18726         }
18727     
18728         // def
18729         if ((!bq && top) && (cap = this.rules.def.exec(src))) {
18730           src = src.substring(cap[0].length);
18731           this.tokens.links[cap[1].toLowerCase()] = {
18732             href: cap[2],
18733             title: cap[3]
18734           };
18735           continue;
18736         }
18737     
18738         // table (gfm)
18739         if (top && (cap = this.rules.table.exec(src))) {
18740           src = src.substring(cap[0].length);
18741     
18742           item = {
18743             type: 'table',
18744             header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
18745             align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
18746             cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
18747           };
18748     
18749           for (i = 0; i < item.align.length; i++) {
18750             if (/^ *-+: *$/.test(item.align[i])) {
18751               item.align[i] = 'right';
18752             } else if (/^ *:-+: *$/.test(item.align[i])) {
18753               item.align[i] = 'center';
18754             } else if (/^ *:-+ *$/.test(item.align[i])) {
18755               item.align[i] = 'left';
18756             } else {
18757               item.align[i] = null;
18758             }
18759           }
18760     
18761           for (i = 0; i < item.cells.length; i++) {
18762             item.cells[i] = item.cells[i]
18763               .replace(/^ *\| *| *\| *$/g, '')
18764               .split(/ *\| */);
18765           }
18766     
18767           this.tokens.push(item);
18768     
18769           continue;
18770         }
18771     
18772         // top-level paragraph
18773         if (top && (cap = this.rules.paragraph.exec(src))) {
18774           src = src.substring(cap[0].length);
18775           this.tokens.push({
18776             type: 'paragraph',
18777             text: cap[1].charAt(cap[1].length - 1) === '\n'
18778               ? cap[1].slice(0, -1)
18779               : cap[1]
18780           });
18781           continue;
18782         }
18783     
18784         // text
18785         if (cap = this.rules.text.exec(src)) {
18786           // Top-level should never reach here.
18787           src = src.substring(cap[0].length);
18788           this.tokens.push({
18789             type: 'text',
18790             text: cap[0]
18791           });
18792           continue;
18793         }
18794     
18795         if (src) {
18796           throw new
18797             Error('Infinite loop on byte: ' + src.charCodeAt(0));
18798         }
18799       }
18800     
18801       return this.tokens;
18802     };
18803     
18804     /**
18805      * Inline-Level Grammar
18806      */
18807     
18808     var inline = {
18809       escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
18810       autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
18811       url: noop,
18812       tag: /^<!--[\s\S]*?-->|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
18813       link: /^!?\[(inside)\]\(href\)/,
18814       reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
18815       nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
18816       strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
18817       em: /^\b_((?:[^_]|__)+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
18818       code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
18819       br: /^ {2,}\n(?!\s*$)/,
18820       del: noop,
18821       text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
18822     };
18823     
18824     inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
18825     inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;
18826     
18827     inline.link = replace(inline.link)
18828       ('inside', inline._inside)
18829       ('href', inline._href)
18830       ();
18831     
18832     inline.reflink = replace(inline.reflink)
18833       ('inside', inline._inside)
18834       ();
18835     
18836     /**
18837      * Normal Inline Grammar
18838      */
18839     
18840     inline.normal = merge({}, inline);
18841     
18842     /**
18843      * Pedantic Inline Grammar
18844      */
18845     
18846     inline.pedantic = merge({}, inline.normal, {
18847       strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
18848       em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
18849     });
18850     
18851     /**
18852      * GFM Inline Grammar
18853      */
18854     
18855     inline.gfm = merge({}, inline.normal, {
18856       escape: replace(inline.escape)('])', '~|])')(),
18857       url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
18858       del: /^~~(?=\S)([\s\S]*?\S)~~/,
18859       text: replace(inline.text)
18860         (']|', '~]|')
18861         ('|', '|https?://|')
18862         ()
18863     });
18864     
18865     /**
18866      * GFM + Line Breaks Inline Grammar
18867      */
18868     
18869     inline.breaks = merge({}, inline.gfm, {
18870       br: replace(inline.br)('{2,}', '*')(),
18871       text: replace(inline.gfm.text)('{2,}', '*')()
18872     });
18873     
18874     /**
18875      * Inline Lexer & Compiler
18876      */
18877     
18878     var InlineLexer  = function (links, options) {
18879       this.options = options || marked.defaults;
18880       this.links = links;
18881       this.rules = inline.normal;
18882       this.renderer = this.options.renderer || new Renderer;
18883       this.renderer.options = this.options;
18884     
18885       if (!this.links) {
18886         throw new
18887           Error('Tokens array requires a `links` property.');
18888       }
18889     
18890       if (this.options.gfm) {
18891         if (this.options.breaks) {
18892           this.rules = inline.breaks;
18893         } else {
18894           this.rules = inline.gfm;
18895         }
18896       } else if (this.options.pedantic) {
18897         this.rules = inline.pedantic;
18898       }
18899     }
18900     
18901     /**
18902      * Expose Inline Rules
18903      */
18904     
18905     InlineLexer.rules = inline;
18906     
18907     /**
18908      * Static Lexing/Compiling Method
18909      */
18910     
18911     InlineLexer.output = function(src, links, options) {
18912       var inline = new InlineLexer(links, options);
18913       return inline.output(src);
18914     };
18915     
18916     /**
18917      * Lexing/Compiling
18918      */
18919     
18920     InlineLexer.prototype.output = function(src) {
18921       var out = ''
18922         , link
18923         , text
18924         , href
18925         , cap;
18926     
18927       while (src) {
18928         // escape
18929         if (cap = this.rules.escape.exec(src)) {
18930           src = src.substring(cap[0].length);
18931           out += cap[1];
18932           continue;
18933         }
18934     
18935         // autolink
18936         if (cap = this.rules.autolink.exec(src)) {
18937           src = src.substring(cap[0].length);
18938           if (cap[2] === '@') {
18939             text = cap[1].charAt(6) === ':'
18940               ? this.mangle(cap[1].substring(7))
18941               : this.mangle(cap[1]);
18942             href = this.mangle('mailto:') + text;
18943           } else {
18944             text = escape(cap[1]);
18945             href = text;
18946           }
18947           out += this.renderer.link(href, null, text);
18948           continue;
18949         }
18950     
18951         // url (gfm)
18952         if (!this.inLink && (cap = this.rules.url.exec(src))) {
18953           src = src.substring(cap[0].length);
18954           text = escape(cap[1]);
18955           href = text;
18956           out += this.renderer.link(href, null, text);
18957           continue;
18958         }
18959     
18960         // tag
18961         if (cap = this.rules.tag.exec(src)) {
18962           if (!this.inLink && /^<a /i.test(cap[0])) {
18963             this.inLink = true;
18964           } else if (this.inLink && /^<\/a>/i.test(cap[0])) {
18965             this.inLink = false;
18966           }
18967           src = src.substring(cap[0].length);
18968           out += this.options.sanitize
18969             ? this.options.sanitizer
18970               ? this.options.sanitizer(cap[0])
18971               : escape(cap[0])
18972             : cap[0];
18973           continue;
18974         }
18975     
18976         // link
18977         if (cap = this.rules.link.exec(src)) {
18978           src = src.substring(cap[0].length);
18979           this.inLink = true;
18980           out += this.outputLink(cap, {
18981             href: cap[2],
18982             title: cap[3]
18983           });
18984           this.inLink = false;
18985           continue;
18986         }
18987     
18988         // reflink, nolink
18989         if ((cap = this.rules.reflink.exec(src))
18990             || (cap = this.rules.nolink.exec(src))) {
18991           src = src.substring(cap[0].length);
18992           link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
18993           link = this.links[link.toLowerCase()];
18994           if (!link || !link.href) {
18995             out += cap[0].charAt(0);
18996             src = cap[0].substring(1) + src;
18997             continue;
18998           }
18999           this.inLink = true;
19000           out += this.outputLink(cap, link);
19001           this.inLink = false;
19002           continue;
19003         }
19004     
19005         // strong
19006         if (cap = this.rules.strong.exec(src)) {
19007           src = src.substring(cap[0].length);
19008           out += this.renderer.strong(this.output(cap[2] || cap[1]));
19009           continue;
19010         }
19011     
19012         // em
19013         if (cap = this.rules.em.exec(src)) {
19014           src = src.substring(cap[0].length);
19015           out += this.renderer.em(this.output(cap[2] || cap[1]));
19016           continue;
19017         }
19018     
19019         // code
19020         if (cap = this.rules.code.exec(src)) {
19021           src = src.substring(cap[0].length);
19022           out += this.renderer.codespan(escape(cap[2], true));
19023           continue;
19024         }
19025     
19026         // br
19027         if (cap = this.rules.br.exec(src)) {
19028           src = src.substring(cap[0].length);
19029           out += this.renderer.br();
19030           continue;
19031         }
19032     
19033         // del (gfm)
19034         if (cap = this.rules.del.exec(src)) {
19035           src = src.substring(cap[0].length);
19036           out += this.renderer.del(this.output(cap[1]));
19037           continue;
19038         }
19039     
19040         // text
19041         if (cap = this.rules.text.exec(src)) {
19042           src = src.substring(cap[0].length);
19043           out += this.renderer.text(escape(this.smartypants(cap[0])));
19044           continue;
19045         }
19046     
19047         if (src) {
19048           throw new
19049             Error('Infinite loop on byte: ' + src.charCodeAt(0));
19050         }
19051       }
19052     
19053       return out;
19054     };
19055     
19056     /**
19057      * Compile Link
19058      */
19059     
19060     InlineLexer.prototype.outputLink = function(cap, link) {
19061       var href = escape(link.href)
19062         , title = link.title ? escape(link.title) : null;
19063     
19064       return cap[0].charAt(0) !== '!'
19065         ? this.renderer.link(href, title, this.output(cap[1]))
19066         : this.renderer.image(href, title, escape(cap[1]));
19067     };
19068     
19069     /**
19070      * Smartypants Transformations
19071      */
19072     
19073     InlineLexer.prototype.smartypants = function(text) {
19074       if (!this.options.smartypants)  { return text; }
19075       return text
19076         // em-dashes
19077         .replace(/---/g, '\u2014')
19078         // en-dashes
19079         .replace(/--/g, '\u2013')
19080         // opening singles
19081         .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
19082         // closing singles & apostrophes
19083         .replace(/'/g, '\u2019')
19084         // opening doubles
19085         .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
19086         // closing doubles
19087         .replace(/"/g, '\u201d')
19088         // ellipses
19089         .replace(/\.{3}/g, '\u2026');
19090     };
19091     
19092     /**
19093      * Mangle Links
19094      */
19095     
19096     InlineLexer.prototype.mangle = function(text) {
19097       if (!this.options.mangle) { return text; }
19098       var out = ''
19099         , l = text.length
19100         , i = 0
19101         , ch;
19102     
19103       for (; i < l; i++) {
19104         ch = text.charCodeAt(i);
19105         if (Math.random() > 0.5) {
19106           ch = 'x' + ch.toString(16);
19107         }
19108         out += '&#' + ch + ';';
19109       }
19110     
19111       return out;
19112     };
19113     
19114     /**
19115      * Renderer
19116      */
19117     
19118      /**
19119          * eval:var:Renderer
19120     */
19121     
19122     var Renderer   = function (options) {
19123       this.options = options || {};
19124     }
19125     
19126     Renderer.prototype.code = function(code, lang, escaped) {
19127       if (this.options.highlight) {
19128         var out = this.options.highlight(code, lang);
19129         if (out != null && out !== code) {
19130           escaped = true;
19131           code = out;
19132         }
19133       } else {
19134             // hack!!! - it's already escapeD?
19135             escaped = true;
19136       }
19137     
19138       if (!lang) {
19139         return '<pre><code>'
19140           + (escaped ? code : escape(code, true))
19141           + '\n</code></pre>';
19142       }
19143     
19144       return '<pre><code class="'
19145         + this.options.langPrefix
19146         + escape(lang, true)
19147         + '">'
19148         + (escaped ? code : escape(code, true))
19149         + '\n</code></pre>\n';
19150     };
19151     
19152     Renderer.prototype.blockquote = function(quote) {
19153       return '<blockquote>\n' + quote + '</blockquote>\n';
19154     };
19155     
19156     Renderer.prototype.html = function(html) {
19157       return html;
19158     };
19159     
19160     Renderer.prototype.heading = function(text, level, raw) {
19161       return '<h'
19162         + level
19163         + ' id="'
19164         + this.options.headerPrefix
19165         + raw.toLowerCase().replace(/[^\w]+/g, '-')
19166         + '">'
19167         + text
19168         + '</h'
19169         + level
19170         + '>\n';
19171     };
19172     
19173     Renderer.prototype.hr = function() {
19174       return this.options.xhtml ? '<hr/>\n' : '<hr>\n';
19175     };
19176     
19177     Renderer.prototype.list = function(body, ordered) {
19178       var type = ordered ? 'ol' : 'ul';
19179       return '<' + type + '>\n' + body + '</' + type + '>\n';
19180     };
19181     
19182     Renderer.prototype.listitem = function(text) {
19183       return '<li>' + text + '</li>\n';
19184     };
19185     
19186     Renderer.prototype.paragraph = function(text) {
19187       return '<p>' + text + '</p>\n';
19188     };
19189     
19190     Renderer.prototype.table = function(header, body) {
19191       return '<table class="table table-striped">\n'
19192         + '<thead>\n'
19193         + header
19194         + '</thead>\n'
19195         + '<tbody>\n'
19196         + body
19197         + '</tbody>\n'
19198         + '</table>\n';
19199     };
19200     
19201     Renderer.prototype.tablerow = function(content) {
19202       return '<tr>\n' + content + '</tr>\n';
19203     };
19204     
19205     Renderer.prototype.tablecell = function(content, flags) {
19206       var type = flags.header ? 'th' : 'td';
19207       var tag = flags.align
19208         ? '<' + type + ' style="text-align:' + flags.align + '">'
19209         : '<' + type + '>';
19210       return tag + content + '</' + type + '>\n';
19211     };
19212     
19213     // span level renderer
19214     Renderer.prototype.strong = function(text) {
19215       return '<strong>' + text + '</strong>';
19216     };
19217     
19218     Renderer.prototype.em = function(text) {
19219       return '<em>' + text + '</em>';
19220     };
19221     
19222     Renderer.prototype.codespan = function(text) {
19223       return '<code>' + text + '</code>';
19224     };
19225     
19226     Renderer.prototype.br = function() {
19227       return this.options.xhtml ? '<br/>' : '<br>';
19228     };
19229     
19230     Renderer.prototype.del = function(text) {
19231       return '<del>' + text + '</del>';
19232     };
19233     
19234     Renderer.prototype.link = function(href, title, text) {
19235       if (this.options.sanitize) {
19236         try {
19237           var prot = decodeURIComponent(unescape(href))
19238             .replace(/[^\w:]/g, '')
19239             .toLowerCase();
19240         } catch (e) {
19241           return '';
19242         }
19243         if (prot.indexOf('javascript:') === 0 || prot.indexOf('vbscript:') === 0) {
19244           return '';
19245         }
19246       }
19247       var out = '<a href="' + href + '"';
19248       if (title) {
19249         out += ' title="' + title + '"';
19250       }
19251       out += '>' + text + '</a>';
19252       return out;
19253     };
19254     
19255     Renderer.prototype.image = function(href, title, text) {
19256       var out = '<img src="' + href + '" alt="' + text + '"';
19257       if (title) {
19258         out += ' title="' + title + '"';
19259       }
19260       out += this.options.xhtml ? '/>' : '>';
19261       return out;
19262     };
19263     
19264     Renderer.prototype.text = function(text) {
19265       return text;
19266     };
19267     
19268     /**
19269      * Parsing & Compiling
19270      */
19271          /**
19272          * eval:var:Parser
19273     */
19274     
19275     var Parser= function (options) {
19276       this.tokens = [];
19277       this.token = null;
19278       this.options = options || marked.defaults;
19279       this.options.renderer = this.options.renderer || new Renderer;
19280       this.renderer = this.options.renderer;
19281       this.renderer.options = this.options;
19282     }
19283     
19284     /**
19285      * Static Parse Method
19286      */
19287     
19288     Parser.parse = function(src, options, renderer) {
19289       var parser = new Parser(options, renderer);
19290       return parser.parse(src);
19291     };
19292     
19293     /**
19294      * Parse Loop
19295      */
19296     
19297     Parser.prototype.parse = function(src) {
19298       this.inline = new InlineLexer(src.links, this.options, this.renderer);
19299       this.tokens = src.reverse();
19300     
19301       var out = '';
19302       while (this.next()) {
19303         out += this.tok();
19304       }
19305     
19306       return out;
19307     };
19308     
19309     /**
19310      * Next Token
19311      */
19312     
19313     Parser.prototype.next = function() {
19314       return this.token = this.tokens.pop();
19315     };
19316     
19317     /**
19318      * Preview Next Token
19319      */
19320     
19321     Parser.prototype.peek = function() {
19322       return this.tokens[this.tokens.length - 1] || 0;
19323     };
19324     
19325     /**
19326      * Parse Text Tokens
19327      */
19328     
19329     Parser.prototype.parseText = function() {
19330       var body = this.token.text;
19331     
19332       while (this.peek().type === 'text') {
19333         body += '\n' + this.next().text;
19334       }
19335     
19336       return this.inline.output(body);
19337     };
19338     
19339     /**
19340      * Parse Current Token
19341      */
19342     
19343     Parser.prototype.tok = function() {
19344       switch (this.token.type) {
19345         case 'space': {
19346           return '';
19347         }
19348         case 'hr': {
19349           return this.renderer.hr();
19350         }
19351         case 'heading': {
19352           return this.renderer.heading(
19353             this.inline.output(this.token.text),
19354             this.token.depth,
19355             this.token.text);
19356         }
19357         case 'code': {
19358           return this.renderer.code(this.token.text,
19359             this.token.lang,
19360             this.token.escaped);
19361         }
19362         case 'table': {
19363           var header = ''
19364             , body = ''
19365             , i
19366             , row
19367             , cell
19368             , flags
19369             , j;
19370     
19371           // header
19372           cell = '';
19373           for (i = 0; i < this.token.header.length; i++) {
19374             flags = { header: true, align: this.token.align[i] };
19375             cell += this.renderer.tablecell(
19376               this.inline.output(this.token.header[i]),
19377               { header: true, align: this.token.align[i] }
19378             );
19379           }
19380           header += this.renderer.tablerow(cell);
19381     
19382           for (i = 0; i < this.token.cells.length; i++) {
19383             row = this.token.cells[i];
19384     
19385             cell = '';
19386             for (j = 0; j < row.length; j++) {
19387               cell += this.renderer.tablecell(
19388                 this.inline.output(row[j]),
19389                 { header: false, align: this.token.align[j] }
19390               );
19391             }
19392     
19393             body += this.renderer.tablerow(cell);
19394           }
19395           return this.renderer.table(header, body);
19396         }
19397         case 'blockquote_start': {
19398           var body = '';
19399     
19400           while (this.next().type !== 'blockquote_end') {
19401             body += this.tok();
19402           }
19403     
19404           return this.renderer.blockquote(body);
19405         }
19406         case 'list_start': {
19407           var body = ''
19408             , ordered = this.token.ordered;
19409     
19410           while (this.next().type !== 'list_end') {
19411             body += this.tok();
19412           }
19413     
19414           return this.renderer.list(body, ordered);
19415         }
19416         case 'list_item_start': {
19417           var body = '';
19418     
19419           while (this.next().type !== 'list_item_end') {
19420             body += this.token.type === 'text'
19421               ? this.parseText()
19422               : this.tok();
19423           }
19424     
19425           return this.renderer.listitem(body);
19426         }
19427         case 'loose_item_start': {
19428           var body = '';
19429     
19430           while (this.next().type !== 'list_item_end') {
19431             body += this.tok();
19432           }
19433     
19434           return this.renderer.listitem(body);
19435         }
19436         case 'html': {
19437           var html = !this.token.pre && !this.options.pedantic
19438             ? this.inline.output(this.token.text)
19439             : this.token.text;
19440           return this.renderer.html(html);
19441         }
19442         case 'paragraph': {
19443           return this.renderer.paragraph(this.inline.output(this.token.text));
19444         }
19445         case 'text': {
19446           return this.renderer.paragraph(this.parseText());
19447         }
19448       }
19449     };
19450   
19451     
19452     /**
19453      * Marked
19454      */
19455          /**
19456          * eval:var:marked
19457     */
19458     var marked = function (src, opt, callback) {
19459       if (callback || typeof opt === 'function') {
19460         if (!callback) {
19461           callback = opt;
19462           opt = null;
19463         }
19464     
19465         opt = merge({}, marked.defaults, opt || {});
19466     
19467         var highlight = opt.highlight
19468           , tokens
19469           , pending
19470           , i = 0;
19471     
19472         try {
19473           tokens = Lexer.lex(src, opt)
19474         } catch (e) {
19475           return callback(e);
19476         }
19477     
19478         pending = tokens.length;
19479          /**
19480          * eval:var:done
19481     */
19482         var done = function(err) {
19483           if (err) {
19484             opt.highlight = highlight;
19485             return callback(err);
19486           }
19487     
19488           var out;
19489     
19490           try {
19491             out = Parser.parse(tokens, opt);
19492           } catch (e) {
19493             err = e;
19494           }
19495     
19496           opt.highlight = highlight;
19497     
19498           return err
19499             ? callback(err)
19500             : callback(null, out);
19501         };
19502     
19503         if (!highlight || highlight.length < 3) {
19504           return done();
19505         }
19506     
19507         delete opt.highlight;
19508     
19509         if (!pending) { return done(); }
19510     
19511         for (; i < tokens.length; i++) {
19512           (function(token) {
19513             if (token.type !== 'code') {
19514               return --pending || done();
19515             }
19516             return highlight(token.text, token.lang, function(err, code) {
19517               if (err) { return done(err); }
19518               if (code == null || code === token.text) {
19519                 return --pending || done();
19520               }
19521               token.text = code;
19522               token.escaped = true;
19523               --pending || done();
19524             });
19525           })(tokens[i]);
19526         }
19527     
19528         return;
19529       }
19530       try {
19531         if (opt) { opt = merge({}, marked.defaults, opt); }
19532         return Parser.parse(Lexer.lex(src, opt), opt);
19533       } catch (e) {
19534         e.message += '\nPlease report this to https://github.com/chjj/marked.';
19535         if ((opt || marked.defaults).silent) {
19536           return '<p>An error occured:</p><pre>'
19537             + escape(e.message + '', true)
19538             + '</pre>';
19539         }
19540         throw e;
19541       }
19542     }
19543     
19544     /**
19545      * Options
19546      */
19547     
19548     marked.options =
19549     marked.setOptions = function(opt) {
19550       merge(marked.defaults, opt);
19551       return marked;
19552     };
19553     
19554     marked.defaults = {
19555       gfm: true,
19556       tables: true,
19557       breaks: false,
19558       pedantic: false,
19559       sanitize: false,
19560       sanitizer: null,
19561       mangle: true,
19562       smartLists: false,
19563       silent: false,
19564       highlight: null,
19565       langPrefix: 'lang-',
19566       smartypants: false,
19567       headerPrefix: '',
19568       renderer: new Renderer,
19569       xhtml: false
19570     };
19571     
19572     /**
19573      * Expose
19574      */
19575     
19576     marked.Parser = Parser;
19577     marked.parser = Parser.parse;
19578     
19579     marked.Renderer = Renderer;
19580     
19581     marked.Lexer = Lexer;
19582     marked.lexer = Lexer.lex;
19583     
19584     marked.InlineLexer = InlineLexer;
19585     marked.inlineLexer = InlineLexer.output;
19586     
19587     marked.parse = marked;
19588     
19589     Roo.Markdown.marked = marked;
19590
19591 })();/*
19592  * Based on:
19593  * Ext JS Library 1.1.1
19594  * Copyright(c) 2006-2007, Ext JS, LLC.
19595  *
19596  * Originally Released Under LGPL - original licence link has changed is not relivant.
19597  *
19598  * Fork - LGPL
19599  * <script type="text/javascript">
19600  */
19601
19602
19603
19604 /*
19605  * These classes are derivatives of the similarly named classes in the YUI Library.
19606  * The original license:
19607  * Copyright (c) 2006, Yahoo! Inc. All rights reserved.
19608  * Code licensed under the BSD License:
19609  * http://developer.yahoo.net/yui/license.txt
19610  */
19611
19612 (function() {
19613
19614 var Event=Roo.EventManager;
19615 var Dom=Roo.lib.Dom;
19616
19617 /**
19618  * @class Roo.dd.DragDrop
19619  * @extends Roo.util.Observable
19620  * Defines the interface and base operation of items that that can be
19621  * dragged or can be drop targets.  It was designed to be extended, overriding
19622  * the event handlers for startDrag, onDrag, onDragOver and onDragOut.
19623  * Up to three html elements can be associated with a DragDrop instance:
19624  * <ul>
19625  * <li>linked element: the element that is passed into the constructor.
19626  * This is the element which defines the boundaries for interaction with
19627  * other DragDrop objects.</li>
19628  * <li>handle element(s): The drag operation only occurs if the element that
19629  * was clicked matches a handle element.  By default this is the linked
19630  * element, but there are times that you will want only a portion of the
19631  * linked element to initiate the drag operation, and the setHandleElId()
19632  * method provides a way to define this.</li>
19633  * <li>drag element: this represents the element that would be moved along
19634  * with the cursor during a drag operation.  By default, this is the linked
19635  * element itself as in {@link Roo.dd.DD}.  setDragElId() lets you define
19636  * a separate element that would be moved, as in {@link Roo.dd.DDProxy}.
19637  * </li>
19638  * </ul>
19639  * This class should not be instantiated until the onload event to ensure that
19640  * the associated elements are available.
19641  * The following would define a DragDrop obj that would interact with any
19642  * other DragDrop obj in the "group1" group:
19643  * <pre>
19644  *  dd = new Roo.dd.DragDrop("div1", "group1");
19645  * </pre>
19646  * Since none of the event handlers have been implemented, nothing would
19647  * actually happen if you were to run the code above.  Normally you would
19648  * override this class or one of the default implementations, but you can
19649  * also override the methods you want on an instance of the class...
19650  * <pre>
19651  *  dd.onDragDrop = function(e, id) {
19652  *  &nbsp;&nbsp;alert("dd was dropped on " + id);
19653  *  }
19654  * </pre>
19655  * @constructor
19656  * @param {String} id of the element that is linked to this instance
19657  * @param {String} sGroup the group of related DragDrop objects
19658  * @param {object} config an object containing configurable attributes
19659  *                Valid properties for DragDrop:
19660  *                    padding, isTarget, maintainOffset, primaryButtonOnly
19661  */
19662 Roo.dd.DragDrop = function(id, sGroup, config) {
19663     if (id) {
19664         this.init(id, sGroup, config);
19665     }
19666     
19667 };
19668
19669 Roo.extend(Roo.dd.DragDrop, Roo.util.Observable , {
19670
19671     /**
19672      * The id of the element associated with this object.  This is what we
19673      * refer to as the "linked element" because the size and position of
19674      * this element is used to determine when the drag and drop objects have
19675      * interacted.
19676      * @property id
19677      * @type String
19678      */
19679     id: null,
19680
19681     /**
19682      * Configuration attributes passed into the constructor
19683      * @property config
19684      * @type object
19685      */
19686     config: null,
19687
19688     /**
19689      * The id of the element that will be dragged.  By default this is same
19690      * as the linked element , but could be changed to another element. Ex:
19691      * Roo.dd.DDProxy
19692      * @property dragElId
19693      * @type String
19694      * @private
19695      */
19696     dragElId: null,
19697
19698     /**
19699      * the id of the element that initiates the drag operation.  By default
19700      * this is the linked element, but could be changed to be a child of this
19701      * element.  This lets us do things like only starting the drag when the
19702      * header element within the linked html element is clicked.
19703      * @property handleElId
19704      * @type String
19705      * @private
19706      */
19707     handleElId: null,
19708
19709     /**
19710      * An associative array of HTML tags that will be ignored if clicked.
19711      * @property invalidHandleTypes
19712      * @type {string: string}
19713      */
19714     invalidHandleTypes: null,
19715
19716     /**
19717      * An associative array of ids for elements that will be ignored if clicked
19718      * @property invalidHandleIds
19719      * @type {string: string}
19720      */
19721     invalidHandleIds: null,
19722
19723     /**
19724      * An indexted array of css class names for elements that will be ignored
19725      * if clicked.
19726      * @property invalidHandleClasses
19727      * @type string[]
19728      */
19729     invalidHandleClasses: null,
19730
19731     /**
19732      * The linked element's absolute X position at the time the drag was
19733      * started
19734      * @property startPageX
19735      * @type int
19736      * @private
19737      */
19738     startPageX: 0,
19739
19740     /**
19741      * The linked element's absolute X position at the time the drag was
19742      * started
19743      * @property startPageY
19744      * @type int
19745      * @private
19746      */
19747     startPageY: 0,
19748
19749     /**
19750      * The group defines a logical collection of DragDrop objects that are
19751      * related.  Instances only get events when interacting with other
19752      * DragDrop object in the same group.  This lets us define multiple
19753      * groups using a single DragDrop subclass if we want.
19754      * @property groups
19755      * @type {string: string}
19756      */
19757     groups: null,
19758
19759     /**
19760      * Individual drag/drop instances can be locked.  This will prevent
19761      * onmousedown start drag.
19762      * @property locked
19763      * @type boolean
19764      * @private
19765      */
19766     locked: false,
19767
19768     /**
19769      * Lock this instance
19770      * @method lock
19771      */
19772     lock: function() { this.locked = true; },
19773
19774     /**
19775      * Unlock this instace
19776      * @method unlock
19777      */
19778     unlock: function() { this.locked = false; },
19779
19780     /**
19781      * By default, all insances can be a drop target.  This can be disabled by
19782      * setting isTarget to false.
19783      * @method isTarget
19784      * @type boolean
19785      */
19786     isTarget: true,
19787
19788     /**
19789      * The padding configured for this drag and drop object for calculating
19790      * the drop zone intersection with this object.
19791      * @method padding
19792      * @type int[]
19793      */
19794     padding: null,
19795
19796     /**
19797      * Cached reference to the linked element
19798      * @property _domRef
19799      * @private
19800      */
19801     _domRef: null,
19802
19803     /**
19804      * Internal typeof flag
19805      * @property __ygDragDrop
19806      * @private
19807      */
19808     __ygDragDrop: true,
19809
19810     /**
19811      * Set to true when horizontal contraints are applied
19812      * @property constrainX
19813      * @type boolean
19814      * @private
19815      */
19816     constrainX: false,
19817
19818     /**
19819      * Set to true when vertical contraints are applied
19820      * @property constrainY
19821      * @type boolean
19822      * @private
19823      */
19824     constrainY: false,
19825
19826     /**
19827      * The left constraint
19828      * @property minX
19829      * @type int
19830      * @private
19831      */
19832     minX: 0,
19833
19834     /**
19835      * The right constraint
19836      * @property maxX
19837      * @type int
19838      * @private
19839      */
19840     maxX: 0,
19841
19842     /**
19843      * The up constraint
19844      * @property minY
19845      * @type int
19846      * @type int
19847      * @private
19848      */
19849     minY: 0,
19850
19851     /**
19852      * The down constraint
19853      * @property maxY
19854      * @type int
19855      * @private
19856      */
19857     maxY: 0,
19858
19859     /**
19860      * Maintain offsets when we resetconstraints.  Set to true when you want
19861      * the position of the element relative to its parent to stay the same
19862      * when the page changes
19863      *
19864      * @property maintainOffset
19865      * @type boolean
19866      */
19867     maintainOffset: false,
19868
19869     /**
19870      * Array of pixel locations the element will snap to if we specified a
19871      * horizontal graduation/interval.  This array is generated automatically
19872      * when you define a tick interval.
19873      * @property xTicks
19874      * @type int[]
19875      */
19876     xTicks: null,
19877
19878     /**
19879      * Array of pixel locations the element will snap to if we specified a
19880      * vertical graduation/interval.  This array is generated automatically
19881      * when you define a tick interval.
19882      * @property yTicks
19883      * @type int[]
19884      */
19885     yTicks: null,
19886
19887     /**
19888      * By default the drag and drop instance will only respond to the primary
19889      * button click (left button for a right-handed mouse).  Set to true to
19890      * allow drag and drop to start with any mouse click that is propogated
19891      * by the browser
19892      * @property primaryButtonOnly
19893      * @type boolean
19894      */
19895     primaryButtonOnly: true,
19896
19897     /**
19898      * The availabe property is false until the linked dom element is accessible.
19899      * @property available
19900      * @type boolean
19901      */
19902     available: false,
19903
19904     /**
19905      * By default, drags can only be initiated if the mousedown occurs in the
19906      * region the linked element is.  This is done in part to work around a
19907      * bug in some browsers that mis-report the mousedown if the previous
19908      * mouseup happened outside of the window.  This property is set to true
19909      * if outer handles are defined.
19910      *
19911      * @property hasOuterHandles
19912      * @type boolean
19913      * @default false
19914      */
19915     hasOuterHandles: false,
19916
19917     /**
19918      * Code that executes immediately before the startDrag event
19919      * @method b4StartDrag
19920      * @private
19921      */
19922     b4StartDrag: function(x, y) { },
19923
19924     /**
19925      * Abstract method called after a drag/drop object is clicked
19926      * and the drag or mousedown time thresholds have beeen met.
19927      * @method startDrag
19928      * @param {int} X click location
19929      * @param {int} Y click location
19930      */
19931     startDrag: function(x, y) { /* override this */ },
19932
19933     /**
19934      * Code that executes immediately before the onDrag event
19935      * @method b4Drag
19936      * @private
19937      */
19938     b4Drag: function(e) { },
19939
19940     /**
19941      * Abstract method called during the onMouseMove event while dragging an
19942      * object.
19943      * @method onDrag
19944      * @param {Event} e the mousemove event
19945      */
19946     onDrag: function(e) { /* override this */ },
19947
19948     /**
19949      * Abstract method called when this element fist begins hovering over
19950      * another DragDrop obj
19951      * @method onDragEnter
19952      * @param {Event} e the mousemove event
19953      * @param {String|DragDrop[]} id In POINT mode, the element
19954      * id this is hovering over.  In INTERSECT mode, an array of one or more
19955      * dragdrop items being hovered over.
19956      */
19957     onDragEnter: function(e, id) { /* override this */ },
19958
19959     /**
19960      * Code that executes immediately before the onDragOver event
19961      * @method b4DragOver
19962      * @private
19963      */
19964     b4DragOver: function(e) { },
19965
19966     /**
19967      * Abstract method called when this element is hovering over another
19968      * DragDrop obj
19969      * @method onDragOver
19970      * @param {Event} e the mousemove event
19971      * @param {String|DragDrop[]} id In POINT mode, the element
19972      * id this is hovering over.  In INTERSECT mode, an array of dd items
19973      * being hovered over.
19974      */
19975     onDragOver: function(e, id) { /* override this */ },
19976
19977     /**
19978      * Code that executes immediately before the onDragOut event
19979      * @method b4DragOut
19980      * @private
19981      */
19982     b4DragOut: function(e) { },
19983
19984     /**
19985      * Abstract method called when we are no longer hovering over an element
19986      * @method onDragOut
19987      * @param {Event} e the mousemove event
19988      * @param {String|DragDrop[]} id In POINT mode, the element
19989      * id this was hovering over.  In INTERSECT mode, an array of dd items
19990      * that the mouse is no longer over.
19991      */
19992     onDragOut: function(e, id) { /* override this */ },
19993
19994     /**
19995      * Code that executes immediately before the onDragDrop event
19996      * @method b4DragDrop
19997      * @private
19998      */
19999     b4DragDrop: function(e) { },
20000
20001     /**
20002      * Abstract method called when this item is dropped on another DragDrop
20003      * obj
20004      * @method onDragDrop
20005      * @param {Event} e the mouseup event
20006      * @param {String|DragDrop[]} id In POINT mode, the element
20007      * id this was dropped on.  In INTERSECT mode, an array of dd items this
20008      * was dropped on.
20009      */
20010     onDragDrop: function(e, id) { /* override this */ },
20011
20012     /**
20013      * Abstract method called when this item is dropped on an area with no
20014      * drop target
20015      * @method onInvalidDrop
20016      * @param {Event} e the mouseup event
20017      */
20018     onInvalidDrop: function(e) { /* override this */ },
20019
20020     /**
20021      * Code that executes immediately before the endDrag event
20022      * @method b4EndDrag
20023      * @private
20024      */
20025     b4EndDrag: function(e) { },
20026
20027     /**
20028      * Fired when we are done dragging the object
20029      * @method endDrag
20030      * @param {Event} e the mouseup event
20031      */
20032     endDrag: function(e) { /* override this */ },
20033
20034     /**
20035      * Code executed immediately before the onMouseDown event
20036      * @method b4MouseDown
20037      * @param {Event} e the mousedown event
20038      * @private
20039      */
20040     b4MouseDown: function(e) {  },
20041
20042     /**
20043      * Event handler that fires when a drag/drop obj gets a mousedown
20044      * @method onMouseDown
20045      * @param {Event} e the mousedown event
20046      */
20047     onMouseDown: function(e) { /* override this */ },
20048
20049     /**
20050      * Event handler that fires when a drag/drop obj gets a mouseup
20051      * @method onMouseUp
20052      * @param {Event} e the mouseup event
20053      */
20054     onMouseUp: function(e) { /* override this */ },
20055
20056     /**
20057      * Override the onAvailable method to do what is needed after the initial
20058      * position was determined.
20059      * @method onAvailable
20060      */
20061     onAvailable: function () {
20062     },
20063
20064     /*
20065      * Provides default constraint padding to "constrainTo" elements (defaults to {left: 0, right:0, top:0, bottom:0}).
20066      * @type Object
20067      */
20068     defaultPadding : {left:0, right:0, top:0, bottom:0},
20069
20070     /*
20071      * Initializes the drag drop object's constraints to restrict movement to a certain element.
20072  *
20073  * Usage:
20074  <pre><code>
20075  var dd = new Roo.dd.DDProxy("dragDiv1", "proxytest",
20076                 { dragElId: "existingProxyDiv" });
20077  dd.startDrag = function(){
20078      this.constrainTo("parent-id");
20079  };
20080  </code></pre>
20081  * Or you can initalize it using the {@link Roo.Element} object:
20082  <pre><code>
20083  Roo.get("dragDiv1").initDDProxy("proxytest", {dragElId: "existingProxyDiv"}, {
20084      startDrag : function(){
20085          this.constrainTo("parent-id");
20086      }
20087  });
20088  </code></pre>
20089      * @param {String/HTMLElement/Element} constrainTo The element to constrain to.
20090      * @param {Object/Number} pad (optional) Pad provides a way to specify "padding" of the constraints,
20091      * and can be either a number for symmetrical padding (4 would be equal to {left:4, right:4, top:4, bottom:4}) or
20092      * an object containing the sides to pad. For example: {right:10, bottom:10}
20093      * @param {Boolean} inContent (optional) Constrain the draggable in the content box of the element (inside padding and borders)
20094      */
20095     constrainTo : function(constrainTo, pad, inContent){
20096         if(typeof pad == "number"){
20097             pad = {left: pad, right:pad, top:pad, bottom:pad};
20098         }
20099         pad = pad || this.defaultPadding;
20100         var b = Roo.get(this.getEl()).getBox();
20101         var ce = Roo.get(constrainTo);
20102         var s = ce.getScroll();
20103         var c, cd = ce.dom;
20104         if(cd == document.body){
20105             c = { x: s.left, y: s.top, width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
20106         }else{
20107             xy = ce.getXY();
20108             c = {x : xy[0]+s.left, y: xy[1]+s.top, width: cd.clientWidth, height: cd.clientHeight};
20109         }
20110
20111
20112         var topSpace = b.y - c.y;
20113         var leftSpace = b.x - c.x;
20114
20115         this.resetConstraints();
20116         this.setXConstraint(leftSpace - (pad.left||0), // left
20117                 c.width - leftSpace - b.width - (pad.right||0) //right
20118         );
20119         this.setYConstraint(topSpace - (pad.top||0), //top
20120                 c.height - topSpace - b.height - (pad.bottom||0) //bottom
20121         );
20122     },
20123
20124     /**
20125      * Returns a reference to the linked element
20126      * @method getEl
20127      * @return {HTMLElement} the html element
20128      */
20129     getEl: function() {
20130         if (!this._domRef) {
20131             this._domRef = Roo.getDom(this.id);
20132         }
20133
20134         return this._domRef;
20135     },
20136
20137     /**
20138      * Returns a reference to the actual element to drag.  By default this is
20139      * the same as the html element, but it can be assigned to another
20140      * element. An example of this can be found in Roo.dd.DDProxy
20141      * @method getDragEl
20142      * @return {HTMLElement} the html element
20143      */
20144     getDragEl: function() {
20145         return Roo.getDom(this.dragElId);
20146     },
20147
20148     /**
20149      * Sets up the DragDrop object.  Must be called in the constructor of any
20150      * Roo.dd.DragDrop subclass
20151      * @method init
20152      * @param id the id of the linked element
20153      * @param {String} sGroup the group of related items
20154      * @param {object} config configuration attributes
20155      */
20156     init: function(id, sGroup, config) {
20157         this.initTarget(id, sGroup, config);
20158         if (!Roo.isTouch) {
20159             Event.on(this.id, "mousedown", this.handleMouseDown, this);
20160         }
20161         Event.on(this.id, "touchstart", this.handleMouseDown, this);
20162         // Event.on(this.id, "selectstart", Event.preventDefault);
20163     },
20164
20165     /**
20166      * Initializes Targeting functionality only... the object does not
20167      * get a mousedown handler.
20168      * @method initTarget
20169      * @param id the id of the linked element
20170      * @param {String} sGroup the group of related items
20171      * @param {object} config configuration attributes
20172      */
20173     initTarget: function(id, sGroup, config) {
20174
20175         // configuration attributes
20176         this.config = config || {};
20177
20178         // create a local reference to the drag and drop manager
20179         this.DDM = Roo.dd.DDM;
20180         // initialize the groups array
20181         this.groups = {};
20182
20183         // assume that we have an element reference instead of an id if the
20184         // parameter is not a string
20185         if (typeof id !== "string") {
20186             id = Roo.id(id);
20187         }
20188
20189         // set the id
20190         this.id = id;
20191
20192         // add to an interaction group
20193         this.addToGroup((sGroup) ? sGroup : "default");
20194
20195         // We don't want to register this as the handle with the manager
20196         // so we just set the id rather than calling the setter.
20197         this.handleElId = id;
20198
20199         // the linked element is the element that gets dragged by default
20200         this.setDragElId(id);
20201
20202         // by default, clicked anchors will not start drag operations.
20203         this.invalidHandleTypes = { A: "A" };
20204         this.invalidHandleIds = {};
20205         this.invalidHandleClasses = [];
20206
20207         this.applyConfig();
20208
20209         this.handleOnAvailable();
20210     },
20211
20212     /**
20213      * Applies the configuration parameters that were passed into the constructor.
20214      * This is supposed to happen at each level through the inheritance chain.  So
20215      * a DDProxy implentation will execute apply config on DDProxy, DD, and
20216      * DragDrop in order to get all of the parameters that are available in
20217      * each object.
20218      * @method applyConfig
20219      */
20220     applyConfig: function() {
20221
20222         // configurable properties:
20223         //    padding, isTarget, maintainOffset, primaryButtonOnly
20224         this.padding           = this.config.padding || [0, 0, 0, 0];
20225         this.isTarget          = (this.config.isTarget !== false);
20226         this.maintainOffset    = (this.config.maintainOffset);
20227         this.primaryButtonOnly = (this.config.primaryButtonOnly !== false);
20228
20229     },
20230
20231     /**
20232      * Executed when the linked element is available
20233      * @method handleOnAvailable
20234      * @private
20235      */
20236     handleOnAvailable: function() {
20237         this.available = true;
20238         this.resetConstraints();
20239         this.onAvailable();
20240     },
20241
20242      /**
20243      * Configures the padding for the target zone in px.  Effectively expands
20244      * (or reduces) the virtual object size for targeting calculations.
20245      * Supports css-style shorthand; if only one parameter is passed, all sides
20246      * will have that padding, and if only two are passed, the top and bottom
20247      * will have the first param, the left and right the second.
20248      * @method setPadding
20249      * @param {int} iTop    Top pad
20250      * @param {int} iRight  Right pad
20251      * @param {int} iBot    Bot pad
20252      * @param {int} iLeft   Left pad
20253      */
20254     setPadding: function(iTop, iRight, iBot, iLeft) {
20255         // this.padding = [iLeft, iRight, iTop, iBot];
20256         if (!iRight && 0 !== iRight) {
20257             this.padding = [iTop, iTop, iTop, iTop];
20258         } else if (!iBot && 0 !== iBot) {
20259             this.padding = [iTop, iRight, iTop, iRight];
20260         } else {
20261             this.padding = [iTop, iRight, iBot, iLeft];
20262         }
20263     },
20264
20265     /**
20266      * Stores the initial placement of the linked element.
20267      * @method setInitialPosition
20268      * @param {int} diffX   the X offset, default 0
20269      * @param {int} diffY   the Y offset, default 0
20270      */
20271     setInitPosition: function(diffX, diffY) {
20272         var el = this.getEl();
20273
20274         if (!this.DDM.verifyEl(el)) {
20275             return;
20276         }
20277
20278         var dx = diffX || 0;
20279         var dy = diffY || 0;
20280
20281         var p = Dom.getXY( el );
20282
20283         this.initPageX = p[0] - dx;
20284         this.initPageY = p[1] - dy;
20285
20286         this.lastPageX = p[0];
20287         this.lastPageY = p[1];
20288
20289
20290         this.setStartPosition(p);
20291     },
20292
20293     /**
20294      * Sets the start position of the element.  This is set when the obj
20295      * is initialized, the reset when a drag is started.
20296      * @method setStartPosition
20297      * @param pos current position (from previous lookup)
20298      * @private
20299      */
20300     setStartPosition: function(pos) {
20301         var p = pos || Dom.getXY( this.getEl() );
20302         this.deltaSetXY = null;
20303
20304         this.startPageX = p[0];
20305         this.startPageY = p[1];
20306     },
20307
20308     /**
20309      * Add this instance to a group of related drag/drop objects.  All
20310      * instances belong to at least one group, and can belong to as many
20311      * groups as needed.
20312      * @method addToGroup
20313      * @param sGroup {string} the name of the group
20314      */
20315     addToGroup: function(sGroup) {
20316         this.groups[sGroup] = true;
20317         this.DDM.regDragDrop(this, sGroup);
20318     },
20319
20320     /**
20321      * Remove's this instance from the supplied interaction group
20322      * @method removeFromGroup
20323      * @param {string}  sGroup  The group to drop
20324      */
20325     removeFromGroup: function(sGroup) {
20326         if (this.groups[sGroup]) {
20327             delete this.groups[sGroup];
20328         }
20329
20330         this.DDM.removeDDFromGroup(this, sGroup);
20331     },
20332
20333     /**
20334      * Allows you to specify that an element other than the linked element
20335      * will be moved with the cursor during a drag
20336      * @method setDragElId
20337      * @param id {string} the id of the element that will be used to initiate the drag
20338      */
20339     setDragElId: function(id) {
20340         this.dragElId = id;
20341     },
20342
20343     /**
20344      * Allows you to specify a child of the linked element that should be
20345      * used to initiate the drag operation.  An example of this would be if
20346      * you have a content div with text and links.  Clicking anywhere in the
20347      * content area would normally start the drag operation.  Use this method
20348      * to specify that an element inside of the content div is the element
20349      * that starts the drag operation.
20350      * @method setHandleElId
20351      * @param id {string} the id of the element that will be used to
20352      * initiate the drag.
20353      */
20354     setHandleElId: function(id) {
20355         if (typeof id !== "string") {
20356             id = Roo.id(id);
20357         }
20358         this.handleElId = id;
20359         this.DDM.regHandle(this.id, id);
20360     },
20361
20362     /**
20363      * Allows you to set an element outside of the linked element as a drag
20364      * handle
20365      * @method setOuterHandleElId
20366      * @param id the id of the element that will be used to initiate the drag
20367      */
20368     setOuterHandleElId: function(id) {
20369         if (typeof id !== "string") {
20370             id = Roo.id(id);
20371         }
20372         Event.on(id, "mousedown",
20373                 this.handleMouseDown, this);
20374         this.setHandleElId(id);
20375
20376         this.hasOuterHandles = true;
20377     },
20378
20379     /**
20380      * Remove all drag and drop hooks for this element
20381      * @method unreg
20382      */
20383     unreg: function() {
20384         Event.un(this.id, "mousedown",
20385                 this.handleMouseDown);
20386         Event.un(this.id, "touchstart",
20387                 this.handleMouseDown);
20388         this._domRef = null;
20389         this.DDM._remove(this);
20390     },
20391
20392     destroy : function(){
20393         this.unreg();
20394     },
20395
20396     /**
20397      * Returns true if this instance is locked, or the drag drop mgr is locked
20398      * (meaning that all drag/drop is disabled on the page.)
20399      * @method isLocked
20400      * @return {boolean} true if this obj or all drag/drop is locked, else
20401      * false
20402      */
20403     isLocked: function() {
20404         return (this.DDM.isLocked() || this.locked);
20405     },
20406
20407     /**
20408      * Fired when this object is clicked
20409      * @method handleMouseDown
20410      * @param {Event} e
20411      * @param {Roo.dd.DragDrop} oDD the clicked dd object (this dd obj)
20412      * @private
20413      */
20414     handleMouseDown: function(e, oDD){
20415      
20416         if (!Roo.isTouch && this.primaryButtonOnly && e.button != 0) {
20417             //Roo.log('not touch/ button !=0');
20418             return;
20419         }
20420         if (e.browserEvent.touches && e.browserEvent.touches.length != 1) {
20421             return; // double touch..
20422         }
20423         
20424
20425         if (this.isLocked()) {
20426             //Roo.log('locked');
20427             return;
20428         }
20429
20430         this.DDM.refreshCache(this.groups);
20431 //        Roo.log([Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e)]);
20432         var pt = new Roo.lib.Point(Roo.lib.Event.getPageX(e), Roo.lib.Event.getPageY(e));
20433         if (!this.hasOuterHandles && !this.DDM.isOverTarget(pt, this) )  {
20434             //Roo.log('no outer handes or not over target');
20435                 // do nothing.
20436         } else {
20437 //            Roo.log('check validator');
20438             if (this.clickValidator(e)) {
20439 //                Roo.log('validate success');
20440                 // set the initial element position
20441                 this.setStartPosition();
20442
20443
20444                 this.b4MouseDown(e);
20445                 this.onMouseDown(e);
20446
20447                 this.DDM.handleMouseDown(e, this);
20448
20449                 this.DDM.stopEvent(e);
20450             } else {
20451
20452
20453             }
20454         }
20455     },
20456
20457     clickValidator: function(e) {
20458         var target = e.getTarget();
20459         return ( this.isValidHandleChild(target) &&
20460                     (this.id == this.handleElId ||
20461                         this.DDM.handleWasClicked(target, this.id)) );
20462     },
20463
20464     /**
20465      * Allows you to specify a tag name that should not start a drag operation
20466      * when clicked.  This is designed to facilitate embedding links within a
20467      * drag handle that do something other than start the drag.
20468      * @method addInvalidHandleType
20469      * @param {string} tagName the type of element to exclude
20470      */
20471     addInvalidHandleType: function(tagName) {
20472         var type = tagName.toUpperCase();
20473         this.invalidHandleTypes[type] = type;
20474     },
20475
20476     /**
20477      * Lets you to specify an element id for a child of a drag handle
20478      * that should not initiate a drag
20479      * @method addInvalidHandleId
20480      * @param {string} id the element id of the element you wish to ignore
20481      */
20482     addInvalidHandleId: function(id) {
20483         if (typeof id !== "string") {
20484             id = Roo.id(id);
20485         }
20486         this.invalidHandleIds[id] = id;
20487     },
20488
20489     /**
20490      * Lets you specify a css class of elements that will not initiate a drag
20491      * @method addInvalidHandleClass
20492      * @param {string} cssClass the class of the elements you wish to ignore
20493      */
20494     addInvalidHandleClass: function(cssClass) {
20495         this.invalidHandleClasses.push(cssClass);
20496     },
20497
20498     /**
20499      * Unsets an excluded tag name set by addInvalidHandleType
20500      * @method removeInvalidHandleType
20501      * @param {string} tagName the type of element to unexclude
20502      */
20503     removeInvalidHandleType: function(tagName) {
20504         var type = tagName.toUpperCase();
20505         // this.invalidHandleTypes[type] = null;
20506         delete this.invalidHandleTypes[type];
20507     },
20508
20509     /**
20510      * Unsets an invalid handle id
20511      * @method removeInvalidHandleId
20512      * @param {string} id the id of the element to re-enable
20513      */
20514     removeInvalidHandleId: function(id) {
20515         if (typeof id !== "string") {
20516             id = Roo.id(id);
20517         }
20518         delete this.invalidHandleIds[id];
20519     },
20520
20521     /**
20522      * Unsets an invalid css class
20523      * @method removeInvalidHandleClass
20524      * @param {string} cssClass the class of the element(s) you wish to
20525      * re-enable
20526      */
20527     removeInvalidHandleClass: function(cssClass) {
20528         for (var i=0, len=this.invalidHandleClasses.length; i<len; ++i) {
20529             if (this.invalidHandleClasses[i] == cssClass) {
20530                 delete this.invalidHandleClasses[i];
20531             }
20532         }
20533     },
20534
20535     /**
20536      * Checks the tag exclusion list to see if this click should be ignored
20537      * @method isValidHandleChild
20538      * @param {HTMLElement} node the HTMLElement to evaluate
20539      * @return {boolean} true if this is a valid tag type, false if not
20540      */
20541     isValidHandleChild: function(node) {
20542
20543         var valid = true;
20544         // var n = (node.nodeName == "#text") ? node.parentNode : node;
20545         var nodeName;
20546         try {
20547             nodeName = node.nodeName.toUpperCase();
20548         } catch(e) {
20549             nodeName = node.nodeName;
20550         }
20551         valid = valid && !this.invalidHandleTypes[nodeName];
20552         valid = valid && !this.invalidHandleIds[node.id];
20553
20554         for (var i=0, len=this.invalidHandleClasses.length; valid && i<len; ++i) {
20555             valid = !Dom.hasClass(node, this.invalidHandleClasses[i]);
20556         }
20557
20558
20559         return valid;
20560
20561     },
20562
20563     /**
20564      * Create the array of horizontal tick marks if an interval was specified
20565      * in setXConstraint().
20566      * @method setXTicks
20567      * @private
20568      */
20569     setXTicks: function(iStartX, iTickSize) {
20570         this.xTicks = [];
20571         this.xTickSize = iTickSize;
20572
20573         var tickMap = {};
20574
20575         for (var i = this.initPageX; i >= this.minX; i = i - iTickSize) {
20576             if (!tickMap[i]) {
20577                 this.xTicks[this.xTicks.length] = i;
20578                 tickMap[i] = true;
20579             }
20580         }
20581
20582         for (i = this.initPageX; i <= this.maxX; i = i + iTickSize) {
20583             if (!tickMap[i]) {
20584                 this.xTicks[this.xTicks.length] = i;
20585                 tickMap[i] = true;
20586             }
20587         }
20588
20589         this.xTicks.sort(this.DDM.numericSort) ;
20590     },
20591
20592     /**
20593      * Create the array of vertical tick marks if an interval was specified in
20594      * setYConstraint().
20595      * @method setYTicks
20596      * @private
20597      */
20598     setYTicks: function(iStartY, iTickSize) {
20599         this.yTicks = [];
20600         this.yTickSize = iTickSize;
20601
20602         var tickMap = {};
20603
20604         for (var i = this.initPageY; i >= this.minY; i = i - iTickSize) {
20605             if (!tickMap[i]) {
20606                 this.yTicks[this.yTicks.length] = i;
20607                 tickMap[i] = true;
20608             }
20609         }
20610
20611         for (i = this.initPageY; i <= this.maxY; i = i + iTickSize) {
20612             if (!tickMap[i]) {
20613                 this.yTicks[this.yTicks.length] = i;
20614                 tickMap[i] = true;
20615             }
20616         }
20617
20618         this.yTicks.sort(this.DDM.numericSort) ;
20619     },
20620
20621     /**
20622      * By default, the element can be dragged any place on the screen.  Use
20623      * this method to limit the horizontal travel of the element.  Pass in
20624      * 0,0 for the parameters if you want to lock the drag to the y axis.
20625      * @method setXConstraint
20626      * @param {int} iLeft the number of pixels the element can move to the left
20627      * @param {int} iRight the number of pixels the element can move to the
20628      * right
20629      * @param {int} iTickSize optional parameter for specifying that the
20630      * element
20631      * should move iTickSize pixels at a time.
20632      */
20633     setXConstraint: function(iLeft, iRight, iTickSize) {
20634         this.leftConstraint = iLeft;
20635         this.rightConstraint = iRight;
20636
20637         this.minX = this.initPageX - iLeft;
20638         this.maxX = this.initPageX + iRight;
20639         if (iTickSize) { this.setXTicks(this.initPageX, iTickSize); }
20640
20641         this.constrainX = true;
20642     },
20643
20644     /**
20645      * Clears any constraints applied to this instance.  Also clears ticks
20646      * since they can't exist independent of a constraint at this time.
20647      * @method clearConstraints
20648      */
20649     clearConstraints: function() {
20650         this.constrainX = false;
20651         this.constrainY = false;
20652         this.clearTicks();
20653     },
20654
20655     /**
20656      * Clears any tick interval defined for this instance
20657      * @method clearTicks
20658      */
20659     clearTicks: function() {
20660         this.xTicks = null;
20661         this.yTicks = null;
20662         this.xTickSize = 0;
20663         this.yTickSize = 0;
20664     },
20665
20666     /**
20667      * By default, the element can be dragged any place on the screen.  Set
20668      * this to limit the vertical travel of the element.  Pass in 0,0 for the
20669      * parameters if you want to lock the drag to the x axis.
20670      * @method setYConstraint
20671      * @param {int} iUp the number of pixels the element can move up
20672      * @param {int} iDown the number of pixels the element can move down
20673      * @param {int} iTickSize optional parameter for specifying that the
20674      * element should move iTickSize pixels at a time.
20675      */
20676     setYConstraint: function(iUp, iDown, iTickSize) {
20677         this.topConstraint = iUp;
20678         this.bottomConstraint = iDown;
20679
20680         this.minY = this.initPageY - iUp;
20681         this.maxY = this.initPageY + iDown;
20682         if (iTickSize) { this.setYTicks(this.initPageY, iTickSize); }
20683
20684         this.constrainY = true;
20685
20686     },
20687
20688     /**
20689      * resetConstraints must be called if you manually reposition a dd element.
20690      * @method resetConstraints
20691      * @param {boolean} maintainOffset
20692      */
20693     resetConstraints: function() {
20694
20695
20696         // Maintain offsets if necessary
20697         if (this.initPageX || this.initPageX === 0) {
20698             // figure out how much this thing has moved
20699             var dx = (this.maintainOffset) ? this.lastPageX - this.initPageX : 0;
20700             var dy = (this.maintainOffset) ? this.lastPageY - this.initPageY : 0;
20701
20702             this.setInitPosition(dx, dy);
20703
20704         // This is the first time we have detected the element's position
20705         } else {
20706             this.setInitPosition();
20707         }
20708
20709         if (this.constrainX) {
20710             this.setXConstraint( this.leftConstraint,
20711                                  this.rightConstraint,
20712                                  this.xTickSize        );
20713         }
20714
20715         if (this.constrainY) {
20716             this.setYConstraint( this.topConstraint,
20717                                  this.bottomConstraint,
20718                                  this.yTickSize         );
20719         }
20720     },
20721
20722     /**
20723      * Normally the drag element is moved pixel by pixel, but we can specify
20724      * that it move a number of pixels at a time.  This method resolves the
20725      * location when we have it set up like this.
20726      * @method getTick
20727      * @param {int} val where we want to place the object
20728      * @param {int[]} tickArray sorted array of valid points
20729      * @return {int} the closest tick
20730      * @private
20731      */
20732     getTick: function(val, tickArray) {
20733
20734         if (!tickArray) {
20735             // If tick interval is not defined, it is effectively 1 pixel,
20736             // so we return the value passed to us.
20737             return val;
20738         } else if (tickArray[0] >= val) {
20739             // The value is lower than the first tick, so we return the first
20740             // tick.
20741             return tickArray[0];
20742         } else {
20743             for (var i=0, len=tickArray.length; i<len; ++i) {
20744                 var next = i + 1;
20745                 if (tickArray[next] && tickArray[next] >= val) {
20746                     var diff1 = val - tickArray[i];
20747                     var diff2 = tickArray[next] - val;
20748                     return (diff2 > diff1) ? tickArray[i] : tickArray[next];
20749                 }
20750             }
20751
20752             // The value is larger than the last tick, so we return the last
20753             // tick.
20754             return tickArray[tickArray.length - 1];
20755         }
20756     },
20757
20758     /**
20759      * toString method
20760      * @method toString
20761      * @return {string} string representation of the dd obj
20762      */
20763     toString: function() {
20764         return ("DragDrop " + this.id);
20765     }
20766
20767 });
20768
20769 })();
20770 /*
20771  * Based on:
20772  * Ext JS Library 1.1.1
20773  * Copyright(c) 2006-2007, Ext JS, LLC.
20774  *
20775  * Originally Released Under LGPL - original licence link has changed is not relivant.
20776  *
20777  * Fork - LGPL
20778  * <script type="text/javascript">
20779  */
20780
20781
20782 /**
20783  * The drag and drop utility provides a framework for building drag and drop
20784  * applications.  In addition to enabling drag and drop for specific elements,
20785  * the drag and drop elements are tracked by the manager class, and the
20786  * interactions between the various elements are tracked during the drag and
20787  * the implementing code is notified about these important moments.
20788  */
20789
20790 // Only load the library once.  Rewriting the manager class would orphan
20791 // existing drag and drop instances.
20792 if (!Roo.dd.DragDropMgr) {
20793
20794 /**
20795  * @class Roo.dd.DragDropMgr
20796  * DragDropMgr is a singleton that tracks the element interaction for
20797  * all DragDrop items in the window.  Generally, you will not call
20798  * this class directly, but it does have helper methods that could
20799  * be useful in your DragDrop implementations.
20800  * @static
20801  */
20802 Roo.dd.DragDropMgr = function() {
20803
20804     var Event = Roo.EventManager;
20805
20806     return {
20807
20808         /**
20809          * Two dimensional Array of registered DragDrop objects.  The first
20810          * dimension is the DragDrop item group, the second the DragDrop
20811          * object.
20812          * @property ids
20813          * @type {string: string}
20814          * @private
20815          * @static
20816          */
20817         ids: {},
20818
20819         /**
20820          * Array of element ids defined as drag handles.  Used to determine
20821          * if the element that generated the mousedown event is actually the
20822          * handle and not the html element itself.
20823          * @property handleIds
20824          * @type {string: string}
20825          * @private
20826          * @static
20827          */
20828         handleIds: {},
20829
20830         /**
20831          * the DragDrop object that is currently being dragged
20832          * @property dragCurrent
20833          * @type DragDrop
20834          * @private
20835          * @static
20836          **/
20837         dragCurrent: null,
20838
20839         /**
20840          * the DragDrop object(s) that are being hovered over
20841          * @property dragOvers
20842          * @type Array
20843          * @private
20844          * @static
20845          */
20846         dragOvers: {},
20847
20848         /**
20849          * the X distance between the cursor and the object being dragged
20850          * @property deltaX
20851          * @type int
20852          * @private
20853          * @static
20854          */
20855         deltaX: 0,
20856
20857         /**
20858          * the Y distance between the cursor and the object being dragged
20859          * @property deltaY
20860          * @type int
20861          * @private
20862          * @static
20863          */
20864         deltaY: 0,
20865
20866         /**
20867          * Flag to determine if we should prevent the default behavior of the
20868          * events we define. By default this is true, but this can be set to
20869          * false if you need the default behavior (not recommended)
20870          * @property preventDefault
20871          * @type boolean
20872          * @static
20873          */
20874         preventDefault: true,
20875
20876         /**
20877          * Flag to determine if we should stop the propagation of the events
20878          * we generate. This is true by default but you may want to set it to
20879          * false if the html element contains other features that require the
20880          * mouse click.
20881          * @property stopPropagation
20882          * @type boolean
20883          * @static
20884          */
20885         stopPropagation: true,
20886
20887         /**
20888          * Internal flag that is set to true when drag and drop has been
20889          * intialized
20890          * @property initialized
20891          * @private
20892          * @static
20893          */
20894         initalized: false,
20895
20896         /**
20897          * All drag and drop can be disabled.
20898          * @property locked
20899          * @private
20900          * @static
20901          */
20902         locked: false,
20903
20904         /**
20905          * Called the first time an element is registered.
20906          * @method init
20907          * @private
20908          * @static
20909          */
20910         init: function() {
20911             this.initialized = true;
20912         },
20913
20914         /**
20915          * In point mode, drag and drop interaction is defined by the
20916          * location of the cursor during the drag/drop
20917          * @property POINT
20918          * @type int
20919          * @static
20920          */
20921         POINT: 0,
20922
20923         /**
20924          * In intersect mode, drag and drop interactio nis defined by the
20925          * overlap of two or more drag and drop objects.
20926          * @property INTERSECT
20927          * @type int
20928          * @static
20929          */
20930         INTERSECT: 1,
20931
20932         /**
20933          * The current drag and drop mode.  Default: POINT
20934          * @property mode
20935          * @type int
20936          * @static
20937          */
20938         mode: 0,
20939
20940         /**
20941          * Runs method on all drag and drop objects
20942          * @method _execOnAll
20943          * @private
20944          * @static
20945          */
20946         _execOnAll: function(sMethod, args) {
20947             for (var i in this.ids) {
20948                 for (var j in this.ids[i]) {
20949                     var oDD = this.ids[i][j];
20950                     if (! this.isTypeOfDD(oDD)) {
20951                         continue;
20952                     }
20953                     oDD[sMethod].apply(oDD, args);
20954                 }
20955             }
20956         },
20957
20958         /**
20959          * Drag and drop initialization.  Sets up the global event handlers
20960          * @method _onLoad
20961          * @private
20962          * @static
20963          */
20964         _onLoad: function() {
20965
20966             this.init();
20967
20968             if (!Roo.isTouch) {
20969                 Event.on(document, "mouseup",   this.handleMouseUp, this, true);
20970                 Event.on(document, "mousemove", this.handleMouseMove, this, true);
20971             }
20972             Event.on(document, "touchend",   this.handleMouseUp, this, true);
20973             Event.on(document, "touchmove", this.handleMouseMove, this, true);
20974             
20975             Event.on(window,   "unload",    this._onUnload, this, true);
20976             Event.on(window,   "resize",    this._onResize, this, true);
20977             // Event.on(window,   "mouseout",    this._test);
20978
20979         },
20980
20981         /**
20982          * Reset constraints on all drag and drop objs
20983          * @method _onResize
20984          * @private
20985          * @static
20986          */
20987         _onResize: function(e) {
20988             this._execOnAll("resetConstraints", []);
20989         },
20990
20991         /**
20992          * Lock all drag and drop functionality
20993          * @method lock
20994          * @static
20995          */
20996         lock: function() { this.locked = true; },
20997
20998         /**
20999          * Unlock all drag and drop functionality
21000          * @method unlock
21001          * @static
21002          */
21003         unlock: function() { this.locked = false; },
21004
21005         /**
21006          * Is drag and drop locked?
21007          * @method isLocked
21008          * @return {boolean} True if drag and drop is locked, false otherwise.
21009          * @static
21010          */
21011         isLocked: function() { return this.locked; },
21012
21013         /**
21014          * Location cache that is set for all drag drop objects when a drag is
21015          * initiated, cleared when the drag is finished.
21016          * @property locationCache
21017          * @private
21018          * @static
21019          */
21020         locationCache: {},
21021
21022         /**
21023          * Set useCache to false if you want to force object the lookup of each
21024          * drag and drop linked element constantly during a drag.
21025          * @property useCache
21026          * @type boolean
21027          * @static
21028          */
21029         useCache: true,
21030
21031         /**
21032          * The number of pixels that the mouse needs to move after the
21033          * mousedown before the drag is initiated.  Default=3;
21034          * @property clickPixelThresh
21035          * @type int
21036          * @static
21037          */
21038         clickPixelThresh: 3,
21039
21040         /**
21041          * The number of milliseconds after the mousedown event to initiate the
21042          * drag if we don't get a mouseup event. Default=1000
21043          * @property clickTimeThresh
21044          * @type int
21045          * @static
21046          */
21047         clickTimeThresh: 350,
21048
21049         /**
21050          * Flag that indicates that either the drag pixel threshold or the
21051          * mousdown time threshold has been met
21052          * @property dragThreshMet
21053          * @type boolean
21054          * @private
21055          * @static
21056          */
21057         dragThreshMet: false,
21058
21059         /**
21060          * Timeout used for the click time threshold
21061          * @property clickTimeout
21062          * @type Object
21063          * @private
21064          * @static
21065          */
21066         clickTimeout: null,
21067
21068         /**
21069          * The X position of the mousedown event stored for later use when a
21070          * drag threshold is met.
21071          * @property startX
21072          * @type int
21073          * @private
21074          * @static
21075          */
21076         startX: 0,
21077
21078         /**
21079          * The Y position of the mousedown event stored for later use when a
21080          * drag threshold is met.
21081          * @property startY
21082          * @type int
21083          * @private
21084          * @static
21085          */
21086         startY: 0,
21087
21088         /**
21089          * Each DragDrop instance must be registered with the DragDropMgr.
21090          * This is executed in DragDrop.init()
21091          * @method regDragDrop
21092          * @param {DragDrop} oDD the DragDrop object to register
21093          * @param {String} sGroup the name of the group this element belongs to
21094          * @static
21095          */
21096         regDragDrop: function(oDD, sGroup) {
21097             if (!this.initialized) { this.init(); }
21098
21099             if (!this.ids[sGroup]) {
21100                 this.ids[sGroup] = {};
21101             }
21102             this.ids[sGroup][oDD.id] = oDD;
21103         },
21104
21105         /**
21106          * Removes the supplied dd instance from the supplied group. Executed
21107          * by DragDrop.removeFromGroup, so don't call this function directly.
21108          * @method removeDDFromGroup
21109          * @private
21110          * @static
21111          */
21112         removeDDFromGroup: function(oDD, sGroup) {
21113             if (!this.ids[sGroup]) {
21114                 this.ids[sGroup] = {};
21115             }
21116
21117             var obj = this.ids[sGroup];
21118             if (obj && obj[oDD.id]) {
21119                 delete obj[oDD.id];
21120             }
21121         },
21122
21123         /**
21124          * Unregisters a drag and drop item.  This is executed in
21125          * DragDrop.unreg, use that method instead of calling this directly.
21126          * @method _remove
21127          * @private
21128          * @static
21129          */
21130         _remove: function(oDD) {
21131             for (var g in oDD.groups) {
21132                 if (g && this.ids[g][oDD.id]) {
21133                     delete this.ids[g][oDD.id];
21134                 }
21135             }
21136             delete this.handleIds[oDD.id];
21137         },
21138
21139         /**
21140          * Each DragDrop handle element must be registered.  This is done
21141          * automatically when executing DragDrop.setHandleElId()
21142          * @method regHandle
21143          * @param {String} sDDId the DragDrop id this element is a handle for
21144          * @param {String} sHandleId the id of the element that is the drag
21145          * handle
21146          * @static
21147          */
21148         regHandle: function(sDDId, sHandleId) {
21149             if (!this.handleIds[sDDId]) {
21150                 this.handleIds[sDDId] = {};
21151             }
21152             this.handleIds[sDDId][sHandleId] = sHandleId;
21153         },
21154
21155         /**
21156          * Utility function to determine if a given element has been
21157          * registered as a drag drop item.
21158          * @method isDragDrop
21159          * @param {String} id the element id to check
21160          * @return {boolean} true if this element is a DragDrop item,
21161          * false otherwise
21162          * @static
21163          */
21164         isDragDrop: function(id) {
21165             return ( this.getDDById(id) ) ? true : false;
21166         },
21167
21168         /**
21169          * Returns the drag and drop instances that are in all groups the
21170          * passed in instance belongs to.
21171          * @method getRelated
21172          * @param {DragDrop} p_oDD the obj to get related data for
21173          * @param {boolean} bTargetsOnly if true, only return targetable objs
21174          * @return {DragDrop[]} the related instances
21175          * @static
21176          */
21177         getRelated: function(p_oDD, bTargetsOnly) {
21178             var oDDs = [];
21179             for (var i in p_oDD.groups) {
21180                 for (j in this.ids[i]) {
21181                     var dd = this.ids[i][j];
21182                     if (! this.isTypeOfDD(dd)) {
21183                         continue;
21184                     }
21185                     if (!bTargetsOnly || dd.isTarget) {
21186                         oDDs[oDDs.length] = dd;
21187                     }
21188                 }
21189             }
21190
21191             return oDDs;
21192         },
21193
21194         /**
21195          * Returns true if the specified dd target is a legal target for
21196          * the specifice drag obj
21197          * @method isLegalTarget
21198          * @param {DragDrop} the drag obj
21199          * @param {DragDrop} the target
21200          * @return {boolean} true if the target is a legal target for the
21201          * dd obj
21202          * @static
21203          */
21204         isLegalTarget: function (oDD, oTargetDD) {
21205             var targets = this.getRelated(oDD, true);
21206             for (var i=0, len=targets.length;i<len;++i) {
21207                 if (targets[i].id == oTargetDD.id) {
21208                     return true;
21209                 }
21210             }
21211
21212             return false;
21213         },
21214
21215         /**
21216          * My goal is to be able to transparently determine if an object is
21217          * typeof DragDrop, and the exact subclass of DragDrop.  typeof
21218          * returns "object", oDD.constructor.toString() always returns
21219          * "DragDrop" and not the name of the subclass.  So for now it just
21220          * evaluates a well-known variable in DragDrop.
21221          * @method isTypeOfDD
21222          * @param {Object} the object to evaluate
21223          * @return {boolean} true if typeof oDD = DragDrop
21224          * @static
21225          */
21226         isTypeOfDD: function (oDD) {
21227             return (oDD && oDD.__ygDragDrop);
21228         },
21229
21230         /**
21231          * Utility function to determine if a given element has been
21232          * registered as a drag drop handle for the given Drag Drop object.
21233          * @method isHandle
21234          * @param {String} id the element id to check
21235          * @return {boolean} true if this element is a DragDrop handle, false
21236          * otherwise
21237          * @static
21238          */
21239         isHandle: function(sDDId, sHandleId) {
21240             return ( this.handleIds[sDDId] &&
21241                             this.handleIds[sDDId][sHandleId] );
21242         },
21243
21244         /**
21245          * Returns the DragDrop instance for a given id
21246          * @method getDDById
21247          * @param {String} id the id of the DragDrop object
21248          * @return {DragDrop} the drag drop object, null if it is not found
21249          * @static
21250          */
21251         getDDById: function(id) {
21252             for (var i in this.ids) {
21253                 if (this.ids[i][id]) {
21254                     return this.ids[i][id];
21255                 }
21256             }
21257             return null;
21258         },
21259
21260         /**
21261          * Fired after a registered DragDrop object gets the mousedown event.
21262          * Sets up the events required to track the object being dragged
21263          * @method handleMouseDown
21264          * @param {Event} e the event
21265          * @param oDD the DragDrop object being dragged
21266          * @private
21267          * @static
21268          */
21269         handleMouseDown: function(e, oDD) {
21270             if(Roo.QuickTips){
21271                 Roo.QuickTips.disable();
21272             }
21273             this.currentTarget = e.getTarget();
21274
21275             this.dragCurrent = oDD;
21276
21277             var el = oDD.getEl();
21278
21279             // track start position
21280             this.startX = e.getPageX();
21281             this.startY = e.getPageY();
21282
21283             this.deltaX = this.startX - el.offsetLeft;
21284             this.deltaY = this.startY - el.offsetTop;
21285
21286             this.dragThreshMet = false;
21287
21288             this.clickTimeout = setTimeout(
21289                     function() {
21290                         var DDM = Roo.dd.DDM;
21291                         DDM.startDrag(DDM.startX, DDM.startY);
21292                     },
21293                     this.clickTimeThresh );
21294         },
21295
21296         /**
21297          * Fired when either the drag pixel threshol or the mousedown hold
21298          * time threshold has been met.
21299          * @method startDrag
21300          * @param x {int} the X position of the original mousedown
21301          * @param y {int} the Y position of the original mousedown
21302          * @static
21303          */
21304         startDrag: function(x, y) {
21305             clearTimeout(this.clickTimeout);
21306             if (this.dragCurrent) {
21307                 this.dragCurrent.b4StartDrag(x, y);
21308                 this.dragCurrent.startDrag(x, y);
21309             }
21310             this.dragThreshMet = true;
21311         },
21312
21313         /**
21314          * Internal function to handle the mouseup event.  Will be invoked
21315          * from the context of the document.
21316          * @method handleMouseUp
21317          * @param {Event} e the event
21318          * @private
21319          * @static
21320          */
21321         handleMouseUp: function(e) {
21322
21323             if(Roo.QuickTips){
21324                 Roo.QuickTips.enable();
21325             }
21326             if (! this.dragCurrent) {
21327                 return;
21328             }
21329
21330             clearTimeout(this.clickTimeout);
21331
21332             if (this.dragThreshMet) {
21333                 this.fireEvents(e, true);
21334             } else {
21335             }
21336
21337             this.stopDrag(e);
21338
21339             this.stopEvent(e);
21340         },
21341
21342         /**
21343          * Utility to stop event propagation and event default, if these
21344          * features are turned on.
21345          * @method stopEvent
21346          * @param {Event} e the event as returned by this.getEvent()
21347          * @static
21348          */
21349         stopEvent: function(e){
21350             if(this.stopPropagation) {
21351                 e.stopPropagation();
21352             }
21353
21354             if (this.preventDefault) {
21355                 e.preventDefault();
21356             }
21357         },
21358
21359         /**
21360          * Internal function to clean up event handlers after the drag
21361          * operation is complete
21362          * @method stopDrag
21363          * @param {Event} e the event
21364          * @private
21365          * @static
21366          */
21367         stopDrag: function(e) {
21368             // Fire the drag end event for the item that was dragged
21369             if (this.dragCurrent) {
21370                 if (this.dragThreshMet) {
21371                     this.dragCurrent.b4EndDrag(e);
21372                     this.dragCurrent.endDrag(e);
21373                 }
21374
21375                 this.dragCurrent.onMouseUp(e);
21376             }
21377
21378             this.dragCurrent = null;
21379             this.dragOvers = {};
21380         },
21381
21382         /**
21383          * Internal function to handle the mousemove event.  Will be invoked
21384          * from the context of the html element.
21385          *
21386          * @TODO figure out what we can do about mouse events lost when the
21387          * user drags objects beyond the window boundary.  Currently we can
21388          * detect this in internet explorer by verifying that the mouse is
21389          * down during the mousemove event.  Firefox doesn't give us the
21390          * button state on the mousemove event.
21391          * @method handleMouseMove
21392          * @param {Event} e the event
21393          * @private
21394          * @static
21395          */
21396         handleMouseMove: function(e) {
21397             if (! this.dragCurrent) {
21398                 return true;
21399             }
21400
21401             // var button = e.which || e.button;
21402
21403             // check for IE mouseup outside of page boundary
21404             if (Roo.isIE && (e.button !== 0 && e.button !== 1 && e.button !== 2)) {
21405                 this.stopEvent(e);
21406                 return this.handleMouseUp(e);
21407             }
21408
21409             if (!this.dragThreshMet) {
21410                 var diffX = Math.abs(this.startX - e.getPageX());
21411                 var diffY = Math.abs(this.startY - e.getPageY());
21412                 if (diffX > this.clickPixelThresh ||
21413                             diffY > this.clickPixelThresh) {
21414                     this.startDrag(this.startX, this.startY);
21415                 }
21416             }
21417
21418             if (this.dragThreshMet) {
21419                 this.dragCurrent.b4Drag(e);
21420                 this.dragCurrent.onDrag(e);
21421                 if(!this.dragCurrent.moveOnly){
21422                     this.fireEvents(e, false);
21423                 }
21424             }
21425
21426             this.stopEvent(e);
21427
21428             return true;
21429         },
21430
21431         /**
21432          * Iterates over all of the DragDrop elements to find ones we are
21433          * hovering over or dropping on
21434          * @method fireEvents
21435          * @param {Event} e the event
21436          * @param {boolean} isDrop is this a drop op or a mouseover op?
21437          * @private
21438          * @static
21439          */
21440         fireEvents: function(e, isDrop) {
21441             var dc = this.dragCurrent;
21442
21443             // If the user did the mouse up outside of the window, we could
21444             // get here even though we have ended the drag.
21445             if (!dc || dc.isLocked()) {
21446                 return;
21447             }
21448
21449             var pt = e.getPoint();
21450
21451             // cache the previous dragOver array
21452             var oldOvers = [];
21453
21454             var outEvts   = [];
21455             var overEvts  = [];
21456             var dropEvts  = [];
21457             var enterEvts = [];
21458
21459             // Check to see if the object(s) we were hovering over is no longer
21460             // being hovered over so we can fire the onDragOut event
21461             for (var i in this.dragOvers) {
21462
21463                 var ddo = this.dragOvers[i];
21464
21465                 if (! this.isTypeOfDD(ddo)) {
21466                     continue;
21467                 }
21468
21469                 if (! this.isOverTarget(pt, ddo, this.mode)) {
21470                     outEvts.push( ddo );
21471                 }
21472
21473                 oldOvers[i] = true;
21474                 delete this.dragOvers[i];
21475             }
21476
21477             for (var sGroup in dc.groups) {
21478
21479                 if ("string" != typeof sGroup) {
21480                     continue;
21481                 }
21482
21483                 for (i in this.ids[sGroup]) {
21484                     var oDD = this.ids[sGroup][i];
21485                     if (! this.isTypeOfDD(oDD)) {
21486                         continue;
21487                     }
21488
21489                     if (oDD.isTarget && !oDD.isLocked() && oDD != dc) {
21490                         if (this.isOverTarget(pt, oDD, this.mode)) {
21491                             // look for drop interactions
21492                             if (isDrop) {
21493                                 dropEvts.push( oDD );
21494                             // look for drag enter and drag over interactions
21495                             } else {
21496
21497                                 // initial drag over: dragEnter fires
21498                                 if (!oldOvers[oDD.id]) {
21499                                     enterEvts.push( oDD );
21500                                 // subsequent drag overs: dragOver fires
21501                                 } else {
21502                                     overEvts.push( oDD );
21503                                 }
21504
21505                                 this.dragOvers[oDD.id] = oDD;
21506                             }
21507                         }
21508                     }
21509                 }
21510             }
21511
21512             if (this.mode) {
21513                 if (outEvts.length) {
21514                     dc.b4DragOut(e, outEvts);
21515                     dc.onDragOut(e, outEvts);
21516                 }
21517
21518                 if (enterEvts.length) {
21519                     dc.onDragEnter(e, enterEvts);
21520                 }
21521
21522                 if (overEvts.length) {
21523                     dc.b4DragOver(e, overEvts);
21524                     dc.onDragOver(e, overEvts);
21525                 }
21526
21527                 if (dropEvts.length) {
21528                     dc.b4DragDrop(e, dropEvts);
21529                     dc.onDragDrop(e, dropEvts);
21530                 }
21531
21532             } else {
21533                 // fire dragout events
21534                 var len = 0;
21535                 for (i=0, len=outEvts.length; i<len; ++i) {
21536                     dc.b4DragOut(e, outEvts[i].id);
21537                     dc.onDragOut(e, outEvts[i].id);
21538                 }
21539
21540                 // fire enter events
21541                 for (i=0,len=enterEvts.length; i<len; ++i) {
21542                     // dc.b4DragEnter(e, oDD.id);
21543                     dc.onDragEnter(e, enterEvts[i].id);
21544                 }
21545
21546                 // fire over events
21547                 for (i=0,len=overEvts.length; i<len; ++i) {
21548                     dc.b4DragOver(e, overEvts[i].id);
21549                     dc.onDragOver(e, overEvts[i].id);
21550                 }
21551
21552                 // fire drop events
21553                 for (i=0, len=dropEvts.length; i<len; ++i) {
21554                     dc.b4DragDrop(e, dropEvts[i].id);
21555                     dc.onDragDrop(e, dropEvts[i].id);
21556                 }
21557
21558             }
21559
21560             // notify about a drop that did not find a target
21561             if (isDrop && !dropEvts.length) {
21562                 dc.onInvalidDrop(e);
21563             }
21564
21565         },
21566
21567         /**
21568          * Helper function for getting the best match from the list of drag
21569          * and drop objects returned by the drag and drop events when we are
21570          * in INTERSECT mode.  It returns either the first object that the
21571          * cursor is over, or the object that has the greatest overlap with
21572          * the dragged element.
21573          * @method getBestMatch
21574          * @param  {DragDrop[]} dds The array of drag and drop objects
21575          * targeted
21576          * @return {DragDrop}       The best single match
21577          * @static
21578          */
21579         getBestMatch: function(dds) {
21580             var winner = null;
21581             // Return null if the input is not what we expect
21582             //if (!dds || !dds.length || dds.length == 0) {
21583                // winner = null;
21584             // If there is only one item, it wins
21585             //} else if (dds.length == 1) {
21586
21587             var len = dds.length;
21588
21589             if (len == 1) {
21590                 winner = dds[0];
21591             } else {
21592                 // Loop through the targeted items
21593                 for (var i=0; i<len; ++i) {
21594                     var dd = dds[i];
21595                     // If the cursor is over the object, it wins.  If the
21596                     // cursor is over multiple matches, the first one we come
21597                     // to wins.
21598                     if (dd.cursorIsOver) {
21599                         winner = dd;
21600                         break;
21601                     // Otherwise the object with the most overlap wins
21602                     } else {
21603                         if (!winner ||
21604                             winner.overlap.getArea() < dd.overlap.getArea()) {
21605                             winner = dd;
21606                         }
21607                     }
21608                 }
21609             }
21610
21611             return winner;
21612         },
21613
21614         /**
21615          * Refreshes the cache of the top-left and bottom-right points of the
21616          * drag and drop objects in the specified group(s).  This is in the
21617          * format that is stored in the drag and drop instance, so typical
21618          * usage is:
21619          * <code>
21620          * Roo.dd.DragDropMgr.refreshCache(ddinstance.groups);
21621          * </code>
21622          * Alternatively:
21623          * <code>
21624          * Roo.dd.DragDropMgr.refreshCache({group1:true, group2:true});
21625          * </code>
21626          * @TODO this really should be an indexed array.  Alternatively this
21627          * method could accept both.
21628          * @method refreshCache
21629          * @param {Object} groups an associative array of groups to refresh
21630          * @static
21631          */
21632         refreshCache: function(groups) {
21633             for (var sGroup in groups) {
21634                 if ("string" != typeof sGroup) {
21635                     continue;
21636                 }
21637                 for (var i in this.ids[sGroup]) {
21638                     var oDD = this.ids[sGroup][i];
21639
21640                     if (this.isTypeOfDD(oDD)) {
21641                     // if (this.isTypeOfDD(oDD) && oDD.isTarget) {
21642                         var loc = this.getLocation(oDD);
21643                         if (loc) {
21644                             this.locationCache[oDD.id] = loc;
21645                         } else {
21646                             delete this.locationCache[oDD.id];
21647                             // this will unregister the drag and drop object if
21648                             // the element is not in a usable state
21649                             // oDD.unreg();
21650                         }
21651                     }
21652                 }
21653             }
21654         },
21655
21656         /**
21657          * This checks to make sure an element exists and is in the DOM.  The
21658          * main purpose is to handle cases where innerHTML is used to remove
21659          * drag and drop objects from the DOM.  IE provides an 'unspecified
21660          * error' when trying to access the offsetParent of such an element
21661          * @method verifyEl
21662          * @param {HTMLElement} el the element to check
21663          * @return {boolean} true if the element looks usable
21664          * @static
21665          */
21666         verifyEl: function(el) {
21667             if (el) {
21668                 var parent;
21669                 if(Roo.isIE){
21670                     try{
21671                         parent = el.offsetParent;
21672                     }catch(e){}
21673                 }else{
21674                     parent = el.offsetParent;
21675                 }
21676                 if (parent) {
21677                     return true;
21678                 }
21679             }
21680
21681             return false;
21682         },
21683
21684         /**
21685          * Returns a Region object containing the drag and drop element's position
21686          * and size, including the padding configured for it
21687          * @method getLocation
21688          * @param {DragDrop} oDD the drag and drop object to get the
21689          *                       location for
21690          * @return {Roo.lib.Region} a Region object representing the total area
21691          *                             the element occupies, including any padding
21692          *                             the instance is configured for.
21693          * @static
21694          */
21695         getLocation: function(oDD) {
21696             if (! this.isTypeOfDD(oDD)) {
21697                 return null;
21698             }
21699
21700             var el = oDD.getEl(), pos, x1, x2, y1, y2, t, r, b, l;
21701
21702             try {
21703                 pos= Roo.lib.Dom.getXY(el);
21704             } catch (e) { }
21705
21706             if (!pos) {
21707                 return null;
21708             }
21709
21710             x1 = pos[0];
21711             x2 = x1 + el.offsetWidth;
21712             y1 = pos[1];
21713             y2 = y1 + el.offsetHeight;
21714
21715             t = y1 - oDD.padding[0];
21716             r = x2 + oDD.padding[1];
21717             b = y2 + oDD.padding[2];
21718             l = x1 - oDD.padding[3];
21719
21720             return new Roo.lib.Region( t, r, b, l );
21721         },
21722
21723         /**
21724          * Checks the cursor location to see if it over the target
21725          * @method isOverTarget
21726          * @param {Roo.lib.Point} pt The point to evaluate
21727          * @param {DragDrop} oTarget the DragDrop object we are inspecting
21728          * @return {boolean} true if the mouse is over the target
21729          * @private
21730          * @static
21731          */
21732         isOverTarget: function(pt, oTarget, intersect) {
21733             // use cache if available
21734             var loc = this.locationCache[oTarget.id];
21735             if (!loc || !this.useCache) {
21736                 loc = this.getLocation(oTarget);
21737                 this.locationCache[oTarget.id] = loc;
21738
21739             }
21740
21741             if (!loc) {
21742                 return false;
21743             }
21744
21745             oTarget.cursorIsOver = loc.contains( pt );
21746
21747             // DragDrop is using this as a sanity check for the initial mousedown
21748             // in this case we are done.  In POINT mode, if the drag obj has no
21749             // contraints, we are also done. Otherwise we need to evaluate the
21750             // location of the target as related to the actual location of the
21751             // dragged element.
21752             var dc = this.dragCurrent;
21753             if (!dc || !dc.getTargetCoord ||
21754                     (!intersect && !dc.constrainX && !dc.constrainY)) {
21755                 return oTarget.cursorIsOver;
21756             }
21757
21758             oTarget.overlap = null;
21759
21760             // Get the current location of the drag element, this is the
21761             // location of the mouse event less the delta that represents
21762             // where the original mousedown happened on the element.  We
21763             // need to consider constraints and ticks as well.
21764             var pos = dc.getTargetCoord(pt.x, pt.y);
21765
21766             var el = dc.getDragEl();
21767             var curRegion = new Roo.lib.Region( pos.y,
21768                                                    pos.x + el.offsetWidth,
21769                                                    pos.y + el.offsetHeight,
21770                                                    pos.x );
21771
21772             var overlap = curRegion.intersect(loc);
21773
21774             if (overlap) {
21775                 oTarget.overlap = overlap;
21776                 return (intersect) ? true : oTarget.cursorIsOver;
21777             } else {
21778                 return false;
21779             }
21780         },
21781
21782         /**
21783          * unload event handler
21784          * @method _onUnload
21785          * @private
21786          * @static
21787          */
21788         _onUnload: function(e, me) {
21789             Roo.dd.DragDropMgr.unregAll();
21790         },
21791
21792         /**
21793          * Cleans up the drag and drop events and objects.
21794          * @method unregAll
21795          * @private
21796          * @static
21797          */
21798         unregAll: function() {
21799
21800             if (this.dragCurrent) {
21801                 this.stopDrag();
21802                 this.dragCurrent = null;
21803             }
21804
21805             this._execOnAll("unreg", []);
21806
21807             for (i in this.elementCache) {
21808                 delete this.elementCache[i];
21809             }
21810
21811             this.elementCache = {};
21812             this.ids = {};
21813         },
21814
21815         /**
21816          * A cache of DOM elements
21817          * @property elementCache
21818          * @private
21819          * @static
21820          */
21821         elementCache: {},
21822
21823         /**
21824          * Get the wrapper for the DOM element specified
21825          * @method getElWrapper
21826          * @param {String} id the id of the element to get
21827          * @return {Roo.dd.DDM.ElementWrapper} the wrapped element
21828          * @private
21829          * @deprecated This wrapper isn't that useful
21830          * @static
21831          */
21832         getElWrapper: function(id) {
21833             var oWrapper = this.elementCache[id];
21834             if (!oWrapper || !oWrapper.el) {
21835                 oWrapper = this.elementCache[id] =
21836                     new this.ElementWrapper(Roo.getDom(id));
21837             }
21838             return oWrapper;
21839         },
21840
21841         /**
21842          * Returns the actual DOM element
21843          * @method getElement
21844          * @param {String} id the id of the elment to get
21845          * @return {Object} The element
21846          * @deprecated use Roo.getDom instead
21847          * @static
21848          */
21849         getElement: function(id) {
21850             return Roo.getDom(id);
21851         },
21852
21853         /**
21854          * Returns the style property for the DOM element (i.e.,
21855          * document.getElById(id).style)
21856          * @method getCss
21857          * @param {String} id the id of the elment to get
21858          * @return {Object} The style property of the element
21859          * @deprecated use Roo.getDom instead
21860          * @static
21861          */
21862         getCss: function(id) {
21863             var el = Roo.getDom(id);
21864             return (el) ? el.style : null;
21865         },
21866
21867         /**
21868          * Inner class for cached elements
21869          * @class DragDropMgr.ElementWrapper
21870          * @for DragDropMgr
21871          * @private
21872          * @deprecated
21873          */
21874         ElementWrapper: function(el) {
21875                 /**
21876                  * The element
21877                  * @property el
21878                  */
21879                 this.el = el || null;
21880                 /**
21881                  * The element id
21882                  * @property id
21883                  */
21884                 this.id = this.el && el.id;
21885                 /**
21886                  * A reference to the style property
21887                  * @property css
21888                  */
21889                 this.css = this.el && el.style;
21890             },
21891
21892         /**
21893          * Returns the X position of an html element
21894          * @method getPosX
21895          * @param el the element for which to get the position
21896          * @return {int} the X coordinate
21897          * @for DragDropMgr
21898          * @deprecated use Roo.lib.Dom.getX instead
21899          * @static
21900          */
21901         getPosX: function(el) {
21902             return Roo.lib.Dom.getX(el);
21903         },
21904
21905         /**
21906          * Returns the Y position of an html element
21907          * @method getPosY
21908          * @param el the element for which to get the position
21909          * @return {int} the Y coordinate
21910          * @deprecated use Roo.lib.Dom.getY instead
21911          * @static
21912          */
21913         getPosY: function(el) {
21914             return Roo.lib.Dom.getY(el);
21915         },
21916
21917         /**
21918          * Swap two nodes.  In IE, we use the native method, for others we
21919          * emulate the IE behavior
21920          * @method swapNode
21921          * @param n1 the first node to swap
21922          * @param n2 the other node to swap
21923          * @static
21924          */
21925         swapNode: function(n1, n2) {
21926             if (n1.swapNode) {
21927                 n1.swapNode(n2);
21928             } else {
21929                 var p = n2.parentNode;
21930                 var s = n2.nextSibling;
21931
21932                 if (s == n1) {
21933                     p.insertBefore(n1, n2);
21934                 } else if (n2 == n1.nextSibling) {
21935                     p.insertBefore(n2, n1);
21936                 } else {
21937                     n1.parentNode.replaceChild(n2, n1);
21938                     p.insertBefore(n1, s);
21939                 }
21940             }
21941         },
21942
21943         /**
21944          * Returns the current scroll position
21945          * @method getScroll
21946          * @private
21947          * @static
21948          */
21949         getScroll: function () {
21950             var t, l, dde=document.documentElement, db=document.body;
21951             if (dde && (dde.scrollTop || dde.scrollLeft)) {
21952                 t = dde.scrollTop;
21953                 l = dde.scrollLeft;
21954             } else if (db) {
21955                 t = db.scrollTop;
21956                 l = db.scrollLeft;
21957             } else {
21958
21959             }
21960             return { top: t, left: l };
21961         },
21962
21963         /**
21964          * Returns the specified element style property
21965          * @method getStyle
21966          * @param {HTMLElement} el          the element
21967          * @param {string}      styleProp   the style property
21968          * @return {string} The value of the style property
21969          * @deprecated use Roo.lib.Dom.getStyle
21970          * @static
21971          */
21972         getStyle: function(el, styleProp) {
21973             return Roo.fly(el).getStyle(styleProp);
21974         },
21975
21976         /**
21977          * Gets the scrollTop
21978          * @method getScrollTop
21979          * @return {int} the document's scrollTop
21980          * @static
21981          */
21982         getScrollTop: function () { return this.getScroll().top; },
21983
21984         /**
21985          * Gets the scrollLeft
21986          * @method getScrollLeft
21987          * @return {int} the document's scrollTop
21988          * @static
21989          */
21990         getScrollLeft: function () { return this.getScroll().left; },
21991
21992         /**
21993          * Sets the x/y position of an element to the location of the
21994          * target element.
21995          * @method moveToEl
21996          * @param {HTMLElement} moveEl      The element to move
21997          * @param {HTMLElement} targetEl    The position reference element
21998          * @static
21999          */
22000         moveToEl: function (moveEl, targetEl) {
22001             var aCoord = Roo.lib.Dom.getXY(targetEl);
22002             Roo.lib.Dom.setXY(moveEl, aCoord);
22003         },
22004
22005         /**
22006          * Numeric array sort function
22007          * @method numericSort
22008          * @static
22009          */
22010         numericSort: function(a, b) { return (a - b); },
22011
22012         /**
22013          * Internal counter
22014          * @property _timeoutCount
22015          * @private
22016          * @static
22017          */
22018         _timeoutCount: 0,
22019
22020         /**
22021          * Trying to make the load order less important.  Without this we get
22022          * an error if this file is loaded before the Event Utility.
22023          * @method _addListeners
22024          * @private
22025          * @static
22026          */
22027         _addListeners: function() {
22028             var DDM = Roo.dd.DDM;
22029             if ( Roo.lib.Event && document ) {
22030                 DDM._onLoad();
22031             } else {
22032                 if (DDM._timeoutCount > 2000) {
22033                 } else {
22034                     setTimeout(DDM._addListeners, 10);
22035                     if (document && document.body) {
22036                         DDM._timeoutCount += 1;
22037                     }
22038                 }
22039             }
22040         },
22041
22042         /**
22043          * Recursively searches the immediate parent and all child nodes for
22044          * the handle element in order to determine wheter or not it was
22045          * clicked.
22046          * @method handleWasClicked
22047          * @param node the html element to inspect
22048          * @static
22049          */
22050         handleWasClicked: function(node, id) {
22051             if (this.isHandle(id, node.id)) {
22052                 return true;
22053             } else {
22054                 // check to see if this is a text node child of the one we want
22055                 var p = node.parentNode;
22056
22057                 while (p) {
22058                     if (this.isHandle(id, p.id)) {
22059                         return true;
22060                     } else {
22061                         p = p.parentNode;
22062                     }
22063                 }
22064             }
22065
22066             return false;
22067         }
22068
22069     };
22070
22071 }();
22072
22073 // shorter alias, save a few bytes
22074 Roo.dd.DDM = Roo.dd.DragDropMgr;
22075 Roo.dd.DDM._addListeners();
22076
22077 }/*
22078  * Based on:
22079  * Ext JS Library 1.1.1
22080  * Copyright(c) 2006-2007, Ext JS, LLC.
22081  *
22082  * Originally Released Under LGPL - original licence link has changed is not relivant.
22083  *
22084  * Fork - LGPL
22085  * <script type="text/javascript">
22086  */
22087
22088 /**
22089  * @class Roo.dd.DD
22090  * A DragDrop implementation where the linked element follows the
22091  * mouse cursor during a drag.
22092  * @extends Roo.dd.DragDrop
22093  * @constructor
22094  * @param {String} id the id of the linked element
22095  * @param {String} sGroup the group of related DragDrop items
22096  * @param {object} config an object containing configurable attributes
22097  *                Valid properties for DD:
22098  *                    scroll
22099  */
22100 Roo.dd.DD = function(id, sGroup, config) {
22101     if (id) {
22102         this.init(id, sGroup, config);
22103     }
22104 };
22105
22106 Roo.extend(Roo.dd.DD, Roo.dd.DragDrop, {
22107
22108     /**
22109      * When set to true, the utility automatically tries to scroll the browser
22110      * window wehn a drag and drop element is dragged near the viewport boundary.
22111      * Defaults to true.
22112      * @property scroll
22113      * @type boolean
22114      */
22115     scroll: true,
22116
22117     /**
22118      * Sets the pointer offset to the distance between the linked element's top
22119      * left corner and the location the element was clicked
22120      * @method autoOffset
22121      * @param {int} iPageX the X coordinate of the click
22122      * @param {int} iPageY the Y coordinate of the click
22123      */
22124     autoOffset: function(iPageX, iPageY) {
22125         var x = iPageX - this.startPageX;
22126         var y = iPageY - this.startPageY;
22127         this.setDelta(x, y);
22128     },
22129
22130     /**
22131      * Sets the pointer offset.  You can call this directly to force the
22132      * offset to be in a particular location (e.g., pass in 0,0 to set it
22133      * to the center of the object)
22134      * @method setDelta
22135      * @param {int} iDeltaX the distance from the left
22136      * @param {int} iDeltaY the distance from the top
22137      */
22138     setDelta: function(iDeltaX, iDeltaY) {
22139         this.deltaX = iDeltaX;
22140         this.deltaY = iDeltaY;
22141     },
22142
22143     /**
22144      * Sets the drag element to the location of the mousedown or click event,
22145      * maintaining the cursor location relative to the location on the element
22146      * that was clicked.  Override this if you want to place the element in a
22147      * location other than where the cursor is.
22148      * @method setDragElPos
22149      * @param {int} iPageX the X coordinate of the mousedown or drag event
22150      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22151      */
22152     setDragElPos: function(iPageX, iPageY) {
22153         // the first time we do this, we are going to check to make sure
22154         // the element has css positioning
22155
22156         var el = this.getDragEl();
22157         this.alignElWithMouse(el, iPageX, iPageY);
22158     },
22159
22160     /**
22161      * Sets the element to the location of the mousedown or click event,
22162      * maintaining the cursor location relative to the location on the element
22163      * that was clicked.  Override this if you want to place the element in a
22164      * location other than where the cursor is.
22165      * @method alignElWithMouse
22166      * @param {HTMLElement} el the element to move
22167      * @param {int} iPageX the X coordinate of the mousedown or drag event
22168      * @param {int} iPageY the Y coordinate of the mousedown or drag event
22169      */
22170     alignElWithMouse: function(el, iPageX, iPageY) {
22171         var oCoord = this.getTargetCoord(iPageX, iPageY);
22172         var fly = el.dom ? el : Roo.fly(el);
22173         if (!this.deltaSetXY) {
22174             var aCoord = [oCoord.x, oCoord.y];
22175             fly.setXY(aCoord);
22176             var newLeft = fly.getLeft(true);
22177             var newTop  = fly.getTop(true);
22178             this.deltaSetXY = [ newLeft - oCoord.x, newTop - oCoord.y ];
22179         } else {
22180             fly.setLeftTop(oCoord.x + this.deltaSetXY[0], oCoord.y + this.deltaSetXY[1]);
22181         }
22182
22183         this.cachePosition(oCoord.x, oCoord.y);
22184         this.autoScroll(oCoord.x, oCoord.y, el.offsetHeight, el.offsetWidth);
22185         return oCoord;
22186     },
22187
22188     /**
22189      * Saves the most recent position so that we can reset the constraints and
22190      * tick marks on-demand.  We need to know this so that we can calculate the
22191      * number of pixels the element is offset from its original position.
22192      * @method cachePosition
22193      * @param iPageX the current x position (optional, this just makes it so we
22194      * don't have to look it up again)
22195      * @param iPageY the current y position (optional, this just makes it so we
22196      * don't have to look it up again)
22197      */
22198     cachePosition: function(iPageX, iPageY) {
22199         if (iPageX) {
22200             this.lastPageX = iPageX;
22201             this.lastPageY = iPageY;
22202         } else {
22203             var aCoord = Roo.lib.Dom.getXY(this.getEl());
22204             this.lastPageX = aCoord[0];
22205             this.lastPageY = aCoord[1];
22206         }
22207     },
22208
22209     /**
22210      * Auto-scroll the window if the dragged object has been moved beyond the
22211      * visible window boundary.
22212      * @method autoScroll
22213      * @param {int} x the drag element's x position
22214      * @param {int} y the drag element's y position
22215      * @param {int} h the height of the drag element
22216      * @param {int} w the width of the drag element
22217      * @private
22218      */
22219     autoScroll: function(x, y, h, w) {
22220
22221         if (this.scroll) {
22222             // The client height
22223             var clientH = Roo.lib.Dom.getViewWidth();
22224
22225             // The client width
22226             var clientW = Roo.lib.Dom.getViewHeight();
22227
22228             // The amt scrolled down
22229             var st = this.DDM.getScrollTop();
22230
22231             // The amt scrolled right
22232             var sl = this.DDM.getScrollLeft();
22233
22234             // Location of the bottom of the element
22235             var bot = h + y;
22236
22237             // Location of the right of the element
22238             var right = w + x;
22239
22240             // The distance from the cursor to the bottom of the visible area,
22241             // adjusted so that we don't scroll if the cursor is beyond the
22242             // element drag constraints
22243             var toBot = (clientH + st - y - this.deltaY);
22244
22245             // The distance from the cursor to the right of the visible area
22246             var toRight = (clientW + sl - x - this.deltaX);
22247
22248
22249             // How close to the edge the cursor must be before we scroll
22250             // var thresh = (document.all) ? 100 : 40;
22251             var thresh = 40;
22252
22253             // How many pixels to scroll per autoscroll op.  This helps to reduce
22254             // clunky scrolling. IE is more sensitive about this ... it needs this
22255             // value to be higher.
22256             var scrAmt = (document.all) ? 80 : 30;
22257
22258             // Scroll down if we are near the bottom of the visible page and the
22259             // obj extends below the crease
22260             if ( bot > clientH && toBot < thresh ) {
22261                 window.scrollTo(sl, st + scrAmt);
22262             }
22263
22264             // Scroll up if the window is scrolled down and the top of the object
22265             // goes above the top border
22266             if ( y < st && st > 0 && y - st < thresh ) {
22267                 window.scrollTo(sl, st - scrAmt);
22268             }
22269
22270             // Scroll right if the obj is beyond the right border and the cursor is
22271             // near the border.
22272             if ( right > clientW && toRight < thresh ) {
22273                 window.scrollTo(sl + scrAmt, st);
22274             }
22275
22276             // Scroll left if the window has been scrolled to the right and the obj
22277             // extends past the left border
22278             if ( x < sl && sl > 0 && x - sl < thresh ) {
22279                 window.scrollTo(sl - scrAmt, st);
22280             }
22281         }
22282     },
22283
22284     /**
22285      * Finds the location the element should be placed if we want to move
22286      * it to where the mouse location less the click offset would place us.
22287      * @method getTargetCoord
22288      * @param {int} iPageX the X coordinate of the click
22289      * @param {int} iPageY the Y coordinate of the click
22290      * @return an object that contains the coordinates (Object.x and Object.y)
22291      * @private
22292      */
22293     getTargetCoord: function(iPageX, iPageY) {
22294
22295
22296         var x = iPageX - this.deltaX;
22297         var y = iPageY - this.deltaY;
22298
22299         if (this.constrainX) {
22300             if (x < this.minX) { x = this.minX; }
22301             if (x > this.maxX) { x = this.maxX; }
22302         }
22303
22304         if (this.constrainY) {
22305             if (y < this.minY) { y = this.minY; }
22306             if (y > this.maxY) { y = this.maxY; }
22307         }
22308
22309         x = this.getTick(x, this.xTicks);
22310         y = this.getTick(y, this.yTicks);
22311
22312
22313         return {x:x, y:y};
22314     },
22315
22316     /*
22317      * Sets up config options specific to this class. Overrides
22318      * Roo.dd.DragDrop, but all versions of this method through the
22319      * inheritance chain are called
22320      */
22321     applyConfig: function() {
22322         Roo.dd.DD.superclass.applyConfig.call(this);
22323         this.scroll = (this.config.scroll !== false);
22324     },
22325
22326     /*
22327      * Event that fires prior to the onMouseDown event.  Overrides
22328      * Roo.dd.DragDrop.
22329      */
22330     b4MouseDown: function(e) {
22331         // this.resetConstraints();
22332         this.autoOffset(e.getPageX(),
22333                             e.getPageY());
22334     },
22335
22336     /*
22337      * Event that fires prior to the onDrag event.  Overrides
22338      * Roo.dd.DragDrop.
22339      */
22340     b4Drag: function(e) {
22341         this.setDragElPos(e.getPageX(),
22342                             e.getPageY());
22343     },
22344
22345     toString: function() {
22346         return ("DD " + this.id);
22347     }
22348
22349     //////////////////////////////////////////////////////////////////////////
22350     // Debugging ygDragDrop events that can be overridden
22351     //////////////////////////////////////////////////////////////////////////
22352     /*
22353     startDrag: function(x, y) {
22354     },
22355
22356     onDrag: function(e) {
22357     },
22358
22359     onDragEnter: function(e, id) {
22360     },
22361
22362     onDragOver: function(e, id) {
22363     },
22364
22365     onDragOut: function(e, id) {
22366     },
22367
22368     onDragDrop: function(e, id) {
22369     },
22370
22371     endDrag: function(e) {
22372     }
22373
22374     */
22375
22376 });/*
22377  * Based on:
22378  * Ext JS Library 1.1.1
22379  * Copyright(c) 2006-2007, Ext JS, LLC.
22380  *
22381  * Originally Released Under LGPL - original licence link has changed is not relivant.
22382  *
22383  * Fork - LGPL
22384  * <script type="text/javascript">
22385  */
22386
22387 /**
22388  * @class Roo.dd.DDProxy
22389  * A DragDrop implementation that inserts an empty, bordered div into
22390  * the document that follows the cursor during drag operations.  At the time of
22391  * the click, the frame div is resized to the dimensions of the linked html
22392  * element, and moved to the exact location of the linked element.
22393  *
22394  * References to the "frame" element refer to the single proxy element that
22395  * was created to be dragged in place of all DDProxy elements on the
22396  * page.
22397  *
22398  * @extends Roo.dd.DD
22399  * @constructor
22400  * @param {String} id the id of the linked html element
22401  * @param {String} sGroup the group of related DragDrop objects
22402  * @param {object} config an object containing configurable attributes
22403  *                Valid properties for DDProxy in addition to those in DragDrop:
22404  *                   resizeFrame, centerFrame, dragElId
22405  */
22406 Roo.dd.DDProxy = function(id, sGroup, config) {
22407     if (id) {
22408         this.init(id, sGroup, config);
22409         this.initFrame();
22410     }
22411 };
22412
22413 /**
22414  * The default drag frame div id
22415  * @property Roo.dd.DDProxy.dragElId
22416  * @type String
22417  * @static
22418  */
22419 Roo.dd.DDProxy.dragElId = "ygddfdiv";
22420
22421 Roo.extend(Roo.dd.DDProxy, Roo.dd.DD, {
22422
22423     /**
22424      * By default we resize the drag frame to be the same size as the element
22425      * we want to drag (this is to get the frame effect).  We can turn it off
22426      * if we want a different behavior.
22427      * @property resizeFrame
22428      * @type boolean
22429      */
22430     resizeFrame: true,
22431
22432     /**
22433      * By default the frame is positioned exactly where the drag element is, so
22434      * we use the cursor offset provided by Roo.dd.DD.  Another option that works only if
22435      * you do not have constraints on the obj is to have the drag frame centered
22436      * around the cursor.  Set centerFrame to true for this effect.
22437      * @property centerFrame
22438      * @type boolean
22439      */
22440     centerFrame: false,
22441
22442     /**
22443      * Creates the proxy element if it does not yet exist
22444      * @method createFrame
22445      */
22446     createFrame: function() {
22447         var self = this;
22448         var body = document.body;
22449
22450         if (!body || !body.firstChild) {
22451             setTimeout( function() { self.createFrame(); }, 50 );
22452             return;
22453         }
22454
22455         var div = this.getDragEl();
22456
22457         if (!div) {
22458             div    = document.createElement("div");
22459             div.id = this.dragElId;
22460             var s  = div.style;
22461
22462             s.position   = "absolute";
22463             s.visibility = "hidden";
22464             s.cursor     = "move";
22465             s.border     = "2px solid #aaa";
22466             s.zIndex     = 999;
22467
22468             // appendChild can blow up IE if invoked prior to the window load event
22469             // while rendering a table.  It is possible there are other scenarios
22470             // that would cause this to happen as well.
22471             body.insertBefore(div, body.firstChild);
22472         }
22473     },
22474
22475     /**
22476      * Initialization for the drag frame element.  Must be called in the
22477      * constructor of all subclasses
22478      * @method initFrame
22479      */
22480     initFrame: function() {
22481         this.createFrame();
22482     },
22483
22484     applyConfig: function() {
22485         Roo.dd.DDProxy.superclass.applyConfig.call(this);
22486
22487         this.resizeFrame = (this.config.resizeFrame !== false);
22488         this.centerFrame = (this.config.centerFrame);
22489         this.setDragElId(this.config.dragElId || Roo.dd.DDProxy.dragElId);
22490     },
22491
22492     /**
22493      * Resizes the drag frame to the dimensions of the clicked object, positions
22494      * it over the object, and finally displays it
22495      * @method showFrame
22496      * @param {int} iPageX X click position
22497      * @param {int} iPageY Y click position
22498      * @private
22499      */
22500     showFrame: function(iPageX, iPageY) {
22501         var el = this.getEl();
22502         var dragEl = this.getDragEl();
22503         var s = dragEl.style;
22504
22505         this._resizeProxy();
22506
22507         if (this.centerFrame) {
22508             this.setDelta( Math.round(parseInt(s.width,  10)/2),
22509                            Math.round(parseInt(s.height, 10)/2) );
22510         }
22511
22512         this.setDragElPos(iPageX, iPageY);
22513
22514         Roo.fly(dragEl).show();
22515     },
22516
22517     /**
22518      * The proxy is automatically resized to the dimensions of the linked
22519      * element when a drag is initiated, unless resizeFrame is set to false
22520      * @method _resizeProxy
22521      * @private
22522      */
22523     _resizeProxy: function() {
22524         if (this.resizeFrame) {
22525             var el = this.getEl();
22526             Roo.fly(this.getDragEl()).setSize(el.offsetWidth, el.offsetHeight);
22527         }
22528     },
22529
22530     // overrides Roo.dd.DragDrop
22531     b4MouseDown: function(e) {
22532         var x = e.getPageX();
22533         var y = e.getPageY();
22534         this.autoOffset(x, y);
22535         this.setDragElPos(x, y);
22536     },
22537
22538     // overrides Roo.dd.DragDrop
22539     b4StartDrag: function(x, y) {
22540         // show the drag frame
22541         this.showFrame(x, y);
22542     },
22543
22544     // overrides Roo.dd.DragDrop
22545     b4EndDrag: function(e) {
22546         Roo.fly(this.getDragEl()).hide();
22547     },
22548
22549     // overrides Roo.dd.DragDrop
22550     // By default we try to move the element to the last location of the frame.
22551     // This is so that the default behavior mirrors that of Roo.dd.DD.
22552     endDrag: function(e) {
22553
22554         var lel = this.getEl();
22555         var del = this.getDragEl();
22556
22557         // Show the drag frame briefly so we can get its position
22558         del.style.visibility = "";
22559
22560         this.beforeMove();
22561         // Hide the linked element before the move to get around a Safari
22562         // rendering bug.
22563         lel.style.visibility = "hidden";
22564         Roo.dd.DDM.moveToEl(lel, del);
22565         del.style.visibility = "hidden";
22566         lel.style.visibility = "";
22567
22568         this.afterDrag();
22569     },
22570
22571     beforeMove : function(){
22572
22573     },
22574
22575     afterDrag : function(){
22576
22577     },
22578
22579     toString: function() {
22580         return ("DDProxy " + this.id);
22581     }
22582
22583 });
22584 /*
22585  * Based on:
22586  * Ext JS Library 1.1.1
22587  * Copyright(c) 2006-2007, Ext JS, LLC.
22588  *
22589  * Originally Released Under LGPL - original licence link has changed is not relivant.
22590  *
22591  * Fork - LGPL
22592  * <script type="text/javascript">
22593  */
22594
22595  /**
22596  * @class Roo.dd.DDTarget
22597  * A DragDrop implementation that does not move, but can be a drop
22598  * target.  You would get the same result by simply omitting implementation
22599  * for the event callbacks, but this way we reduce the processing cost of the
22600  * event listener and the callbacks.
22601  * @extends Roo.dd.DragDrop
22602  * @constructor
22603  * @param {String} id the id of the element that is a drop target
22604  * @param {String} sGroup the group of related DragDrop objects
22605  * @param {object} config an object containing configurable attributes
22606  *                 Valid properties for DDTarget in addition to those in
22607  *                 DragDrop:
22608  *                    none
22609  */
22610 Roo.dd.DDTarget = function(id, sGroup, config) {
22611     if (id) {
22612         this.initTarget(id, sGroup, config);
22613     }
22614     if (config && (config.listeners || config.events)) { 
22615         Roo.dd.DragDrop.superclass.constructor.call(this,  { 
22616             listeners : config.listeners || {}, 
22617             events : config.events || {} 
22618         });    
22619     }
22620 };
22621
22622 // Roo.dd.DDTarget.prototype = new Roo.dd.DragDrop();
22623 Roo.extend(Roo.dd.DDTarget, Roo.dd.DragDrop, {
22624     toString: function() {
22625         return ("DDTarget " + this.id);
22626     }
22627 });
22628 /*
22629  * Based on:
22630  * Ext JS Library 1.1.1
22631  * Copyright(c) 2006-2007, Ext JS, LLC.
22632  *
22633  * Originally Released Under LGPL - original licence link has changed is not relivant.
22634  *
22635  * Fork - LGPL
22636  * <script type="text/javascript">
22637  */
22638  
22639
22640 /**
22641  * @class Roo.dd.ScrollManager
22642  * Provides automatic scrolling of overflow regions in the page during drag operations.<br><br>
22643  * <b>Note: This class uses "Point Mode" and is untested in "Intersect Mode".</b>
22644  * @static
22645  */
22646 Roo.dd.ScrollManager = function(){
22647     var ddm = Roo.dd.DragDropMgr;
22648     var els = {};
22649     var dragEl = null;
22650     var proc = {};
22651     
22652     
22653     
22654     var onStop = function(e){
22655         dragEl = null;
22656         clearProc();
22657     };
22658     
22659     var triggerRefresh = function(){
22660         if(ddm.dragCurrent){
22661              ddm.refreshCache(ddm.dragCurrent.groups);
22662         }
22663     };
22664     
22665     var doScroll = function(){
22666         if(ddm.dragCurrent){
22667             var dds = Roo.dd.ScrollManager;
22668             if(!dds.animate){
22669                 if(proc.el.scroll(proc.dir, dds.increment)){
22670                     triggerRefresh();
22671                 }
22672             }else{
22673                 proc.el.scroll(proc.dir, dds.increment, true, dds.animDuration, triggerRefresh);
22674             }
22675         }
22676     };
22677     
22678     var clearProc = function(){
22679         if(proc.id){
22680             clearInterval(proc.id);
22681         }
22682         proc.id = 0;
22683         proc.el = null;
22684         proc.dir = "";
22685     };
22686     
22687     var startProc = function(el, dir){
22688          Roo.log('scroll startproc');
22689         clearProc();
22690         proc.el = el;
22691         proc.dir = dir;
22692         proc.id = setInterval(doScroll, Roo.dd.ScrollManager.frequency);
22693     };
22694     
22695     var onFire = function(e, isDrop){
22696        
22697         if(isDrop || !ddm.dragCurrent){ return; }
22698         var dds = Roo.dd.ScrollManager;
22699         if(!dragEl || dragEl != ddm.dragCurrent){
22700             dragEl = ddm.dragCurrent;
22701             // refresh regions on drag start
22702             dds.refreshCache();
22703         }
22704         
22705         var xy = Roo.lib.Event.getXY(e);
22706         var pt = new Roo.lib.Point(xy[0], xy[1]);
22707         for(var id in els){
22708             var el = els[id], r = el._region;
22709             if(r && r.contains(pt) && el.isScrollable()){
22710                 if(r.bottom - pt.y <= dds.thresh){
22711                     if(proc.el != el){
22712                         startProc(el, "down");
22713                     }
22714                     return;
22715                 }else if(r.right - pt.x <= dds.thresh){
22716                     if(proc.el != el){
22717                         startProc(el, "left");
22718                     }
22719                     return;
22720                 }else if(pt.y - r.top <= dds.thresh){
22721                     if(proc.el != el){
22722                         startProc(el, "up");
22723                     }
22724                     return;
22725                 }else if(pt.x - r.left <= dds.thresh){
22726                     if(proc.el != el){
22727                         startProc(el, "right");
22728                     }
22729                     return;
22730                 }
22731             }
22732         }
22733         clearProc();
22734     };
22735     
22736     ddm.fireEvents = ddm.fireEvents.createSequence(onFire, ddm);
22737     ddm.stopDrag = ddm.stopDrag.createSequence(onStop, ddm);
22738     
22739     return {
22740         /**
22741          * Registers new overflow element(s) to auto scroll
22742          * @param {String/HTMLElement/Element/Array} el The id of or the element to be scrolled or an array of either
22743          */
22744         register : function(el){
22745             if(el instanceof Array){
22746                 for(var i = 0, len = el.length; i < len; i++) {
22747                         this.register(el[i]);
22748                 }
22749             }else{
22750                 el = Roo.get(el);
22751                 els[el.id] = el;
22752             }
22753             Roo.dd.ScrollManager.els = els;
22754         },
22755         
22756         /**
22757          * Unregisters overflow element(s) so they are no longer scrolled
22758          * @param {String/HTMLElement/Element/Array} el The id of or the element to be removed or an array of either
22759          */
22760         unregister : function(el){
22761             if(el instanceof Array){
22762                 for(var i = 0, len = el.length; i < len; i++) {
22763                         this.unregister(el[i]);
22764                 }
22765             }else{
22766                 el = Roo.get(el);
22767                 delete els[el.id];
22768             }
22769         },
22770         
22771         /**
22772          * The number of pixels from the edge of a container the pointer needs to be to 
22773          * trigger scrolling (defaults to 25)
22774          * @type Number
22775          */
22776         thresh : 25,
22777         
22778         /**
22779          * The number of pixels to scroll in each scroll increment (defaults to 50)
22780          * @type Number
22781          */
22782         increment : 100,
22783         
22784         /**
22785          * The frequency of scrolls in milliseconds (defaults to 500)
22786          * @type Number
22787          */
22788         frequency : 500,
22789         
22790         /**
22791          * True to animate the scroll (defaults to true)
22792          * @type Boolean
22793          */
22794         animate: true,
22795         
22796         /**
22797          * The animation duration in seconds - 
22798          * MUST BE less than Roo.dd.ScrollManager.frequency! (defaults to .4)
22799          * @type Number
22800          */
22801         animDuration: .4,
22802         
22803         /**
22804          * Manually trigger a cache refresh.
22805          */
22806         refreshCache : function(){
22807             for(var id in els){
22808                 if(typeof els[id] == 'object'){ // for people extending the object prototype
22809                     els[id]._region = els[id].getRegion();
22810                 }
22811             }
22812         }
22813     };
22814 }();/*
22815  * Based on:
22816  * Ext JS Library 1.1.1
22817  * Copyright(c) 2006-2007, Ext JS, LLC.
22818  *
22819  * Originally Released Under LGPL - original licence link has changed is not relivant.
22820  *
22821  * Fork - LGPL
22822  * <script type="text/javascript">
22823  */
22824  
22825
22826 /**
22827  * @class Roo.dd.Registry
22828  * Provides easy access to all drag drop components that are registered on a page.  Items can be retrieved either
22829  * directly by DOM node id, or by passing in the drag drop event that occurred and looking up the event target.
22830  * @static
22831  */
22832 Roo.dd.Registry = function(){
22833     var elements = {}; 
22834     var handles = {}; 
22835     var autoIdSeed = 0;
22836
22837     var getId = function(el, autogen){
22838         if(typeof el == "string"){
22839             return el;
22840         }
22841         var id = el.id;
22842         if(!id && autogen !== false){
22843             id = "roodd-" + (++autoIdSeed);
22844             el.id = id;
22845         }
22846         return id;
22847     };
22848     
22849     return {
22850     /**
22851      * Register a drag drop element
22852      * @param {String|HTMLElement} element The id or DOM node to register
22853      * @param {Object} data (optional) A custom data object that will be passed between the elements that are involved
22854      * in drag drop operations.  You can populate this object with any arbitrary properties that your own code
22855      * knows how to interpret, plus there are some specific properties known to the Registry that should be
22856      * populated in the data object (if applicable):
22857      * <pre>
22858 Value      Description<br />
22859 ---------  ------------------------------------------<br />
22860 handles    Array of DOM nodes that trigger dragging<br />
22861            for the element being registered<br />
22862 isHandle   True if the element passed in triggers<br />
22863            dragging itself, else false
22864 </pre>
22865      */
22866         register : function(el, data){
22867             data = data || {};
22868             if(typeof el == "string"){
22869                 el = document.getElementById(el);
22870             }
22871             data.ddel = el;
22872             elements[getId(el)] = data;
22873             if(data.isHandle !== false){
22874                 handles[data.ddel.id] = data;
22875             }
22876             if(data.handles){
22877                 var hs = data.handles;
22878                 for(var i = 0, len = hs.length; i < len; i++){
22879                         handles[getId(hs[i])] = data;
22880                 }
22881             }
22882         },
22883
22884     /**
22885      * Unregister a drag drop element
22886      * @param {String|HTMLElement}  element The id or DOM node to unregister
22887      */
22888         unregister : function(el){
22889             var id = getId(el, false);
22890             var data = elements[id];
22891             if(data){
22892                 delete elements[id];
22893                 if(data.handles){
22894                     var hs = data.handles;
22895                     for(var i = 0, len = hs.length; i < len; i++){
22896                         delete handles[getId(hs[i], false)];
22897                     }
22898                 }
22899             }
22900         },
22901
22902     /**
22903      * Returns the handle registered for a DOM Node by id
22904      * @param {String|HTMLElement} id The DOM node or id to look up
22905      * @return {Object} handle The custom handle data
22906      */
22907         getHandle : function(id){
22908             if(typeof id != "string"){ // must be element?
22909                 id = id.id;
22910             }
22911             return handles[id];
22912         },
22913
22914     /**
22915      * Returns the handle that is registered for the DOM node that is the target of the event
22916      * @param {Event} e The event
22917      * @return {Object} handle The custom handle data
22918      */
22919         getHandleFromEvent : function(e){
22920             var t = Roo.lib.Event.getTarget(e);
22921             return t ? handles[t.id] : null;
22922         },
22923
22924     /**
22925      * Returns a custom data object that is registered for a DOM node by id
22926      * @param {String|HTMLElement} id The DOM node or id to look up
22927      * @return {Object} data The custom data
22928      */
22929         getTarget : function(id){
22930             if(typeof id != "string"){ // must be element?
22931                 id = id.id;
22932             }
22933             return elements[id];
22934         },
22935
22936     /**
22937      * Returns a custom data object that is registered for the DOM node that is the target of the event
22938      * @param {Event} e The event
22939      * @return {Object} data The custom data
22940      */
22941         getTargetFromEvent : function(e){
22942             var t = Roo.lib.Event.getTarget(e);
22943             return t ? elements[t.id] || handles[t.id] : null;
22944         }
22945     };
22946 }();/*
22947  * Based on:
22948  * Ext JS Library 1.1.1
22949  * Copyright(c) 2006-2007, Ext JS, LLC.
22950  *
22951  * Originally Released Under LGPL - original licence link has changed is not relivant.
22952  *
22953  * Fork - LGPL
22954  * <script type="text/javascript">
22955  */
22956  
22957
22958 /**
22959  * @class Roo.dd.StatusProxy
22960  * A specialized drag proxy that supports a drop status icon, {@link Roo.Layer} styles and auto-repair.  This is the
22961  * default drag proxy used by all Roo.dd components.
22962  * @constructor
22963  * @param {Object} config
22964  */
22965 Roo.dd.StatusProxy = function(config){
22966     Roo.apply(this, config);
22967     this.id = this.id || Roo.id();
22968     this.el = new Roo.Layer({
22969         dh: {
22970             id: this.id, tag: "div", cls: "x-dd-drag-proxy "+this.dropNotAllowed, children: [
22971                 {tag: "div", cls: "x-dd-drop-icon"},
22972                 {tag: "div", cls: "x-dd-drag-ghost"}
22973             ]
22974         }, 
22975         shadow: !config || config.shadow !== false
22976     });
22977     this.ghost = Roo.get(this.el.dom.childNodes[1]);
22978     this.dropStatus = this.dropNotAllowed;
22979 };
22980
22981 Roo.dd.StatusProxy.prototype = {
22982     /**
22983      * @cfg {String} dropAllowed
22984      * The CSS class to apply to the status element when drop is allowed (defaults to "x-dd-drop-ok").
22985      */
22986     dropAllowed : "x-dd-drop-ok",
22987     /**
22988      * @cfg {String} dropNotAllowed
22989      * The CSS class to apply to the status element when drop is not allowed (defaults to "x-dd-drop-nodrop").
22990      */
22991     dropNotAllowed : "x-dd-drop-nodrop",
22992
22993     /**
22994      * Updates the proxy's visual element to indicate the status of whether or not drop is allowed
22995      * over the current target element.
22996      * @param {String} cssClass The css class for the new drop status indicator image
22997      */
22998     setStatus : function(cssClass){
22999         cssClass = cssClass || this.dropNotAllowed;
23000         if(this.dropStatus != cssClass){
23001             this.el.replaceClass(this.dropStatus, cssClass);
23002             this.dropStatus = cssClass;
23003         }
23004     },
23005
23006     /**
23007      * Resets the status indicator to the default dropNotAllowed value
23008      * @param {Boolean} clearGhost True to also remove all content from the ghost, false to preserve it
23009      */
23010     reset : function(clearGhost){
23011         this.el.dom.className = "x-dd-drag-proxy " + this.dropNotAllowed;
23012         this.dropStatus = this.dropNotAllowed;
23013         if(clearGhost){
23014             this.ghost.update("");
23015         }
23016     },
23017
23018     /**
23019      * Updates the contents of the ghost element
23020      * @param {String} html The html that will replace the current innerHTML of the ghost element
23021      */
23022     update : function(html){
23023         if(typeof html == "string"){
23024             this.ghost.update(html);
23025         }else{
23026             this.ghost.update("");
23027             html.style.margin = "0";
23028             this.ghost.dom.appendChild(html);
23029         }
23030         // ensure float = none set?? cant remember why though.
23031         var el = this.ghost.dom.firstChild;
23032                 if(el){
23033                         Roo.fly(el).setStyle('float', 'none');
23034                 }
23035     },
23036     
23037     /**
23038      * Returns the underlying proxy {@link Roo.Layer}
23039      * @return {Roo.Layer} el
23040     */
23041     getEl : function(){
23042         return this.el;
23043     },
23044
23045     /**
23046      * Returns the ghost element
23047      * @return {Roo.Element} el
23048      */
23049     getGhost : function(){
23050         return this.ghost;
23051     },
23052
23053     /**
23054      * Hides the proxy
23055      * @param {Boolean} clear True to reset the status and clear the ghost contents, false to preserve them
23056      */
23057     hide : function(clear){
23058         this.el.hide();
23059         if(clear){
23060             this.reset(true);
23061         }
23062     },
23063
23064     /**
23065      * Stops the repair animation if it's currently running
23066      */
23067     stop : function(){
23068         if(this.anim && this.anim.isAnimated && this.anim.isAnimated()){
23069             this.anim.stop();
23070         }
23071     },
23072
23073     /**
23074      * Displays this proxy
23075      */
23076     show : function(){
23077         this.el.show();
23078     },
23079
23080     /**
23081      * Force the Layer to sync its shadow and shim positions to the element
23082      */
23083     sync : function(){
23084         this.el.sync();
23085     },
23086
23087     /**
23088      * Causes the proxy to return to its position of origin via an animation.  Should be called after an
23089      * invalid drop operation by the item being dragged.
23090      * @param {Array} xy The XY position of the element ([x, y])
23091      * @param {Function} callback The function to call after the repair is complete
23092      * @param {Object} scope The scope in which to execute the callback
23093      */
23094     repair : function(xy, callback, scope){
23095         this.callback = callback;
23096         this.scope = scope;
23097         if(xy && this.animRepair !== false){
23098             this.el.addClass("x-dd-drag-repair");
23099             this.el.hideUnders(true);
23100             this.anim = this.el.shift({
23101                 duration: this.repairDuration || .5,
23102                 easing: 'easeOut',
23103                 xy: xy,
23104                 stopFx: true,
23105                 callback: this.afterRepair,
23106                 scope: this
23107             });
23108         }else{
23109             this.afterRepair();
23110         }
23111     },
23112
23113     // private
23114     afterRepair : function(){
23115         this.hide(true);
23116         if(typeof this.callback == "function"){
23117             this.callback.call(this.scope || this);
23118         }
23119         this.callback = null;
23120         this.scope = null;
23121     }
23122 };/*
23123  * Based on:
23124  * Ext JS Library 1.1.1
23125  * Copyright(c) 2006-2007, Ext JS, LLC.
23126  *
23127  * Originally Released Under LGPL - original licence link has changed is not relivant.
23128  *
23129  * Fork - LGPL
23130  * <script type="text/javascript">
23131  */
23132
23133 /**
23134  * @class Roo.dd.DragSource
23135  * @extends Roo.dd.DDProxy
23136  * A simple class that provides the basic implementation needed to make any element draggable.
23137  * @constructor
23138  * @param {String/HTMLElement/Element} el The container element
23139  * @param {Object} config
23140  */
23141 Roo.dd.DragSource = function(el, config){
23142     this.el = Roo.get(el);
23143     this.dragData = {};
23144     
23145     Roo.apply(this, config);
23146     
23147     if(!this.proxy){
23148         this.proxy = new Roo.dd.StatusProxy();
23149     }
23150
23151     Roo.dd.DragSource.superclass.constructor.call(this, this.el.dom, this.ddGroup || this.group,
23152           {dragElId : this.proxy.id, resizeFrame: false, isTarget: false, scroll: this.scroll === true});
23153     
23154     this.dragging = false;
23155 };
23156
23157 Roo.extend(Roo.dd.DragSource, Roo.dd.DDProxy, {
23158     /**
23159      * @cfg {String} dropAllowed
23160      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23161      */
23162     dropAllowed : "x-dd-drop-ok",
23163     /**
23164      * @cfg {String} dropNotAllowed
23165      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23166      */
23167     dropNotAllowed : "x-dd-drop-nodrop",
23168
23169     /**
23170      * Returns the data object associated with this drag source
23171      * @return {Object} data An object containing arbitrary data
23172      */
23173     getDragData : function(e){
23174         return this.dragData;
23175     },
23176
23177     // private
23178     onDragEnter : function(e, id){
23179         var target = Roo.dd.DragDropMgr.getDDById(id);
23180         this.cachedTarget = target;
23181         if(this.beforeDragEnter(target, e, id) !== false){
23182             if(target.isNotifyTarget){
23183                 var status = target.notifyEnter(this, e, this.dragData);
23184                 this.proxy.setStatus(status);
23185             }else{
23186                 this.proxy.setStatus(this.dropAllowed);
23187             }
23188             
23189             if(this.afterDragEnter){
23190                 /**
23191                  * An empty function by default, but provided so that you can perform a custom action
23192                  * when the dragged item enters the drop target by providing an implementation.
23193                  * @param {Roo.dd.DragDrop} target The drop target
23194                  * @param {Event} e The event object
23195                  * @param {String} id The id of the dragged element
23196                  * @method afterDragEnter
23197                  */
23198                 this.afterDragEnter(target, e, id);
23199             }
23200         }
23201     },
23202
23203     /**
23204      * An empty function by default, but provided so that you can perform a custom action
23205      * before the dragged item enters the drop target and optionally cancel the onDragEnter.
23206      * @param {Roo.dd.DragDrop} target The drop target
23207      * @param {Event} e The event object
23208      * @param {String} id The id of the dragged element
23209      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23210      */
23211     beforeDragEnter : function(target, e, id){
23212         return true;
23213     },
23214
23215     // private
23216     alignElWithMouse: function() {
23217         Roo.dd.DragSource.superclass.alignElWithMouse.apply(this, arguments);
23218         this.proxy.sync();
23219     },
23220
23221     // private
23222     onDragOver : function(e, id){
23223         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23224         if(this.beforeDragOver(target, e, id) !== false){
23225             if(target.isNotifyTarget){
23226                 var status = target.notifyOver(this, e, this.dragData);
23227                 this.proxy.setStatus(status);
23228             }
23229
23230             if(this.afterDragOver){
23231                 /**
23232                  * An empty function by default, but provided so that you can perform a custom action
23233                  * while the dragged item is over the drop target by providing an implementation.
23234                  * @param {Roo.dd.DragDrop} target The drop target
23235                  * @param {Event} e The event object
23236                  * @param {String} id The id of the dragged element
23237                  * @method afterDragOver
23238                  */
23239                 this.afterDragOver(target, e, id);
23240             }
23241         }
23242     },
23243
23244     /**
23245      * An empty function by default, but provided so that you can perform a custom action
23246      * while the dragged item is over the drop target and optionally cancel the onDragOver.
23247      * @param {Roo.dd.DragDrop} target The drop target
23248      * @param {Event} e The event object
23249      * @param {String} id The id of the dragged element
23250      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23251      */
23252     beforeDragOver : function(target, e, id){
23253         return true;
23254     },
23255
23256     // private
23257     onDragOut : function(e, id){
23258         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23259         if(this.beforeDragOut(target, e, id) !== false){
23260             if(target.isNotifyTarget){
23261                 target.notifyOut(this, e, this.dragData);
23262             }
23263             this.proxy.reset();
23264             if(this.afterDragOut){
23265                 /**
23266                  * An empty function by default, but provided so that you can perform a custom action
23267                  * after the dragged item is dragged out of the target without dropping.
23268                  * @param {Roo.dd.DragDrop} target The drop target
23269                  * @param {Event} e The event object
23270                  * @param {String} id The id of the dragged element
23271                  * @method afterDragOut
23272                  */
23273                 this.afterDragOut(target, e, id);
23274             }
23275         }
23276         this.cachedTarget = null;
23277     },
23278
23279     /**
23280      * An empty function by default, but provided so that you can perform a custom action before the dragged
23281      * item is dragged out of the target without dropping, and optionally cancel the onDragOut.
23282      * @param {Roo.dd.DragDrop} target The drop target
23283      * @param {Event} e The event object
23284      * @param {String} id The id of the dragged element
23285      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23286      */
23287     beforeDragOut : function(target, e, id){
23288         return true;
23289     },
23290     
23291     // private
23292     onDragDrop : function(e, id){
23293         var target = this.cachedTarget || Roo.dd.DragDropMgr.getDDById(id);
23294         if(this.beforeDragDrop(target, e, id) !== false){
23295             if(target.isNotifyTarget){
23296                 if(target.notifyDrop(this, e, this.dragData)){ // valid drop?
23297                     this.onValidDrop(target, e, id);
23298                 }else{
23299                     this.onInvalidDrop(target, e, id);
23300                 }
23301             }else{
23302                 this.onValidDrop(target, e, id);
23303             }
23304             
23305             if(this.afterDragDrop){
23306                 /**
23307                  * An empty function by default, but provided so that you can perform a custom action
23308                  * after a valid drag drop has occurred by providing an implementation.
23309                  * @param {Roo.dd.DragDrop} target The drop target
23310                  * @param {Event} e The event object
23311                  * @param {String} id The id of the dropped element
23312                  * @method afterDragDrop
23313                  */
23314                 this.afterDragDrop(target, e, id);
23315             }
23316         }
23317         delete this.cachedTarget;
23318     },
23319
23320     /**
23321      * An empty function by default, but provided so that you can perform a custom action before the dragged
23322      * item is dropped onto the target and optionally cancel the onDragDrop.
23323      * @param {Roo.dd.DragDrop} target The drop target
23324      * @param {Event} e The event object
23325      * @param {String} id The id of the dragged element
23326      * @return {Boolean} isValid True if the drag drop event is valid, else false to cancel
23327      */
23328     beforeDragDrop : function(target, e, id){
23329         return true;
23330     },
23331
23332     // private
23333     onValidDrop : function(target, e, id){
23334         this.hideProxy();
23335         if(this.afterValidDrop){
23336             /**
23337              * An empty function by default, but provided so that you can perform a custom action
23338              * after a valid drop has occurred by providing an implementation.
23339              * @param {Object} target The target DD 
23340              * @param {Event} e The event object
23341              * @param {String} id The id of the dropped element
23342              * @method afterInvalidDrop
23343              */
23344             this.afterValidDrop(target, e, id);
23345         }
23346     },
23347
23348     // private
23349     getRepairXY : function(e, data){
23350         return this.el.getXY();  
23351     },
23352
23353     // private
23354     onInvalidDrop : function(target, e, id){
23355         this.beforeInvalidDrop(target, e, id);
23356         if(this.cachedTarget){
23357             if(this.cachedTarget.isNotifyTarget){
23358                 this.cachedTarget.notifyOut(this, e, this.dragData);
23359             }
23360             this.cacheTarget = null;
23361         }
23362         this.proxy.repair(this.getRepairXY(e, this.dragData), this.afterRepair, this);
23363
23364         if(this.afterInvalidDrop){
23365             /**
23366              * An empty function by default, but provided so that you can perform a custom action
23367              * after an invalid drop has occurred by providing an implementation.
23368              * @param {Event} e The event object
23369              * @param {String} id The id of the dropped element
23370              * @method afterInvalidDrop
23371              */
23372             this.afterInvalidDrop(e, id);
23373         }
23374     },
23375
23376     // private
23377     afterRepair : function(){
23378         if(Roo.enableFx){
23379             this.el.highlight(this.hlColor || "c3daf9");
23380         }
23381         this.dragging = false;
23382     },
23383
23384     /**
23385      * An empty function by default, but provided so that you can perform a custom action after an invalid
23386      * drop has occurred.
23387      * @param {Roo.dd.DragDrop} target The drop target
23388      * @param {Event} e The event object
23389      * @param {String} id The id of the dragged element
23390      * @return {Boolean} isValid True if the invalid drop should proceed, else false to cancel
23391      */
23392     beforeInvalidDrop : function(target, e, id){
23393         return true;
23394     },
23395
23396     // private
23397     handleMouseDown : function(e){
23398         if(this.dragging) {
23399             return;
23400         }
23401         var data = this.getDragData(e);
23402         if(data && this.onBeforeDrag(data, e) !== false){
23403             this.dragData = data;
23404             this.proxy.stop();
23405             Roo.dd.DragSource.superclass.handleMouseDown.apply(this, arguments);
23406         } 
23407     },
23408
23409     /**
23410      * An empty function by default, but provided so that you can perform a custom action before the initial
23411      * drag event begins and optionally cancel it.
23412      * @param {Object} data An object containing arbitrary data to be shared with drop targets
23413      * @param {Event} e The event object
23414      * @return {Boolean} isValid True if the drag event is valid, else false to cancel
23415      */
23416     onBeforeDrag : function(data, e){
23417         return true;
23418     },
23419
23420     /**
23421      * An empty function by default, but provided so that you can perform a custom action once the initial
23422      * drag event has begun.  The drag cannot be canceled from this function.
23423      * @param {Number} x The x position of the click on the dragged object
23424      * @param {Number} y The y position of the click on the dragged object
23425      */
23426     onStartDrag : Roo.emptyFn,
23427
23428     // private - YUI override
23429     startDrag : function(x, y){
23430         this.proxy.reset();
23431         this.dragging = true;
23432         this.proxy.update("");
23433         this.onInitDrag(x, y);
23434         this.proxy.show();
23435     },
23436
23437     // private
23438     onInitDrag : function(x, y){
23439         var clone = this.el.dom.cloneNode(true);
23440         clone.id = Roo.id(); // prevent duplicate ids
23441         this.proxy.update(clone);
23442         this.onStartDrag(x, y);
23443         return true;
23444     },
23445
23446     /**
23447      * Returns the drag source's underlying {@link Roo.dd.StatusProxy}
23448      * @return {Roo.dd.StatusProxy} proxy The StatusProxy
23449      */
23450     getProxy : function(){
23451         return this.proxy;  
23452     },
23453
23454     /**
23455      * Hides the drag source's {@link Roo.dd.StatusProxy}
23456      */
23457     hideProxy : function(){
23458         this.proxy.hide();  
23459         this.proxy.reset(true);
23460         this.dragging = false;
23461     },
23462
23463     // private
23464     triggerCacheRefresh : function(){
23465         Roo.dd.DDM.refreshCache(this.groups);
23466     },
23467
23468     // private - override to prevent hiding
23469     b4EndDrag: function(e) {
23470     },
23471
23472     // private - override to prevent moving
23473     endDrag : function(e){
23474         this.onEndDrag(this.dragData, e);
23475     },
23476
23477     // private
23478     onEndDrag : function(data, e){
23479     },
23480     
23481     // private - pin to cursor
23482     autoOffset : function(x, y) {
23483         this.setDelta(-12, -20);
23484     }    
23485 });/*
23486  * Based on:
23487  * Ext JS Library 1.1.1
23488  * Copyright(c) 2006-2007, Ext JS, LLC.
23489  *
23490  * Originally Released Under LGPL - original licence link has changed is not relivant.
23491  *
23492  * Fork - LGPL
23493  * <script type="text/javascript">
23494  */
23495
23496
23497 /**
23498  * @class Roo.dd.DropTarget
23499  * @extends Roo.dd.DDTarget
23500  * A simple class that provides the basic implementation needed to make any element a drop target that can have
23501  * draggable items dropped onto it.  The drop has no effect until an implementation of notifyDrop is provided.
23502  * @constructor
23503  * @param {String/HTMLElement/Element} el The container element
23504  * @param {Object} config
23505  */
23506 Roo.dd.DropTarget = function(el, config){
23507     this.el = Roo.get(el);
23508     
23509     var listeners = false; ;
23510     if (config && config.listeners) {
23511         listeners= config.listeners;
23512         delete config.listeners;
23513     }
23514     Roo.apply(this, config);
23515     
23516     if(this.containerScroll){
23517         Roo.dd.ScrollManager.register(this.el);
23518     }
23519     this.addEvents( {
23520          /**
23521          * @scope Roo.dd.DropTarget
23522          */
23523          
23524          /**
23525          * @event enter
23526          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source is now over the
23527          * target.  This default implementation adds the CSS class specified by overClass (if any) to the drop element
23528          * and returns the dropAllowed config value.  This method should be overridden if drop validation is required.
23529          * 
23530          * IMPORTANT : it should set  this.valid to true|false
23531          * 
23532          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23533          * @param {Event} e The event
23534          * @param {Object} data An object containing arbitrary data supplied by the drag source
23535          */
23536         "enter" : true,
23537         
23538          /**
23539          * @event over
23540          * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the target.
23541          * This method will be called on every mouse movement while the drag source is over the drop target.
23542          * This default implementation simply returns the dropAllowed config value.
23543          * 
23544          * IMPORTANT : it should set  this.valid to true|false
23545          * 
23546          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23547          * @param {Event} e The event
23548          * @param {Object} data An object containing arbitrary data supplied by the drag source
23549          
23550          */
23551         "over" : true,
23552         /**
23553          * @event out
23554          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the source has been dragged
23555          * out of the target without dropping.  This default implementation simply removes the CSS class specified by
23556          * overClass (if any) from the drop element.
23557          * 
23558          * 
23559          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23560          * @param {Event} e The event
23561          * @param {Object} data An object containing arbitrary data supplied by the drag source
23562          */
23563          "out" : true,
23564          
23565         /**
23566          * @event drop
23567          * The function a {@link Roo.dd.DragSource} calls once to notify this drop target that the dragged item has
23568          * been dropped on it.  This method has no default implementation and returns false, so you must provide an
23569          * implementation that does something to process the drop event and returns true so that the drag source's
23570          * repair action does not run.
23571          * 
23572          * IMPORTANT : it should set this.success
23573          * 
23574          * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23575          * @param {Event} e The event
23576          * @param {Object} data An object containing arbitrary data supplied by the drag source
23577         */
23578          "drop" : true
23579     });
23580             
23581      
23582     Roo.dd.DropTarget.superclass.constructor.call(  this, 
23583         this.el.dom, 
23584         this.ddGroup || this.group,
23585         {
23586             isTarget: true,
23587             listeners : listeners || {} 
23588            
23589         
23590         }
23591     );
23592
23593 };
23594
23595 Roo.extend(Roo.dd.DropTarget, Roo.dd.DDTarget, {
23596     /**
23597      * @cfg {String} overClass
23598      * The CSS class applied to the drop target element while the drag source is over it (defaults to "").
23599      */
23600      /**
23601      * @cfg {String} ddGroup
23602      * The drag drop group to handle drop events for
23603      */
23604      
23605     /**
23606      * @cfg {String} dropAllowed
23607      * The CSS class returned to the drag source when drop is allowed (defaults to "x-dd-drop-ok").
23608      */
23609     dropAllowed : "x-dd-drop-ok",
23610     /**
23611      * @cfg {String} dropNotAllowed
23612      * The CSS class returned to the drag source when drop is not allowed (defaults to "x-dd-drop-nodrop").
23613      */
23614     dropNotAllowed : "x-dd-drop-nodrop",
23615     /**
23616      * @cfg {boolean} success
23617      * set this after drop listener.. 
23618      */
23619     success : false,
23620     /**
23621      * @cfg {boolean|String} valid true/false or string (ok-add/ok-sub/ok/nodrop)
23622      * if the drop point is valid for over/enter..
23623      */
23624     valid : false,
23625     // private
23626     isTarget : true,
23627
23628     // private
23629     isNotifyTarget : true,
23630     
23631     /**
23632      * @hide
23633      */
23634     notifyEnter : function(dd, e, data)
23635     {
23636         this.valid = true;
23637         this.fireEvent('enter', dd, e, data);
23638         if(this.overClass){
23639             this.el.addClass(this.overClass);
23640         }
23641         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23642             this.valid ? this.dropAllowed : this.dropNotAllowed
23643         );
23644     },
23645
23646     /**
23647      * @hide
23648      */
23649     notifyOver : function(dd, e, data)
23650     {
23651         this.valid = true;
23652         this.fireEvent('over', dd, e, data);
23653         return typeof(this.valid) == 'string' ? 'x-dd-drop-' + this.valid : (
23654             this.valid ? this.dropAllowed : this.dropNotAllowed
23655         );
23656     },
23657
23658     /**
23659      * @hide
23660      */
23661     notifyOut : function(dd, e, data)
23662     {
23663         this.fireEvent('out', dd, e, data);
23664         if(this.overClass){
23665             this.el.removeClass(this.overClass);
23666         }
23667     },
23668
23669     /**
23670      * @hide
23671      */
23672     notifyDrop : function(dd, e, data)
23673     {
23674         this.success = false;
23675         this.fireEvent('drop', dd, e, data);
23676         return this.success;
23677     }
23678 });/*
23679  * Based on:
23680  * Ext JS Library 1.1.1
23681  * Copyright(c) 2006-2007, Ext JS, LLC.
23682  *
23683  * Originally Released Under LGPL - original licence link has changed is not relivant.
23684  *
23685  * Fork - LGPL
23686  * <script type="text/javascript">
23687  */
23688
23689
23690 /**
23691  * @class Roo.dd.DragZone
23692  * @extends Roo.dd.DragSource
23693  * This class provides a container DD instance that proxies for multiple child node sources.<br />
23694  * By default, this class requires that draggable child nodes are registered with {@link Roo.dd.Registry}.
23695  * @constructor
23696  * @param {String/HTMLElement/Element} el The container element
23697  * @param {Object} config
23698  */
23699 Roo.dd.DragZone = function(el, config){
23700     Roo.dd.DragZone.superclass.constructor.call(this, el, config);
23701     if(this.containerScroll){
23702         Roo.dd.ScrollManager.register(this.el);
23703     }
23704 };
23705
23706 Roo.extend(Roo.dd.DragZone, Roo.dd.DragSource, {
23707     /**
23708      * @cfg {Boolean} containerScroll True to register this container with the Scrollmanager
23709      * for auto scrolling during drag operations.
23710      */
23711     /**
23712      * @cfg {String} hlColor The color to use when visually highlighting the drag source in the afterRepair
23713      * method after a failed drop (defaults to "c3daf9" - light blue)
23714      */
23715
23716     /**
23717      * Called when a mousedown occurs in this container. Looks in {@link Roo.dd.Registry}
23718      * for a valid target to drag based on the mouse down. Override this method
23719      * to provide your own lookup logic (e.g. finding a child by class name). Make sure your returned
23720      * object has a "ddel" attribute (with an HTML Element) for other functions to work.
23721      * @param {EventObject} e The mouse down event
23722      * @return {Object} The dragData
23723      */
23724     getDragData : function(e){
23725         return Roo.dd.Registry.getHandleFromEvent(e);
23726     },
23727     
23728     /**
23729      * Called once drag threshold has been reached to initialize the proxy element. By default, it clones the
23730      * this.dragData.ddel
23731      * @param {Number} x The x position of the click on the dragged object
23732      * @param {Number} y The y position of the click on the dragged object
23733      * @return {Boolean} true to continue the drag, false to cancel
23734      */
23735     onInitDrag : function(x, y){
23736         this.proxy.update(this.dragData.ddel.cloneNode(true));
23737         this.onStartDrag(x, y);
23738         return true;
23739     },
23740     
23741     /**
23742      * Called after a repair of an invalid drop. By default, highlights this.dragData.ddel 
23743      */
23744     afterRepair : function(){
23745         if(Roo.enableFx){
23746             Roo.Element.fly(this.dragData.ddel).highlight(this.hlColor || "c3daf9");
23747         }
23748         this.dragging = false;
23749     },
23750
23751     /**
23752      * Called before a repair of an invalid drop to get the XY to animate to. By default returns
23753      * the XY of this.dragData.ddel
23754      * @param {EventObject} e The mouse up event
23755      * @return {Array} The xy location (e.g. [100, 200])
23756      */
23757     getRepairXY : function(e){
23758         return Roo.Element.fly(this.dragData.ddel).getXY();  
23759     }
23760 });/*
23761  * Based on:
23762  * Ext JS Library 1.1.1
23763  * Copyright(c) 2006-2007, Ext JS, LLC.
23764  *
23765  * Originally Released Under LGPL - original licence link has changed is not relivant.
23766  *
23767  * Fork - LGPL
23768  * <script type="text/javascript">
23769  */
23770 /**
23771  * @class Roo.dd.DropZone
23772  * @extends Roo.dd.DropTarget
23773  * This class provides a container DD instance that proxies for multiple child node targets.<br />
23774  * By default, this class requires that child nodes accepting drop are registered with {@link Roo.dd.Registry}.
23775  * @constructor
23776  * @param {String/HTMLElement/Element} el The container element
23777  * @param {Object} config
23778  */
23779 Roo.dd.DropZone = function(el, config){
23780     Roo.dd.DropZone.superclass.constructor.call(this, el, config);
23781 };
23782
23783 Roo.extend(Roo.dd.DropZone, Roo.dd.DropTarget, {
23784     /**
23785      * Returns a custom data object associated with the DOM node that is the target of the event.  By default
23786      * this looks up the event target in the {@link Roo.dd.Registry}, although you can override this method to
23787      * provide your own custom lookup.
23788      * @param {Event} e The event
23789      * @return {Object} data The custom data
23790      */
23791     getTargetFromEvent : function(e){
23792         return Roo.dd.Registry.getTargetFromEvent(e);
23793     },
23794
23795     /**
23796      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has entered a drop node
23797      * that it has registered.  This method has no default implementation and should be overridden to provide
23798      * node-specific processing if necessary.
23799      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from 
23800      * {@link #getTargetFromEvent} for this node)
23801      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23802      * @param {Event} e The event
23803      * @param {Object} data An object containing arbitrary data supplied by the drag source
23804      */
23805     onNodeEnter : function(n, dd, e, data){
23806         
23807     },
23808
23809     /**
23810      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is over a drop node
23811      * that it has registered.  The default implementation returns this.dropNotAllowed, so it should be
23812      * overridden to provide the proper feedback.
23813      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23814      * {@link #getTargetFromEvent} for this node)
23815      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23816      * @param {Event} e The event
23817      * @param {Object} data An object containing arbitrary data supplied by the drag source
23818      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23819      * underlying {@link Roo.dd.StatusProxy} can be updated
23820      */
23821     onNodeOver : function(n, dd, e, data){
23822         return this.dropAllowed;
23823     },
23824
23825     /**
23826      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dragged out of
23827      * the drop node without dropping.  This method has no default implementation and should be overridden to provide
23828      * node-specific processing if necessary.
23829      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23830      * {@link #getTargetFromEvent} for this node)
23831      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23832      * @param {Event} e The event
23833      * @param {Object} data An object containing arbitrary data supplied by the drag source
23834      */
23835     onNodeOut : function(n, dd, e, data){
23836         
23837     },
23838
23839     /**
23840      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped onto
23841      * the drop node.  The default implementation returns false, so it should be overridden to provide the
23842      * appropriate processing of the drop event and return true so that the drag source's repair action does not run.
23843      * @param {Object} nodeData The custom data associated with the drop node (this is the same value returned from
23844      * {@link #getTargetFromEvent} for this node)
23845      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23846      * @param {Event} e The event
23847      * @param {Object} data An object containing arbitrary data supplied by the drag source
23848      * @return {Boolean} True if the drop was valid, else false
23849      */
23850     onNodeDrop : function(n, dd, e, data){
23851         return false;
23852     },
23853
23854     /**
23855      * Called internally while the DropZone determines that a {@link Roo.dd.DragSource} is being dragged over it,
23856      * but not over any of its registered drop nodes.  The default implementation returns this.dropNotAllowed, so
23857      * it should be overridden to provide the proper feedback if necessary.
23858      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23859      * @param {Event} e The event
23860      * @param {Object} data An object containing arbitrary data supplied by the drag source
23861      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23862      * underlying {@link Roo.dd.StatusProxy} can be updated
23863      */
23864     onContainerOver : function(dd, e, data){
23865         return this.dropNotAllowed;
23866     },
23867
23868     /**
23869      * Called internally when the DropZone determines that a {@link Roo.dd.DragSource} has been dropped on it,
23870      * but not on any of its registered drop nodes.  The default implementation returns false, so it should be
23871      * overridden to provide the appropriate processing of the drop event if you need the drop zone itself to
23872      * be able to accept drops.  It should return true when valid so that the drag source's repair action does not run.
23873      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23874      * @param {Event} e The event
23875      * @param {Object} data An object containing arbitrary data supplied by the drag source
23876      * @return {Boolean} True if the drop was valid, else false
23877      */
23878     onContainerDrop : function(dd, e, data){
23879         return false;
23880     },
23881
23882     /**
23883      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source is now over
23884      * the zone.  The default implementation returns this.dropNotAllowed and expects that only registered drop
23885      * nodes can process drag drop operations, so if you need the drop zone itself to be able to process drops
23886      * you should override this method and provide a custom implementation.
23887      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23888      * @param {Event} e The event
23889      * @param {Object} data An object containing arbitrary data supplied by the drag source
23890      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23891      * underlying {@link Roo.dd.StatusProxy} can be updated
23892      */
23893     notifyEnter : function(dd, e, data){
23894         return this.dropNotAllowed;
23895     },
23896
23897     /**
23898      * The function a {@link Roo.dd.DragSource} calls continuously while it is being dragged over the drop zone.
23899      * This method will be called on every mouse movement while the drag source is over the drop zone.
23900      * It will call {@link #onNodeOver} while the drag source is over a registered node, and will also automatically
23901      * delegate to the appropriate node-specific methods as necessary when the drag source enters and exits
23902      * registered nodes ({@link #onNodeEnter}, {@link #onNodeOut}). If the drag source is not currently over a
23903      * registered node, it will call {@link #onContainerOver}.
23904      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23905      * @param {Event} e The event
23906      * @param {Object} data An object containing arbitrary data supplied by the drag source
23907      * @return {String} status The CSS class that communicates the drop status back to the source so that the
23908      * underlying {@link Roo.dd.StatusProxy} can be updated
23909      */
23910     notifyOver : function(dd, e, data){
23911         var n = this.getTargetFromEvent(e);
23912         if(!n){ // not over valid drop target
23913             if(this.lastOverNode){
23914                 this.onNodeOut(this.lastOverNode, dd, e, data);
23915                 this.lastOverNode = null;
23916             }
23917             return this.onContainerOver(dd, e, data);
23918         }
23919         if(this.lastOverNode != n){
23920             if(this.lastOverNode){
23921                 this.onNodeOut(this.lastOverNode, dd, e, data);
23922             }
23923             this.onNodeEnter(n, dd, e, data);
23924             this.lastOverNode = n;
23925         }
23926         return this.onNodeOver(n, dd, e, data);
23927     },
23928
23929     /**
23930      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the source has been dragged
23931      * out of the zone without dropping.  If the drag source is currently over a registered node, the notification
23932      * will be delegated to {@link #onNodeOut} for node-specific handling, otherwise it will be ignored.
23933      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop target
23934      * @param {Event} e The event
23935      * @param {Object} data An object containing arbitrary data supplied by the drag zone
23936      */
23937     notifyOut : function(dd, e, data){
23938         if(this.lastOverNode){
23939             this.onNodeOut(this.lastOverNode, dd, e, data);
23940             this.lastOverNode = null;
23941         }
23942     },
23943
23944     /**
23945      * The function a {@link Roo.dd.DragSource} calls once to notify this drop zone that the dragged item has
23946      * been dropped on it.  The drag zone will look up the target node based on the event passed in, and if there
23947      * is a node registered for that event, it will delegate to {@link #onNodeDrop} for node-specific handling,
23948      * otherwise it will call {@link #onContainerDrop}.
23949      * @param {Roo.dd.DragSource} source The drag source that was dragged over this drop zone
23950      * @param {Event} e The event
23951      * @param {Object} data An object containing arbitrary data supplied by the drag source
23952      * @return {Boolean} True if the drop was valid, else false
23953      */
23954     notifyDrop : function(dd, e, data){
23955         if(this.lastOverNode){
23956             this.onNodeOut(this.lastOverNode, dd, e, data);
23957             this.lastOverNode = null;
23958         }
23959         var n = this.getTargetFromEvent(e);
23960         return n ?
23961             this.onNodeDrop(n, dd, e, data) :
23962             this.onContainerDrop(dd, e, data);
23963     },
23964
23965     // private
23966     triggerCacheRefresh : function(){
23967         Roo.dd.DDM.refreshCache(this.groups);
23968     }  
23969 });/*
23970  * Based on:
23971  * Ext JS Library 1.1.1
23972  * Copyright(c) 2006-2007, Ext JS, LLC.
23973  *
23974  * Originally Released Under LGPL - original licence link has changed is not relivant.
23975  *
23976  * Fork - LGPL
23977  * <script type="text/javascript">
23978  */
23979
23980
23981 /**
23982  * @class Roo.data.SortTypes
23983  * @static
23984  * Defines the default sorting (casting?) comparison functions used when sorting data.
23985  */
23986 Roo.data.SortTypes = {
23987     /**
23988      * Default sort that does nothing
23989      * @param {Mixed} s The value being converted
23990      * @return {Mixed} The comparison value
23991      */
23992     none : function(s){
23993         return s;
23994     },
23995     
23996     /**
23997      * The regular expression used to strip tags
23998      * @type {RegExp}
23999      * @property
24000      */
24001     stripTagsRE : /<\/?[^>]+>/gi,
24002     
24003     /**
24004      * Strips all HTML tags to sort on text only
24005      * @param {Mixed} s The value being converted
24006      * @return {String} The comparison value
24007      */
24008     asText : function(s){
24009         return String(s).replace(this.stripTagsRE, "");
24010     },
24011     
24012     /**
24013      * Strips all HTML tags to sort on text only - Case insensitive
24014      * @param {Mixed} s The value being converted
24015      * @return {String} The comparison value
24016      */
24017     asUCText : function(s){
24018         return String(s).toUpperCase().replace(this.stripTagsRE, "");
24019     },
24020     
24021     /**
24022      * Case insensitive string
24023      * @param {Mixed} s The value being converted
24024      * @return {String} The comparison value
24025      */
24026     asUCString : function(s) {
24027         return String(s).toUpperCase();
24028     },
24029     
24030     /**
24031      * Date sorting
24032      * @param {Mixed} s The value being converted
24033      * @return {Number} The comparison value
24034      */
24035     asDate : function(s) {
24036         if(!s){
24037             return 0;
24038         }
24039         if(s instanceof Date){
24040             return s.getTime();
24041         }
24042         return Date.parse(String(s));
24043     },
24044     
24045     /**
24046      * Float sorting
24047      * @param {Mixed} s The value being converted
24048      * @return {Float} The comparison value
24049      */
24050     asFloat : function(s) {
24051         var val = parseFloat(String(s).replace(/,/g, ""));
24052         if(isNaN(val)) {
24053             val = 0;
24054         }
24055         return val;
24056     },
24057     
24058     /**
24059      * Integer sorting
24060      * @param {Mixed} s The value being converted
24061      * @return {Number} The comparison value
24062      */
24063     asInt : function(s) {
24064         var val = parseInt(String(s).replace(/,/g, ""));
24065         if(isNaN(val)) {
24066             val = 0;
24067         }
24068         return val;
24069     }
24070 };/*
24071  * Based on:
24072  * Ext JS Library 1.1.1
24073  * Copyright(c) 2006-2007, Ext JS, LLC.
24074  *
24075  * Originally Released Under LGPL - original licence link has changed is not relivant.
24076  *
24077  * Fork - LGPL
24078  * <script type="text/javascript">
24079  */
24080
24081 /**
24082 * @class Roo.data.Record
24083  * Instances of this class encapsulate both record <em>definition</em> information, and record
24084  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
24085  * to access Records cached in an {@link Roo.data.Store} object.<br>
24086  * <p>
24087  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
24088  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
24089  * objects.<br>
24090  * <p>
24091  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
24092  * @constructor
24093  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
24094  * {@link #create}. The parameters are the same.
24095  * @param {Array} data An associative Array of data values keyed by the field name.
24096  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
24097  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
24098  * not specified an integer id is generated.
24099  */
24100 Roo.data.Record = function(data, id){
24101     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
24102     this.data = data;
24103 };
24104
24105 /**
24106  * Generate a constructor for a specific record layout.
24107  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
24108  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
24109  * Each field definition object may contain the following properties: <ul>
24110  * <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,
24111  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
24112  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
24113  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
24114  * is being used, then this is a string containing the javascript expression to reference the data relative to 
24115  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
24116  * to the data item relative to the record element. If the mapping expression is the same as the field name,
24117  * this may be omitted.</p></li>
24118  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
24119  * <ul><li>auto (Default, implies no conversion)</li>
24120  * <li>string</li>
24121  * <li>int</li>
24122  * <li>float</li>
24123  * <li>boolean</li>
24124  * <li>date</li></ul></p></li>
24125  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
24126  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
24127  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
24128  * by the Reader into an object that will be stored in the Record. It is passed the
24129  * following parameters:<ul>
24130  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
24131  * </ul></p></li>
24132  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
24133  * </ul>
24134  * <br>usage:<br><pre><code>
24135 var TopicRecord = Roo.data.Record.create(
24136     {name: 'title', mapping: 'topic_title'},
24137     {name: 'author', mapping: 'username'},
24138     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
24139     {name: 'lastPost', mapping: 'post_time', type: 'date'},
24140     {name: 'lastPoster', mapping: 'user2'},
24141     {name: 'excerpt', mapping: 'post_text'}
24142 );
24143
24144 var myNewRecord = new TopicRecord({
24145     title: 'Do my job please',
24146     author: 'noobie',
24147     totalPosts: 1,
24148     lastPost: new Date(),
24149     lastPoster: 'Animal',
24150     excerpt: 'No way dude!'
24151 });
24152 myStore.add(myNewRecord);
24153 </code></pre>
24154  * @method create
24155  * @static
24156  */
24157 Roo.data.Record.create = function(o){
24158     var f = function(){
24159         f.superclass.constructor.apply(this, arguments);
24160     };
24161     Roo.extend(f, Roo.data.Record);
24162     var p = f.prototype;
24163     p.fields = new Roo.util.MixedCollection(false, function(field){
24164         return field.name;
24165     });
24166     for(var i = 0, len = o.length; i < len; i++){
24167         p.fields.add(new Roo.data.Field(o[i]));
24168     }
24169     f.getField = function(name){
24170         return p.fields.get(name);  
24171     };
24172     return f;
24173 };
24174
24175 Roo.data.Record.AUTO_ID = 1000;
24176 Roo.data.Record.EDIT = 'edit';
24177 Roo.data.Record.REJECT = 'reject';
24178 Roo.data.Record.COMMIT = 'commit';
24179
24180 Roo.data.Record.prototype = {
24181     /**
24182      * Readonly flag - true if this record has been modified.
24183      * @type Boolean
24184      */
24185     dirty : false,
24186     editing : false,
24187     error: null,
24188     modified: null,
24189
24190     // private
24191     join : function(store){
24192         this.store = store;
24193     },
24194
24195     /**
24196      * Set the named field to the specified value.
24197      * @param {String} name The name of the field to set.
24198      * @param {Object} value The value to set the field to.
24199      */
24200     set : function(name, value){
24201         if(this.data[name] == value){
24202             return;
24203         }
24204         this.dirty = true;
24205         if(!this.modified){
24206             this.modified = {};
24207         }
24208         if(typeof this.modified[name] == 'undefined'){
24209             this.modified[name] = this.data[name];
24210         }
24211         this.data[name] = value;
24212         if(!this.editing && this.store){
24213             this.store.afterEdit(this);
24214         }       
24215     },
24216
24217     /**
24218      * Get the value of the named field.
24219      * @param {String} name The name of the field to get the value of.
24220      * @return {Object} The value of the field.
24221      */
24222     get : function(name){
24223         return this.data[name]; 
24224     },
24225
24226     // private
24227     beginEdit : function(){
24228         this.editing = true;
24229         this.modified = {}; 
24230     },
24231
24232     // private
24233     cancelEdit : function(){
24234         this.editing = false;
24235         delete this.modified;
24236     },
24237
24238     // private
24239     endEdit : function(){
24240         this.editing = false;
24241         if(this.dirty && this.store){
24242             this.store.afterEdit(this);
24243         }
24244     },
24245
24246     /**
24247      * Usually called by the {@link Roo.data.Store} which owns the Record.
24248      * Rejects all changes made to the Record since either creation, or the last commit operation.
24249      * Modified fields are reverted to their original values.
24250      * <p>
24251      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24252      * of reject operations.
24253      */
24254     reject : function(){
24255         var m = this.modified;
24256         for(var n in m){
24257             if(typeof m[n] != "function"){
24258                 this.data[n] = m[n];
24259             }
24260         }
24261         this.dirty = false;
24262         delete this.modified;
24263         this.editing = false;
24264         if(this.store){
24265             this.store.afterReject(this);
24266         }
24267     },
24268
24269     /**
24270      * Usually called by the {@link Roo.data.Store} which owns the Record.
24271      * Commits all changes made to the Record since either creation, or the last commit operation.
24272      * <p>
24273      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
24274      * of commit operations.
24275      */
24276     commit : function(){
24277         this.dirty = false;
24278         delete this.modified;
24279         this.editing = false;
24280         if(this.store){
24281             this.store.afterCommit(this);
24282         }
24283     },
24284
24285     // private
24286     hasError : function(){
24287         return this.error != null;
24288     },
24289
24290     // private
24291     clearError : function(){
24292         this.error = null;
24293     },
24294
24295     /**
24296      * Creates a copy of this record.
24297      * @param {String} id (optional) A new record id if you don't want to use this record's id
24298      * @return {Record}
24299      */
24300     copy : function(newId) {
24301         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
24302     }
24303 };/*
24304  * Based on:
24305  * Ext JS Library 1.1.1
24306  * Copyright(c) 2006-2007, Ext JS, LLC.
24307  *
24308  * Originally Released Under LGPL - original licence link has changed is not relivant.
24309  *
24310  * Fork - LGPL
24311  * <script type="text/javascript">
24312  */
24313
24314
24315
24316 /**
24317  * @class Roo.data.Store
24318  * @extends Roo.util.Observable
24319  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
24320  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
24321  * <p>
24322  * 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
24323  * has no knowledge of the format of the data returned by the Proxy.<br>
24324  * <p>
24325  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
24326  * instances from the data object. These records are cached and made available through accessor functions.
24327  * @constructor
24328  * Creates a new Store.
24329  * @param {Object} config A config object containing the objects needed for the Store to access data,
24330  * and read the data into Records.
24331  */
24332 Roo.data.Store = function(config){
24333     this.data = new Roo.util.MixedCollection(false);
24334     this.data.getKey = function(o){
24335         return o.id;
24336     };
24337     this.baseParams = {};
24338     // private
24339     this.paramNames = {
24340         "start" : "start",
24341         "limit" : "limit",
24342         "sort" : "sort",
24343         "dir" : "dir",
24344         "multisort" : "_multisort"
24345     };
24346
24347     if(config && config.data){
24348         this.inlineData = config.data;
24349         delete config.data;
24350     }
24351
24352     Roo.apply(this, config);
24353     
24354     if(this.reader){ // reader passed
24355         this.reader = Roo.factory(this.reader, Roo.data);
24356         this.reader.xmodule = this.xmodule || false;
24357         if(!this.recordType){
24358             this.recordType = this.reader.recordType;
24359         }
24360         if(this.reader.onMetaChange){
24361             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
24362         }
24363     }
24364
24365     if(this.recordType){
24366         this.fields = this.recordType.prototype.fields;
24367     }
24368     this.modified = [];
24369
24370     this.addEvents({
24371         /**
24372          * @event datachanged
24373          * Fires when the data cache has changed, and a widget which is using this Store
24374          * as a Record cache should refresh its view.
24375          * @param {Store} this
24376          */
24377         datachanged : true,
24378         /**
24379          * @event metachange
24380          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
24381          * @param {Store} this
24382          * @param {Object} meta The JSON metadata
24383          */
24384         metachange : true,
24385         /**
24386          * @event add
24387          * Fires when Records have been added to the Store
24388          * @param {Store} this
24389          * @param {Roo.data.Record[]} records The array of Records added
24390          * @param {Number} index The index at which the record(s) were added
24391          */
24392         add : true,
24393         /**
24394          * @event remove
24395          * Fires when a Record has been removed from the Store
24396          * @param {Store} this
24397          * @param {Roo.data.Record} record The Record that was removed
24398          * @param {Number} index The index at which the record was removed
24399          */
24400         remove : true,
24401         /**
24402          * @event update
24403          * Fires when a Record has been updated
24404          * @param {Store} this
24405          * @param {Roo.data.Record} record The Record that was updated
24406          * @param {String} operation The update operation being performed.  Value may be one of:
24407          * <pre><code>
24408  Roo.data.Record.EDIT
24409  Roo.data.Record.REJECT
24410  Roo.data.Record.COMMIT
24411          * </code></pre>
24412          */
24413         update : true,
24414         /**
24415          * @event clear
24416          * Fires when the data cache has been cleared.
24417          * @param {Store} this
24418          */
24419         clear : true,
24420         /**
24421          * @event beforeload
24422          * Fires before a request is made for a new data object.  If the beforeload handler returns false
24423          * the load action will be canceled.
24424          * @param {Store} this
24425          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24426          */
24427         beforeload : true,
24428         /**
24429          * @event beforeloadadd
24430          * Fires after a new set of Records has been loaded.
24431          * @param {Store} this
24432          * @param {Roo.data.Record[]} records The Records that were loaded
24433          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24434          */
24435         beforeloadadd : true,
24436         /**
24437          * @event load
24438          * Fires after a new set of Records has been loaded, before they are added to the store.
24439          * @param {Store} this
24440          * @param {Roo.data.Record[]} records The Records that were loaded
24441          * @param {Object} options The loading options that were specified (see {@link #load} for details)
24442          * @params {Object} return from reader
24443          */
24444         load : true,
24445         /**
24446          * @event loadexception
24447          * Fires if an exception occurs in the Proxy during loading.
24448          * Called with the signature of the Proxy's "loadexception" event.
24449          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
24450          * 
24451          * @param {Proxy} 
24452          * @param {Object} return from JsonData.reader() - success, totalRecords, records
24453          * @param {Object} load options 
24454          * @param {Object} jsonData from your request (normally this contains the Exception)
24455          */
24456         loadexception : true
24457     });
24458     
24459     if(this.proxy){
24460         this.proxy = Roo.factory(this.proxy, Roo.data);
24461         this.proxy.xmodule = this.xmodule || false;
24462         this.relayEvents(this.proxy,  ["loadexception"]);
24463     }
24464     this.sortToggle = {};
24465     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
24466
24467     Roo.data.Store.superclass.constructor.call(this);
24468
24469     if(this.inlineData){
24470         this.loadData(this.inlineData);
24471         delete this.inlineData;
24472     }
24473 };
24474
24475 Roo.extend(Roo.data.Store, Roo.util.Observable, {
24476      /**
24477     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
24478     * without a remote query - used by combo/forms at present.
24479     */
24480     
24481     /**
24482     * @cfg {Roo.data.DataProxy} proxy [required] The Proxy object which provides access to a data object.
24483     */
24484     /**
24485     * @cfg {Array} data Inline data to be loaded when the store is initialized.
24486     */
24487     /**
24488     * @cfg {Roo.data.DataReader} reader [required]  The Reader object which processes the data object and returns
24489     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
24490     */
24491     /**
24492     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
24493     * on any HTTP request
24494     */
24495     /**
24496     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
24497     */
24498     /**
24499     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
24500     */
24501     multiSort: false,
24502     /**
24503     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
24504     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
24505     */
24506     remoteSort : false,
24507
24508     /**
24509     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
24510      * loaded or when a record is removed. (defaults to false).
24511     */
24512     pruneModifiedRecords : false,
24513
24514     // private
24515     lastOptions : null,
24516
24517     /**
24518      * Add Records to the Store and fires the add event.
24519      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24520      */
24521     add : function(records){
24522         records = [].concat(records);
24523         for(var i = 0, len = records.length; i < len; i++){
24524             records[i].join(this);
24525         }
24526         var index = this.data.length;
24527         this.data.addAll(records);
24528         this.fireEvent("add", this, records, index);
24529     },
24530
24531     /**
24532      * Remove a Record from the Store and fires the remove event.
24533      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
24534      */
24535     remove : function(record){
24536         var index = this.data.indexOf(record);
24537         this.data.removeAt(index);
24538  
24539         if(this.pruneModifiedRecords){
24540             this.modified.remove(record);
24541         }
24542         this.fireEvent("remove", this, record, index);
24543     },
24544
24545     /**
24546      * Remove all Records from the Store and fires the clear event.
24547      */
24548     removeAll : function(){
24549         this.data.clear();
24550         if(this.pruneModifiedRecords){
24551             this.modified = [];
24552         }
24553         this.fireEvent("clear", this);
24554     },
24555
24556     /**
24557      * Inserts Records to the Store at the given index and fires the add event.
24558      * @param {Number} index The start index at which to insert the passed Records.
24559      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
24560      */
24561     insert : function(index, records){
24562         records = [].concat(records);
24563         for(var i = 0, len = records.length; i < len; i++){
24564             this.data.insert(index, records[i]);
24565             records[i].join(this);
24566         }
24567         this.fireEvent("add", this, records, index);
24568     },
24569
24570     /**
24571      * Get the index within the cache of the passed Record.
24572      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
24573      * @return {Number} The index of the passed Record. Returns -1 if not found.
24574      */
24575     indexOf : function(record){
24576         return this.data.indexOf(record);
24577     },
24578
24579     /**
24580      * Get the index within the cache of the Record with the passed id.
24581      * @param {String} id The id of the Record to find.
24582      * @return {Number} The index of the Record. Returns -1 if not found.
24583      */
24584     indexOfId : function(id){
24585         return this.data.indexOfKey(id);
24586     },
24587
24588     /**
24589      * Get the Record with the specified id.
24590      * @param {String} id The id of the Record to find.
24591      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
24592      */
24593     getById : function(id){
24594         return this.data.key(id);
24595     },
24596
24597     /**
24598      * Get the Record at the specified index.
24599      * @param {Number} index The index of the Record to find.
24600      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
24601      */
24602     getAt : function(index){
24603         return this.data.itemAt(index);
24604     },
24605
24606     /**
24607      * Returns a range of Records between specified indices.
24608      * @param {Number} startIndex (optional) The starting index (defaults to 0)
24609      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
24610      * @return {Roo.data.Record[]} An array of Records
24611      */
24612     getRange : function(start, end){
24613         return this.data.getRange(start, end);
24614     },
24615
24616     // private
24617     storeOptions : function(o){
24618         o = Roo.apply({}, o);
24619         delete o.callback;
24620         delete o.scope;
24621         this.lastOptions = o;
24622     },
24623
24624     /**
24625      * Loads the Record cache from the configured Proxy using the configured Reader.
24626      * <p>
24627      * If using remote paging, then the first load call must specify the <em>start</em>
24628      * and <em>limit</em> properties in the options.params property to establish the initial
24629      * position within the dataset, and the number of Records to cache on each read from the Proxy.
24630      * <p>
24631      * <strong>It is important to note that for remote data sources, loading is asynchronous,
24632      * and this call will return before the new data has been loaded. Perform any post-processing
24633      * in a callback function, or in a "load" event handler.</strong>
24634      * <p>
24635      * @param {Object} options An object containing properties which control loading options:<ul>
24636      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
24637      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
24638      * passed the following arguments:<ul>
24639      * <li>r : Roo.data.Record[]</li>
24640      * <li>options: Options object from the load call</li>
24641      * <li>success: Boolean success indicator</li></ul></li>
24642      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
24643      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
24644      * </ul>
24645      */
24646     load : function(options){
24647         options = options || {};
24648         if(this.fireEvent("beforeload", this, options) !== false){
24649             this.storeOptions(options);
24650             var p = Roo.apply(options.params || {}, this.baseParams);
24651             // if meta was not loaded from remote source.. try requesting it.
24652             if (!this.reader.metaFromRemote) {
24653                 p._requestMeta = 1;
24654             }
24655             if(this.sortInfo && this.remoteSort){
24656                 var pn = this.paramNames;
24657                 p[pn["sort"]] = this.sortInfo.field;
24658                 p[pn["dir"]] = this.sortInfo.direction;
24659             }
24660             if (this.multiSort) {
24661                 var pn = this.paramNames;
24662                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
24663             }
24664             
24665             this.proxy.load(p, this.reader, this.loadRecords, this, options);
24666         }
24667     },
24668
24669     /**
24670      * Reloads the Record cache from the configured Proxy using the configured Reader and
24671      * the options from the last load operation performed.
24672      * @param {Object} options (optional) An object containing properties which may override the options
24673      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
24674      * the most recently used options are reused).
24675      */
24676     reload : function(options){
24677         this.load(Roo.applyIf(options||{}, this.lastOptions));
24678     },
24679
24680     // private
24681     // Called as a callback by the Reader during a load operation.
24682     loadRecords : function(o, options, success){
24683          
24684         if(!o){
24685             if(success !== false){
24686                 this.fireEvent("load", this, [], options, o);
24687             }
24688             if(options.callback){
24689                 options.callback.call(options.scope || this, [], options, false);
24690             }
24691             return;
24692         }
24693         // if data returned failure - throw an exception.
24694         if (o.success === false) {
24695             // show a message if no listener is registered.
24696             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
24697                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
24698             }
24699             // loadmask wil be hooked into this..
24700             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
24701             return;
24702         }
24703         var r = o.records, t = o.totalRecords || r.length;
24704         
24705         this.fireEvent("beforeloadadd", this, r, options, o);
24706         
24707         if(!options || options.add !== true){
24708             if(this.pruneModifiedRecords){
24709                 this.modified = [];
24710             }
24711             for(var i = 0, len = r.length; i < len; i++){
24712                 r[i].join(this);
24713             }
24714             if(this.snapshot){
24715                 this.data = this.snapshot;
24716                 delete this.snapshot;
24717             }
24718             this.data.clear();
24719             this.data.addAll(r);
24720             this.totalLength = t;
24721             this.applySort();
24722             this.fireEvent("datachanged", this);
24723         }else{
24724             this.totalLength = Math.max(t, this.data.length+r.length);
24725             this.add(r);
24726         }
24727         
24728         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
24729                 
24730             var e = new Roo.data.Record({});
24731
24732             e.set(this.parent.displayField, this.parent.emptyTitle);
24733             e.set(this.parent.valueField, '');
24734
24735             this.insert(0, e);
24736         }
24737             
24738         this.fireEvent("load", this, r, options, o);
24739         if(options.callback){
24740             options.callback.call(options.scope || this, r, options, true);
24741         }
24742     },
24743
24744
24745     /**
24746      * Loads data from a passed data block. A Reader which understands the format of the data
24747      * must have been configured in the constructor.
24748      * @param {Object} data The data block from which to read the Records.  The format of the data expected
24749      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
24750      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
24751      */
24752     loadData : function(o, append){
24753         var r = this.reader.readRecords(o);
24754         this.loadRecords(r, {add: append}, true);
24755     },
24756     
24757      /**
24758      * using 'cn' the nested child reader read the child array into it's child stores.
24759      * @param {Object} rec The record with a 'children array
24760      */
24761     loadDataFromChildren : function(rec)
24762     {
24763         this.loadData(this.reader.toLoadData(rec));
24764     },
24765     
24766
24767     /**
24768      * Gets the number of cached records.
24769      * <p>
24770      * <em>If using paging, this may not be the total size of the dataset. If the data object
24771      * used by the Reader contains the dataset size, then the getTotalCount() function returns
24772      * the data set size</em>
24773      */
24774     getCount : function(){
24775         return this.data.length || 0;
24776     },
24777
24778     /**
24779      * Gets the total number of records in the dataset as returned by the server.
24780      * <p>
24781      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
24782      * the dataset size</em>
24783      */
24784     getTotalCount : function(){
24785         return this.totalLength || 0;
24786     },
24787
24788     /**
24789      * Returns the sort state of the Store as an object with two properties:
24790      * <pre><code>
24791  field {String} The name of the field by which the Records are sorted
24792  direction {String} The sort order, "ASC" or "DESC"
24793      * </code></pre>
24794      */
24795     getSortState : function(){
24796         return this.sortInfo;
24797     },
24798
24799     // private
24800     applySort : function(){
24801         if(this.sortInfo && !this.remoteSort){
24802             var s = this.sortInfo, f = s.field;
24803             var st = this.fields.get(f).sortType;
24804             var fn = function(r1, r2){
24805                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
24806                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
24807             };
24808             this.data.sort(s.direction, fn);
24809             if(this.snapshot && this.snapshot != this.data){
24810                 this.snapshot.sort(s.direction, fn);
24811             }
24812         }
24813     },
24814
24815     /**
24816      * Sets the default sort column and order to be used by the next load operation.
24817      * @param {String} fieldName The name of the field to sort by.
24818      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24819      */
24820     setDefaultSort : function(field, dir){
24821         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
24822     },
24823
24824     /**
24825      * Sort the Records.
24826      * If remote sorting is used, the sort is performed on the server, and the cache is
24827      * reloaded. If local sorting is used, the cache is sorted internally.
24828      * @param {String} fieldName The name of the field to sort by.
24829      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
24830      */
24831     sort : function(fieldName, dir){
24832         var f = this.fields.get(fieldName);
24833         if(!dir){
24834             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
24835             
24836             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
24837                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
24838             }else{
24839                 dir = f.sortDir;
24840             }
24841         }
24842         this.sortToggle[f.name] = dir;
24843         this.sortInfo = {field: f.name, direction: dir};
24844         if(!this.remoteSort){
24845             this.applySort();
24846             this.fireEvent("datachanged", this);
24847         }else{
24848             this.load(this.lastOptions);
24849         }
24850     },
24851
24852     /**
24853      * Calls the specified function for each of the Records in the cache.
24854      * @param {Function} fn The function to call. The Record is passed as the first parameter.
24855      * Returning <em>false</em> aborts and exits the iteration.
24856      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
24857      */
24858     each : function(fn, scope){
24859         this.data.each(fn, scope);
24860     },
24861
24862     /**
24863      * Gets all records modified since the last commit.  Modified records are persisted across load operations
24864      * (e.g., during paging).
24865      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
24866      */
24867     getModifiedRecords : function(){
24868         return this.modified;
24869     },
24870
24871     // private
24872     createFilterFn : function(property, value, anyMatch){
24873         if(!value.exec){ // not a regex
24874             value = String(value);
24875             if(value.length == 0){
24876                 return false;
24877             }
24878             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
24879         }
24880         return function(r){
24881             return value.test(r.data[property]);
24882         };
24883     },
24884
24885     /**
24886      * Sums the value of <i>property</i> for each record between start and end and returns the result.
24887      * @param {String} property A field on your records
24888      * @param {Number} start The record index to start at (defaults to 0)
24889      * @param {Number} end The last record index to include (defaults to length - 1)
24890      * @return {Number} The sum
24891      */
24892     sum : function(property, start, end){
24893         var rs = this.data.items, v = 0;
24894         start = start || 0;
24895         end = (end || end === 0) ? end : rs.length-1;
24896
24897         for(var i = start; i <= end; i++){
24898             v += (rs[i].data[property] || 0);
24899         }
24900         return v;
24901     },
24902
24903     /**
24904      * Filter the records by a specified property.
24905      * @param {String} field A field on your records
24906      * @param {String/RegExp} value Either a string that the field
24907      * should start with or a RegExp to test against the field
24908      * @param {Boolean} anyMatch True to match any part not just the beginning
24909      */
24910     filter : function(property, value, anyMatch){
24911         var fn = this.createFilterFn(property, value, anyMatch);
24912         return fn ? this.filterBy(fn) : this.clearFilter();
24913     },
24914
24915     /**
24916      * Filter by a function. The specified function will be called with each
24917      * record in this data source. If the function returns true the record is included,
24918      * otherwise it is filtered.
24919      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24920      * @param {Object} scope (optional) The scope of the function (defaults to this)
24921      */
24922     filterBy : function(fn, scope){
24923         this.snapshot = this.snapshot || this.data;
24924         this.data = this.queryBy(fn, scope||this);
24925         this.fireEvent("datachanged", this);
24926     },
24927
24928     /**
24929      * Query the records by a specified property.
24930      * @param {String} field A field on your records
24931      * @param {String/RegExp} value Either a string that the field
24932      * should start with or a RegExp to test against the field
24933      * @param {Boolean} anyMatch True to match any part not just the beginning
24934      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24935      */
24936     query : function(property, value, anyMatch){
24937         var fn = this.createFilterFn(property, value, anyMatch);
24938         return fn ? this.queryBy(fn) : this.data.clone();
24939     },
24940
24941     /**
24942      * Query by a function. The specified function will be called with each
24943      * record in this data source. If the function returns true the record is included
24944      * in the results.
24945      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
24946      * @param {Object} scope (optional) The scope of the function (defaults to this)
24947       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
24948      **/
24949     queryBy : function(fn, scope){
24950         var data = this.snapshot || this.data;
24951         return data.filterBy(fn, scope||this);
24952     },
24953
24954     /**
24955      * Collects unique values for a particular dataIndex from this store.
24956      * @param {String} dataIndex The property to collect
24957      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
24958      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
24959      * @return {Array} An array of the unique values
24960      **/
24961     collect : function(dataIndex, allowNull, bypassFilter){
24962         var d = (bypassFilter === true && this.snapshot) ?
24963                 this.snapshot.items : this.data.items;
24964         var v, sv, r = [], l = {};
24965         for(var i = 0, len = d.length; i < len; i++){
24966             v = d[i].data[dataIndex];
24967             sv = String(v);
24968             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
24969                 l[sv] = true;
24970                 r[r.length] = v;
24971             }
24972         }
24973         return r;
24974     },
24975
24976     /**
24977      * Revert to a view of the Record cache with no filtering applied.
24978      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
24979      */
24980     clearFilter : function(suppressEvent){
24981         if(this.snapshot && this.snapshot != this.data){
24982             this.data = this.snapshot;
24983             delete this.snapshot;
24984             if(suppressEvent !== true){
24985                 this.fireEvent("datachanged", this);
24986             }
24987         }
24988     },
24989
24990     // private
24991     afterEdit : function(record){
24992         if(this.modified.indexOf(record) == -1){
24993             this.modified.push(record);
24994         }
24995         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
24996     },
24997     
24998     // private
24999     afterReject : function(record){
25000         this.modified.remove(record);
25001         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
25002     },
25003
25004     // private
25005     afterCommit : function(record){
25006         this.modified.remove(record);
25007         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
25008     },
25009
25010     /**
25011      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
25012      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
25013      */
25014     commitChanges : function(){
25015         var m = this.modified.slice(0);
25016         this.modified = [];
25017         for(var i = 0, len = m.length; i < len; i++){
25018             m[i].commit();
25019         }
25020     },
25021
25022     /**
25023      * Cancel outstanding changes on all changed records.
25024      */
25025     rejectChanges : function(){
25026         var m = this.modified.slice(0);
25027         this.modified = [];
25028         for(var i = 0, len = m.length; i < len; i++){
25029             m[i].reject();
25030         }
25031     },
25032
25033     onMetaChange : function(meta, rtype, o){
25034         this.recordType = rtype;
25035         this.fields = rtype.prototype.fields;
25036         delete this.snapshot;
25037         this.sortInfo = meta.sortInfo || this.sortInfo;
25038         this.modified = [];
25039         this.fireEvent('metachange', this, this.reader.meta);
25040     },
25041     
25042     moveIndex : function(data, type)
25043     {
25044         var index = this.indexOf(data);
25045         
25046         var newIndex = index + type;
25047         
25048         this.remove(data);
25049         
25050         this.insert(newIndex, data);
25051         
25052     }
25053 });/*
25054  * Based on:
25055  * Ext JS Library 1.1.1
25056  * Copyright(c) 2006-2007, Ext JS, LLC.
25057  *
25058  * Originally Released Under LGPL - original licence link has changed is not relivant.
25059  *
25060  * Fork - LGPL
25061  * <script type="text/javascript">
25062  */
25063
25064 /**
25065  * @class Roo.data.SimpleStore
25066  * @extends Roo.data.Store
25067  * Small helper class to make creating Stores from Array data easier.
25068  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
25069  * @cfg {Array} fields An array of field definition objects, or field name strings.
25070  * @cfg {Object} an existing reader (eg. copied from another store)
25071  * @cfg {Array} data The multi-dimensional array of data
25072  * @cfg {Roo.data.DataProxy} proxy [not-required]  
25073  * @cfg {Roo.data.Reader} reader  [not-required] 
25074  * @constructor
25075  * @param {Object} config
25076  */
25077 Roo.data.SimpleStore = function(config)
25078 {
25079     Roo.data.SimpleStore.superclass.constructor.call(this, {
25080         isLocal : true,
25081         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
25082                 id: config.id
25083             },
25084             Roo.data.Record.create(config.fields)
25085         ),
25086         proxy : new Roo.data.MemoryProxy(config.data)
25087     });
25088     this.load();
25089 };
25090 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
25091  * Based on:
25092  * Ext JS Library 1.1.1
25093  * Copyright(c) 2006-2007, Ext JS, LLC.
25094  *
25095  * Originally Released Under LGPL - original licence link has changed is not relivant.
25096  *
25097  * Fork - LGPL
25098  * <script type="text/javascript">
25099  */
25100
25101 /**
25102 /**
25103  * @extends Roo.data.Store
25104  * @class Roo.data.JsonStore
25105  * Small helper class to make creating Stores for JSON data easier. <br/>
25106 <pre><code>
25107 var store = new Roo.data.JsonStore({
25108     url: 'get-images.php',
25109     root: 'images',
25110     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
25111 });
25112 </code></pre>
25113  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
25114  * JsonReader and HttpProxy (unless inline data is provided).</b>
25115  * @cfg {Array} fields An array of field definition objects, or field name strings.
25116  * @constructor
25117  * @param {Object} config
25118  */
25119 Roo.data.JsonStore = function(c){
25120     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
25121         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
25122         reader: new Roo.data.JsonReader(c, c.fields)
25123     }));
25124 };
25125 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
25126  * Based on:
25127  * Ext JS Library 1.1.1
25128  * Copyright(c) 2006-2007, Ext JS, LLC.
25129  *
25130  * Originally Released Under LGPL - original licence link has changed is not relivant.
25131  *
25132  * Fork - LGPL
25133  * <script type="text/javascript">
25134  */
25135
25136  
25137 Roo.data.Field = function(config){
25138     if(typeof config == "string"){
25139         config = {name: config};
25140     }
25141     Roo.apply(this, config);
25142     
25143     if(!this.type){
25144         this.type = "auto";
25145     }
25146     
25147     var st = Roo.data.SortTypes;
25148     // named sortTypes are supported, here we look them up
25149     if(typeof this.sortType == "string"){
25150         this.sortType = st[this.sortType];
25151     }
25152     
25153     // set default sortType for strings and dates
25154     if(!this.sortType){
25155         switch(this.type){
25156             case "string":
25157                 this.sortType = st.asUCString;
25158                 break;
25159             case "date":
25160                 this.sortType = st.asDate;
25161                 break;
25162             default:
25163                 this.sortType = st.none;
25164         }
25165     }
25166
25167     // define once
25168     var stripRe = /[\$,%]/g;
25169
25170     // prebuilt conversion function for this field, instead of
25171     // switching every time we're reading a value
25172     if(!this.convert){
25173         var cv, dateFormat = this.dateFormat;
25174         switch(this.type){
25175             case "":
25176             case "auto":
25177             case undefined:
25178                 cv = function(v){ return v; };
25179                 break;
25180             case "string":
25181                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
25182                 break;
25183             case "int":
25184                 cv = function(v){
25185                     return v !== undefined && v !== null && v !== '' ?
25186                            parseInt(String(v).replace(stripRe, ""), 10) : '';
25187                     };
25188                 break;
25189             case "float":
25190                 cv = function(v){
25191                     return v !== undefined && v !== null && v !== '' ?
25192                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
25193                     };
25194                 break;
25195             case "bool":
25196             case "boolean":
25197                 cv = function(v){ return v === true || v === "true" || v == 1; };
25198                 break;
25199             case "date":
25200                 cv = function(v){
25201                     if(!v){
25202                         return '';
25203                     }
25204                     if(v instanceof Date){
25205                         return v;
25206                     }
25207                     if(dateFormat){
25208                         if(dateFormat == "timestamp"){
25209                             return new Date(v*1000);
25210                         }
25211                         return Date.parseDate(v, dateFormat);
25212                     }
25213                     var parsed = Date.parse(v);
25214                     return parsed ? new Date(parsed) : null;
25215                 };
25216              break;
25217             
25218         }
25219         this.convert = cv;
25220     }
25221 };
25222
25223 Roo.data.Field.prototype = {
25224     dateFormat: null,
25225     defaultValue: "",
25226     mapping: null,
25227     sortType : null,
25228     sortDir : "ASC"
25229 };/*
25230  * Based on:
25231  * Ext JS Library 1.1.1
25232  * Copyright(c) 2006-2007, Ext JS, LLC.
25233  *
25234  * Originally Released Under LGPL - original licence link has changed is not relivant.
25235  *
25236  * Fork - LGPL
25237  * <script type="text/javascript">
25238  */
25239  
25240 // Base class for reading structured data from a data source.  This class is intended to be
25241 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
25242
25243 /**
25244  * @class Roo.data.DataReader
25245  * @abstract
25246  * Base class for reading structured data from a data source.  This class is intended to be
25247  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
25248  */
25249
25250 Roo.data.DataReader = function(meta, recordType){
25251     
25252     this.meta = meta;
25253     
25254     this.recordType = recordType instanceof Array ? 
25255         Roo.data.Record.create(recordType) : recordType;
25256 };
25257
25258 Roo.data.DataReader.prototype = {
25259     
25260     
25261     readerType : 'Data',
25262      /**
25263      * Create an empty record
25264      * @param {Object} data (optional) - overlay some values
25265      * @return {Roo.data.Record} record created.
25266      */
25267     newRow :  function(d) {
25268         var da =  {};
25269         this.recordType.prototype.fields.each(function(c) {
25270             switch( c.type) {
25271                 case 'int' : da[c.name] = 0; break;
25272                 case 'date' : da[c.name] = new Date(); break;
25273                 case 'float' : da[c.name] = 0.0; break;
25274                 case 'boolean' : da[c.name] = false; break;
25275                 default : da[c.name] = ""; break;
25276             }
25277             
25278         });
25279         return new this.recordType(Roo.apply(da, d));
25280     }
25281     
25282     
25283 };/*
25284  * Based on:
25285  * Ext JS Library 1.1.1
25286  * Copyright(c) 2006-2007, Ext JS, LLC.
25287  *
25288  * Originally Released Under LGPL - original licence link has changed is not relivant.
25289  *
25290  * Fork - LGPL
25291  * <script type="text/javascript">
25292  */
25293
25294 /**
25295  * @class Roo.data.DataProxy
25296  * @extends Roo.util.Observable
25297  * @abstract
25298  * This class is an abstract base class for implementations which provide retrieval of
25299  * unformatted data objects.<br>
25300  * <p>
25301  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
25302  * (of the appropriate type which knows how to parse the data object) to provide a block of
25303  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
25304  * <p>
25305  * Custom implementations must implement the load method as described in
25306  * {@link Roo.data.HttpProxy#load}.
25307  */
25308 Roo.data.DataProxy = function(){
25309     this.addEvents({
25310         /**
25311          * @event beforeload
25312          * Fires before a network request is made to retrieve a data object.
25313          * @param {Object} This DataProxy object.
25314          * @param {Object} params The params parameter to the load function.
25315          */
25316         beforeload : true,
25317         /**
25318          * @event load
25319          * Fires before the load method's callback is called.
25320          * @param {Object} This DataProxy object.
25321          * @param {Object} o The data object.
25322          * @param {Object} arg The callback argument object passed to the load function.
25323          */
25324         load : true,
25325         /**
25326          * @event loadexception
25327          * Fires if an Exception occurs during data retrieval.
25328          * @param {Object} This DataProxy object.
25329          * @param {Object} o The data object.
25330          * @param {Object} arg The callback argument object passed to the load function.
25331          * @param {Object} e The Exception.
25332          */
25333         loadexception : true
25334     });
25335     Roo.data.DataProxy.superclass.constructor.call(this);
25336 };
25337
25338 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
25339
25340     /**
25341      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
25342      */
25343 /*
25344  * Based on:
25345  * Ext JS Library 1.1.1
25346  * Copyright(c) 2006-2007, Ext JS, LLC.
25347  *
25348  * Originally Released Under LGPL - original licence link has changed is not relivant.
25349  *
25350  * Fork - LGPL
25351  * <script type="text/javascript">
25352  */
25353 /**
25354  * @class Roo.data.MemoryProxy
25355  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
25356  * to the Reader when its load method is called.
25357  * @constructor
25358  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
25359  */
25360 Roo.data.MemoryProxy = function(data){
25361     if (data.data) {
25362         data = data.data;
25363     }
25364     Roo.data.MemoryProxy.superclass.constructor.call(this);
25365     this.data = data;
25366 };
25367
25368 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
25369     
25370     /**
25371      * Load data from the requested source (in this case an in-memory
25372      * data object passed to the constructor), read the data object into
25373      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25374      * process that block using the passed callback.
25375      * @param {Object} params This parameter is not used by the MemoryProxy class.
25376      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25377      * object into a block of Roo.data.Records.
25378      * @param {Function} callback The function into which to pass the block of Roo.data.records.
25379      * The function must be passed <ul>
25380      * <li>The Record block object</li>
25381      * <li>The "arg" argument from the load function</li>
25382      * <li>A boolean success indicator</li>
25383      * </ul>
25384      * @param {Object} scope The scope in which to call the callback
25385      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25386      */
25387     load : function(params, reader, callback, scope, arg){
25388         params = params || {};
25389         var result;
25390         try {
25391             result = reader.readRecords(params.data ? params.data :this.data);
25392         }catch(e){
25393             this.fireEvent("loadexception", this, arg, null, e);
25394             callback.call(scope, null, arg, false);
25395             return;
25396         }
25397         callback.call(scope, result, arg, true);
25398     },
25399     
25400     // private
25401     update : function(params, records){
25402         
25403     }
25404 });/*
25405  * Based on:
25406  * Ext JS Library 1.1.1
25407  * Copyright(c) 2006-2007, Ext JS, LLC.
25408  *
25409  * Originally Released Under LGPL - original licence link has changed is not relivant.
25410  *
25411  * Fork - LGPL
25412  * <script type="text/javascript">
25413  */
25414 /**
25415  * @class Roo.data.HttpProxy
25416  * @extends Roo.data.DataProxy
25417  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
25418  * configured to reference a certain URL.<br><br>
25419  * <p>
25420  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
25421  * from which the running page was served.<br><br>
25422  * <p>
25423  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
25424  * <p>
25425  * Be aware that to enable the browser to parse an XML document, the server must set
25426  * the Content-Type header in the HTTP response to "text/xml".
25427  * @constructor
25428  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
25429  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
25430  * will be used to make the request.
25431  */
25432 Roo.data.HttpProxy = function(conn){
25433     Roo.data.HttpProxy.superclass.constructor.call(this);
25434     // is conn a conn config or a real conn?
25435     this.conn = conn;
25436     this.useAjax = !conn || !conn.events;
25437   
25438 };
25439
25440 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
25441     // thse are take from connection...
25442     
25443     /**
25444      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
25445      */
25446     /**
25447      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
25448      * extra parameters to each request made by this object. (defaults to undefined)
25449      */
25450     /**
25451      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
25452      *  to each request made by this object. (defaults to undefined)
25453      */
25454     /**
25455      * @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)
25456      */
25457     /**
25458      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
25459      */
25460      /**
25461      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
25462      * @type Boolean
25463      */
25464   
25465
25466     /**
25467      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
25468      * @type Boolean
25469      */
25470     /**
25471      * Return the {@link Roo.data.Connection} object being used by this Proxy.
25472      * @return {Connection} The Connection object. This object may be used to subscribe to events on
25473      * a finer-grained basis than the DataProxy events.
25474      */
25475     getConnection : function(){
25476         return this.useAjax ? Roo.Ajax : this.conn;
25477     },
25478
25479     /**
25480      * Load data from the configured {@link Roo.data.Connection}, read the data object into
25481      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
25482      * process that block using the passed callback.
25483      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25484      * for the request to the remote server.
25485      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25486      * object into a block of Roo.data.Records.
25487      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25488      * The function must be passed <ul>
25489      * <li>The Record block object</li>
25490      * <li>The "arg" argument from the load function</li>
25491      * <li>A boolean success indicator</li>
25492      * </ul>
25493      * @param {Object} scope The scope in which to call the callback
25494      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25495      */
25496     load : function(params, reader, callback, scope, arg){
25497         if(this.fireEvent("beforeload", this, params) !== false){
25498             var  o = {
25499                 params : params || {},
25500                 request: {
25501                     callback : callback,
25502                     scope : scope,
25503                     arg : arg
25504                 },
25505                 reader: reader,
25506                 callback : this.loadResponse,
25507                 scope: this
25508             };
25509             if(this.useAjax){
25510                 Roo.applyIf(o, this.conn);
25511                 if(this.activeRequest){
25512                     Roo.Ajax.abort(this.activeRequest);
25513                 }
25514                 this.activeRequest = Roo.Ajax.request(o);
25515             }else{
25516                 this.conn.request(o);
25517             }
25518         }else{
25519             callback.call(scope||this, null, arg, false);
25520         }
25521     },
25522
25523     // private
25524     loadResponse : function(o, success, response){
25525         delete this.activeRequest;
25526         if(!success){
25527             this.fireEvent("loadexception", this, o, response);
25528             o.request.callback.call(o.request.scope, null, o.request.arg, false);
25529             return;
25530         }
25531         var result;
25532         try {
25533             result = o.reader.read(response);
25534         }catch(e){
25535             o.success = false;
25536             o.raw = { errorMsg : response.responseText };
25537             this.fireEvent("loadexception", this, o, response, e);
25538             o.request.callback.call(o.request.scope, o, o.request.arg, false);
25539             return;
25540         }
25541         
25542         this.fireEvent("load", this, o, o.request.arg);
25543         o.request.callback.call(o.request.scope, result, o.request.arg, true);
25544     },
25545
25546     // private
25547     update : function(dataSet){
25548
25549     },
25550
25551     // private
25552     updateResponse : function(dataSet){
25553
25554     }
25555 });/*
25556  * Based on:
25557  * Ext JS Library 1.1.1
25558  * Copyright(c) 2006-2007, Ext JS, LLC.
25559  *
25560  * Originally Released Under LGPL - original licence link has changed is not relivant.
25561  *
25562  * Fork - LGPL
25563  * <script type="text/javascript">
25564  */
25565
25566 /**
25567  * @class Roo.data.ScriptTagProxy
25568  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
25569  * other than the originating domain of the running page.<br><br>
25570  * <p>
25571  * <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
25572  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
25573  * <p>
25574  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
25575  * source code that is used as the source inside a &lt;script> tag.<br><br>
25576  * <p>
25577  * In order for the browser to process the returned data, the server must wrap the data object
25578  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
25579  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
25580  * depending on whether the callback name was passed:
25581  * <p>
25582  * <pre><code>
25583 boolean scriptTag = false;
25584 String cb = request.getParameter("callback");
25585 if (cb != null) {
25586     scriptTag = true;
25587     response.setContentType("text/javascript");
25588 } else {
25589     response.setContentType("application/x-json");
25590 }
25591 Writer out = response.getWriter();
25592 if (scriptTag) {
25593     out.write(cb + "(");
25594 }
25595 out.print(dataBlock.toJsonString());
25596 if (scriptTag) {
25597     out.write(");");
25598 }
25599 </pre></code>
25600  *
25601  * @constructor
25602  * @param {Object} config A configuration object.
25603  */
25604 Roo.data.ScriptTagProxy = function(config){
25605     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
25606     Roo.apply(this, config);
25607     this.head = document.getElementsByTagName("head")[0];
25608 };
25609
25610 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
25611
25612 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
25613     /**
25614      * @cfg {String} url The URL from which to request the data object.
25615      */
25616     /**
25617      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
25618      */
25619     timeout : 30000,
25620     /**
25621      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
25622      * the server the name of the callback function set up by the load call to process the returned data object.
25623      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
25624      * javascript output which calls this named function passing the data object as its only parameter.
25625      */
25626     callbackParam : "callback",
25627     /**
25628      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
25629      * name to the request.
25630      */
25631     nocache : true,
25632
25633     /**
25634      * Load data from the configured URL, read the data object into
25635      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
25636      * process that block using the passed callback.
25637      * @param {Object} params An object containing properties which are to be used as HTTP parameters
25638      * for the request to the remote server.
25639      * @param {Roo.data.DataReader} reader The Reader object which converts the data
25640      * object into a block of Roo.data.Records.
25641      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
25642      * The function must be passed <ul>
25643      * <li>The Record block object</li>
25644      * <li>The "arg" argument from the load function</li>
25645      * <li>A boolean success indicator</li>
25646      * </ul>
25647      * @param {Object} scope The scope in which to call the callback
25648      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
25649      */
25650     load : function(params, reader, callback, scope, arg){
25651         if(this.fireEvent("beforeload", this, params) !== false){
25652
25653             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
25654
25655             var url = this.url;
25656             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
25657             if(this.nocache){
25658                 url += "&_dc=" + (new Date().getTime());
25659             }
25660             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
25661             var trans = {
25662                 id : transId,
25663                 cb : "stcCallback"+transId,
25664                 scriptId : "stcScript"+transId,
25665                 params : params,
25666                 arg : arg,
25667                 url : url,
25668                 callback : callback,
25669                 scope : scope,
25670                 reader : reader
25671             };
25672             var conn = this;
25673
25674             window[trans.cb] = function(o){
25675                 conn.handleResponse(o, trans);
25676             };
25677
25678             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
25679
25680             if(this.autoAbort !== false){
25681                 this.abort();
25682             }
25683
25684             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
25685
25686             var script = document.createElement("script");
25687             script.setAttribute("src", url);
25688             script.setAttribute("type", "text/javascript");
25689             script.setAttribute("id", trans.scriptId);
25690             this.head.appendChild(script);
25691
25692             this.trans = trans;
25693         }else{
25694             callback.call(scope||this, null, arg, false);
25695         }
25696     },
25697
25698     // private
25699     isLoading : function(){
25700         return this.trans ? true : false;
25701     },
25702
25703     /**
25704      * Abort the current server request.
25705      */
25706     abort : function(){
25707         if(this.isLoading()){
25708             this.destroyTrans(this.trans);
25709         }
25710     },
25711
25712     // private
25713     destroyTrans : function(trans, isLoaded){
25714         this.head.removeChild(document.getElementById(trans.scriptId));
25715         clearTimeout(trans.timeoutId);
25716         if(isLoaded){
25717             window[trans.cb] = undefined;
25718             try{
25719                 delete window[trans.cb];
25720             }catch(e){}
25721         }else{
25722             // if hasn't been loaded, wait for load to remove it to prevent script error
25723             window[trans.cb] = function(){
25724                 window[trans.cb] = undefined;
25725                 try{
25726                     delete window[trans.cb];
25727                 }catch(e){}
25728             };
25729         }
25730     },
25731
25732     // private
25733     handleResponse : function(o, trans){
25734         this.trans = false;
25735         this.destroyTrans(trans, true);
25736         var result;
25737         try {
25738             result = trans.reader.readRecords(o);
25739         }catch(e){
25740             this.fireEvent("loadexception", this, o, trans.arg, e);
25741             trans.callback.call(trans.scope||window, null, trans.arg, false);
25742             return;
25743         }
25744         this.fireEvent("load", this, o, trans.arg);
25745         trans.callback.call(trans.scope||window, result, trans.arg, true);
25746     },
25747
25748     // private
25749     handleFailure : function(trans){
25750         this.trans = false;
25751         this.destroyTrans(trans, false);
25752         this.fireEvent("loadexception", this, null, trans.arg);
25753         trans.callback.call(trans.scope||window, null, trans.arg, false);
25754     }
25755 });/*
25756  * Based on:
25757  * Ext JS Library 1.1.1
25758  * Copyright(c) 2006-2007, Ext JS, LLC.
25759  *
25760  * Originally Released Under LGPL - original licence link has changed is not relivant.
25761  *
25762  * Fork - LGPL
25763  * <script type="text/javascript">
25764  */
25765
25766 /**
25767  * @class Roo.data.JsonReader
25768  * @extends Roo.data.DataReader
25769  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
25770  * based on mappings in a provided Roo.data.Record constructor.
25771  * 
25772  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
25773  * in the reply previously. 
25774  * 
25775  * <p>
25776  * Example code:
25777  * <pre><code>
25778 var RecordDef = Roo.data.Record.create([
25779     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
25780     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
25781 ]);
25782 var myReader = new Roo.data.JsonReader({
25783     totalProperty: "results",    // The property which contains the total dataset size (optional)
25784     root: "rows",                // The property which contains an Array of row objects
25785     id: "id"                     // The property within each row object that provides an ID for the record (optional)
25786 }, RecordDef);
25787 </code></pre>
25788  * <p>
25789  * This would consume a JSON file like this:
25790  * <pre><code>
25791 { 'results': 2, 'rows': [
25792     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
25793     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
25794 }
25795 </code></pre>
25796  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
25797  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
25798  * paged from the remote server.
25799  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
25800  * @cfg {String} root name of the property which contains the Array of row objects.
25801  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
25802  * @cfg {Array} fields Array of field definition objects
25803  * @constructor
25804  * Create a new JsonReader
25805  * @param {Object} meta Metadata configuration options
25806  * @param {Object} recordType Either an Array of field definition objects,
25807  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
25808  */
25809 Roo.data.JsonReader = function(meta, recordType){
25810     
25811     meta = meta || {};
25812     // set some defaults:
25813     Roo.applyIf(meta, {
25814         totalProperty: 'total',
25815         successProperty : 'success',
25816         root : 'data',
25817         id : 'id'
25818     });
25819     
25820     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
25821 };
25822 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
25823     
25824     readerType : 'Json',
25825     
25826     /**
25827      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
25828      * Used by Store query builder to append _requestMeta to params.
25829      * 
25830      */
25831     metaFromRemote : false,
25832     /**
25833      * This method is only used by a DataProxy which has retrieved data from a remote server.
25834      * @param {Object} response The XHR object which contains the JSON data in its responseText.
25835      * @return {Object} data A data block which is used by an Roo.data.Store object as
25836      * a cache of Roo.data.Records.
25837      */
25838     read : function(response){
25839         var json = response.responseText;
25840        
25841         var o = /* eval:var:o */ eval("("+json+")");
25842         if(!o) {
25843             throw {message: "JsonReader.read: Json object not found"};
25844         }
25845         
25846         if(o.metaData){
25847             
25848             delete this.ef;
25849             this.metaFromRemote = true;
25850             this.meta = o.metaData;
25851             this.recordType = Roo.data.Record.create(o.metaData.fields);
25852             this.onMetaChange(this.meta, this.recordType, o);
25853         }
25854         return this.readRecords(o);
25855     },
25856
25857     // private function a store will implement
25858     onMetaChange : function(meta, recordType, o){
25859
25860     },
25861
25862     /**
25863          * @ignore
25864          */
25865     simpleAccess: function(obj, subsc) {
25866         return obj[subsc];
25867     },
25868
25869         /**
25870          * @ignore
25871          */
25872     getJsonAccessor: function(){
25873         var re = /[\[\.]/;
25874         return function(expr) {
25875             try {
25876                 return(re.test(expr))
25877                     ? new Function("obj", "return obj." + expr)
25878                     : function(obj){
25879                         return obj[expr];
25880                     };
25881             } catch(e){}
25882             return Roo.emptyFn;
25883         };
25884     }(),
25885
25886     /**
25887      * Create a data block containing Roo.data.Records from an XML document.
25888      * @param {Object} o An object which contains an Array of row objects in the property specified
25889      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
25890      * which contains the total size of the dataset.
25891      * @return {Object} data A data block which is used by an Roo.data.Store object as
25892      * a cache of Roo.data.Records.
25893      */
25894     readRecords : function(o){
25895         /**
25896          * After any data loads, the raw JSON data is available for further custom processing.
25897          * @type Object
25898          */
25899         this.o = o;
25900         var s = this.meta, Record = this.recordType,
25901             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
25902
25903 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
25904         if (!this.ef) {
25905             if(s.totalProperty) {
25906                     this.getTotal = this.getJsonAccessor(s.totalProperty);
25907                 }
25908                 if(s.successProperty) {
25909                     this.getSuccess = this.getJsonAccessor(s.successProperty);
25910                 }
25911                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
25912                 if (s.id) {
25913                         var g = this.getJsonAccessor(s.id);
25914                         this.getId = function(rec) {
25915                                 var r = g(rec);  
25916                                 return (r === undefined || r === "") ? null : r;
25917                         };
25918                 } else {
25919                         this.getId = function(){return null;};
25920                 }
25921             this.ef = [];
25922             for(var jj = 0; jj < fl; jj++){
25923                 f = fi[jj];
25924                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
25925                 this.ef[jj] = this.getJsonAccessor(map);
25926             }
25927         }
25928
25929         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
25930         if(s.totalProperty){
25931             var vt = parseInt(this.getTotal(o), 10);
25932             if(!isNaN(vt)){
25933                 totalRecords = vt;
25934             }
25935         }
25936         if(s.successProperty){
25937             var vs = this.getSuccess(o);
25938             if(vs === false || vs === 'false'){
25939                 success = false;
25940             }
25941         }
25942         var records = [];
25943         for(var i = 0; i < c; i++){
25944             var n = root[i];
25945             var values = {};
25946             var id = this.getId(n);
25947             for(var j = 0; j < fl; j++){
25948                 f = fi[j];
25949                                 var v = this.ef[j](n);
25950                                 if (!f.convert) {
25951                                         Roo.log('missing convert for ' + f.name);
25952                                         Roo.log(f);
25953                                         continue;
25954                                 }
25955                                 values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
25956             }
25957                         if (!Record) {
25958                                 return {
25959                                         raw : { errorMsg : "JSON Reader Error: fields or metadata not available to create Record" },
25960                                         success : false,
25961                                         records : [],
25962                                         totalRecords : 0
25963                                 };
25964                         }
25965             var record = new Record(values, id);
25966             record.json = n;
25967             records[i] = record;
25968         }
25969         return {
25970             raw : o,
25971             success : success,
25972             records : records,
25973             totalRecords : totalRecords
25974         };
25975     },
25976     // used when loading children.. @see loadDataFromChildren
25977     toLoadData: function(rec)
25978     {
25979         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
25980         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
25981         return { data : data, total : data.length };
25982         
25983     }
25984 });/*
25985  * Based on:
25986  * Ext JS Library 1.1.1
25987  * Copyright(c) 2006-2007, Ext JS, LLC.
25988  *
25989  * Originally Released Under LGPL - original licence link has changed is not relivant.
25990  *
25991  * Fork - LGPL
25992  * <script type="text/javascript">
25993  */
25994
25995 /**
25996  * @class Roo.data.XmlReader
25997  * @extends Roo.data.DataReader
25998  * Data reader class to create an Array of {@link Roo.data.Record} objects from an XML document
25999  * based on mappings in a provided Roo.data.Record constructor.<br><br>
26000  * <p>
26001  * <em>Note that in order for the browser to parse a returned XML document, the Content-Type
26002  * header in the HTTP response must be set to "text/xml".</em>
26003  * <p>
26004  * Example code:
26005  * <pre><code>
26006 var RecordDef = Roo.data.Record.create([
26007    {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
26008    {name: 'occupation'}                 // This field will use "occupation" as the mapping.
26009 ]);
26010 var myReader = new Roo.data.XmlReader({
26011    totalRecords: "results", // The element which contains the total dataset size (optional)
26012    record: "row",           // The repeated element which contains row information
26013    id: "id"                 // The element within the row that provides an ID for the record (optional)
26014 }, RecordDef);
26015 </code></pre>
26016  * <p>
26017  * This would consume an XML file like this:
26018  * <pre><code>
26019 &lt;?xml?>
26020 &lt;dataset>
26021  &lt;results>2&lt;/results>
26022  &lt;row>
26023    &lt;id>1&lt;/id>
26024    &lt;name>Bill&lt;/name>
26025    &lt;occupation>Gardener&lt;/occupation>
26026  &lt;/row>
26027  &lt;row>
26028    &lt;id>2&lt;/id>
26029    &lt;name>Ben&lt;/name>
26030    &lt;occupation>Horticulturalist&lt;/occupation>
26031  &lt;/row>
26032 &lt;/dataset>
26033 </code></pre>
26034  * @cfg {String} totalRecords The DomQuery path from which to retrieve the total number of records
26035  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
26036  * paged from the remote server.
26037  * @cfg {String} record The DomQuery path to the repeated element which contains record information.
26038  * @cfg {String} success The DomQuery path to the success attribute used by forms.
26039  * @cfg {String} id The DomQuery path relative from the record element to the element that contains
26040  * a record identifier value.
26041  * @constructor
26042  * Create a new XmlReader
26043  * @param {Object} meta Metadata configuration options
26044  * @param {Mixed} recordType The definition of the data record type to produce.  This can be either a valid
26045  * Record subclass created with {@link Roo.data.Record#create}, or an array of objects with which to call
26046  * Roo.data.Record.create.  See the {@link Roo.data.Record} class for more details.
26047  */
26048 Roo.data.XmlReader = function(meta, recordType){
26049     meta = meta || {};
26050     Roo.data.XmlReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26051 };
26052 Roo.extend(Roo.data.XmlReader, Roo.data.DataReader, {
26053     
26054     readerType : 'Xml',
26055     
26056     /**
26057      * This method is only used by a DataProxy which has retrieved data from a remote server.
26058          * @param {Object} response The XHR object which contains the parsed XML document.  The response is expected
26059          * to contain a method called 'responseXML' that returns an XML document object.
26060      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26061      * a cache of Roo.data.Records.
26062      */
26063     read : function(response){
26064         var doc = response.responseXML;
26065         if(!doc) {
26066             throw {message: "XmlReader.read: XML Document not available"};
26067         }
26068         return this.readRecords(doc);
26069     },
26070
26071     /**
26072      * Create a data block containing Roo.data.Records from an XML document.
26073          * @param {Object} doc A parsed XML document.
26074      * @return {Object} records A data block which is used by an {@link Roo.data.Store} as
26075      * a cache of Roo.data.Records.
26076      */
26077     readRecords : function(doc){
26078         /**
26079          * After any data loads/reads, the raw XML Document is available for further custom processing.
26080          * @type XMLDocument
26081          */
26082         this.xmlData = doc;
26083         var root = doc.documentElement || doc;
26084         var q = Roo.DomQuery;
26085         var recordType = this.recordType, fields = recordType.prototype.fields;
26086         var sid = this.meta.id;
26087         var totalRecords = 0, success = true;
26088         if(this.meta.totalRecords){
26089             totalRecords = q.selectNumber(this.meta.totalRecords, root, 0);
26090         }
26091         
26092         if(this.meta.success){
26093             var sv = q.selectValue(this.meta.success, root, true);
26094             success = sv !== false && sv !== 'false';
26095         }
26096         var records = [];
26097         var ns = q.select(this.meta.record, root);
26098         for(var i = 0, len = ns.length; i < len; i++) {
26099                 var n = ns[i];
26100                 var values = {};
26101                 var id = sid ? q.selectValue(sid, n) : undefined;
26102                 for(var j = 0, jlen = fields.length; j < jlen; j++){
26103                     var f = fields.items[j];
26104                 var v = q.selectValue(f.mapping || f.name, n, f.defaultValue);
26105                     v = f.convert(v);
26106                     values[f.name] = v;
26107                 }
26108                 var record = new recordType(values, id);
26109                 record.node = n;
26110                 records[records.length] = record;
26111             }
26112
26113             return {
26114                 success : success,
26115                 records : records,
26116                 totalRecords : totalRecords || records.length
26117             };
26118     }
26119 });/*
26120  * Based on:
26121  * Ext JS Library 1.1.1
26122  * Copyright(c) 2006-2007, Ext JS, LLC.
26123  *
26124  * Originally Released Under LGPL - original licence link has changed is not relivant.
26125  *
26126  * Fork - LGPL
26127  * <script type="text/javascript">
26128  */
26129
26130 /**
26131  * @class Roo.data.ArrayReader
26132  * @extends Roo.data.DataReader
26133  * Data reader class to create an Array of Roo.data.Record objects from an Array.
26134  * Each element of that Array represents a row of data fields. The
26135  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
26136  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
26137  * <p>
26138  * Example code:.
26139  * <pre><code>
26140 var RecordDef = Roo.data.Record.create([
26141     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
26142     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
26143 ]);
26144 var myReader = new Roo.data.ArrayReader({
26145     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
26146 }, RecordDef);
26147 </code></pre>
26148  * <p>
26149  * This would consume an Array like this:
26150  * <pre><code>
26151 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
26152   </code></pre>
26153  
26154  * @constructor
26155  * Create a new JsonReader
26156  * @param {Object} meta Metadata configuration options.
26157  * @param {Object|Array} recordType Either an Array of field definition objects
26158  * 
26159  * @cfg {Array} fields Array of field definition objects
26160  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
26161  * as specified to {@link Roo.data.Record#create},
26162  * or an {@link Roo.data.Record} object
26163  *
26164  * 
26165  * created using {@link Roo.data.Record#create}.
26166  */
26167 Roo.data.ArrayReader = function(meta, recordType)
26168 {    
26169     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
26170 };
26171
26172 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
26173     
26174       /**
26175      * Create a data block containing Roo.data.Records from an XML document.
26176      * @param {Object} o An Array of row objects which represents the dataset.
26177      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
26178      * a cache of Roo.data.Records.
26179      */
26180     readRecords : function(o)
26181     {
26182         var sid = this.meta ? this.meta.id : null;
26183         var recordType = this.recordType, fields = recordType.prototype.fields;
26184         var records = [];
26185         var root = o;
26186         for(var i = 0; i < root.length; i++){
26187             var n = root[i];
26188             var values = {};
26189             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
26190             for(var j = 0, jlen = fields.length; j < jlen; j++){
26191                 var f = fields.items[j];
26192                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
26193                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
26194                 v = f.convert(v);
26195                 values[f.name] = v;
26196             }
26197             var record = new recordType(values, id);
26198             record.json = n;
26199             records[records.length] = record;
26200         }
26201         return {
26202             records : records,
26203             totalRecords : records.length
26204         };
26205     },
26206     // used when loading children.. @see loadDataFromChildren
26207     toLoadData: function(rec)
26208     {
26209         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
26210         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
26211         
26212     }
26213     
26214     
26215 });/*
26216  * Based on:
26217  * Ext JS Library 1.1.1
26218  * Copyright(c) 2006-2007, Ext JS, LLC.
26219  *
26220  * Originally Released Under LGPL - original licence link has changed is not relivant.
26221  *
26222  * Fork - LGPL
26223  * <script type="text/javascript">
26224  */
26225
26226
26227 /**
26228  * @class Roo.data.Tree
26229  * @extends Roo.util.Observable
26230  * Represents a tree data structure and bubbles all the events for its nodes. The nodes
26231  * in the tree have most standard DOM functionality.
26232  * @constructor
26233  * @param {Node} root (optional) The root node
26234  */
26235 Roo.data.Tree = function(root){
26236    this.nodeHash = {};
26237    /**
26238     * The root node for this tree
26239     * @type Node
26240     */
26241    this.root = null;
26242    if(root){
26243        this.setRootNode(root);
26244    }
26245    this.addEvents({
26246        /**
26247         * @event append
26248         * Fires when a new child node is appended to a node in this tree.
26249         * @param {Tree} tree The owner tree
26250         * @param {Node} parent The parent node
26251         * @param {Node} node The newly appended node
26252         * @param {Number} index The index of the newly appended node
26253         */
26254        "append" : true,
26255        /**
26256         * @event remove
26257         * Fires when a child node is removed from a node in this tree.
26258         * @param {Tree} tree The owner tree
26259         * @param {Node} parent The parent node
26260         * @param {Node} node The child node removed
26261         */
26262        "remove" : true,
26263        /**
26264         * @event move
26265         * Fires when a node is moved to a new location in the tree
26266         * @param {Tree} tree The owner tree
26267         * @param {Node} node The node moved
26268         * @param {Node} oldParent The old parent of this node
26269         * @param {Node} newParent The new parent of this node
26270         * @param {Number} index The index it was moved to
26271         */
26272        "move" : true,
26273        /**
26274         * @event insert
26275         * Fires when a new child node is inserted in a node in this tree.
26276         * @param {Tree} tree The owner tree
26277         * @param {Node} parent The parent node
26278         * @param {Node} node The child node inserted
26279         * @param {Node} refNode The child node the node was inserted before
26280         */
26281        "insert" : true,
26282        /**
26283         * @event beforeappend
26284         * Fires before a new child is appended to a node in this tree, return false to cancel the append.
26285         * @param {Tree} tree The owner tree
26286         * @param {Node} parent The parent node
26287         * @param {Node} node The child node to be appended
26288         */
26289        "beforeappend" : true,
26290        /**
26291         * @event beforeremove
26292         * Fires before a child is removed from a node in this tree, return false to cancel the remove.
26293         * @param {Tree} tree The owner tree
26294         * @param {Node} parent The parent node
26295         * @param {Node} node The child node to be removed
26296         */
26297        "beforeremove" : true,
26298        /**
26299         * @event beforemove
26300         * Fires before a node is moved to a new location in the tree. Return false to cancel the move.
26301         * @param {Tree} tree The owner tree
26302         * @param {Node} node The node being moved
26303         * @param {Node} oldParent The parent of the node
26304         * @param {Node} newParent The new parent the node is moving to
26305         * @param {Number} index The index it is being moved to
26306         */
26307        "beforemove" : true,
26308        /**
26309         * @event beforeinsert
26310         * Fires before a new child is inserted in a node in this tree, return false to cancel the insert.
26311         * @param {Tree} tree The owner tree
26312         * @param {Node} parent The parent node
26313         * @param {Node} node The child node to be inserted
26314         * @param {Node} refNode The child node the node is being inserted before
26315         */
26316        "beforeinsert" : true
26317    });
26318
26319     Roo.data.Tree.superclass.constructor.call(this);
26320 };
26321
26322 Roo.extend(Roo.data.Tree, Roo.util.Observable, {
26323     pathSeparator: "/",
26324
26325     proxyNodeEvent : function(){
26326         return this.fireEvent.apply(this, arguments);
26327     },
26328
26329     /**
26330      * Returns the root node for this tree.
26331      * @return {Node}
26332      */
26333     getRootNode : function(){
26334         return this.root;
26335     },
26336
26337     /**
26338      * Sets the root node for this tree.
26339      * @param {Node} node
26340      * @return {Node}
26341      */
26342     setRootNode : function(node){
26343         this.root = node;
26344         node.ownerTree = this;
26345         node.isRoot = true;
26346         this.registerNode(node);
26347         return node;
26348     },
26349
26350     /**
26351      * Gets a node in this tree by its id.
26352      * @param {String} id
26353      * @return {Node}
26354      */
26355     getNodeById : function(id){
26356         return this.nodeHash[id];
26357     },
26358
26359     registerNode : function(node){
26360         this.nodeHash[node.id] = node;
26361     },
26362
26363     unregisterNode : function(node){
26364         delete this.nodeHash[node.id];
26365     },
26366
26367     toString : function(){
26368         return "[Tree"+(this.id?" "+this.id:"")+"]";
26369     }
26370 });
26371
26372 /**
26373  * @class Roo.data.Node
26374  * @extends Roo.util.Observable
26375  * @cfg {Boolean} leaf true if this node is a leaf and does not have children
26376  * @cfg {String} id The id for this node. If one is not specified, one is generated.
26377  * @constructor
26378  * @param {Object} attributes The attributes/config for the node
26379  */
26380 Roo.data.Node = function(attributes){
26381     /**
26382      * The attributes supplied for the node. You can use this property to access any custom attributes you supplied.
26383      * @type {Object}
26384      */
26385     this.attributes = attributes || {};
26386     this.leaf = this.attributes.leaf;
26387     /**
26388      * The node id. @type String
26389      */
26390     this.id = this.attributes.id;
26391     if(!this.id){
26392         this.id = Roo.id(null, "ynode-");
26393         this.attributes.id = this.id;
26394     }
26395      
26396     
26397     /**
26398      * All child nodes of this node. @type Array
26399      */
26400     this.childNodes = [];
26401     if(!this.childNodes.indexOf){ // indexOf is a must
26402         this.childNodes.indexOf = function(o){
26403             for(var i = 0, len = this.length; i < len; i++){
26404                 if(this[i] == o) {
26405                     return i;
26406                 }
26407             }
26408             return -1;
26409         };
26410     }
26411     /**
26412      * The parent node for this node. @type Node
26413      */
26414     this.parentNode = null;
26415     /**
26416      * The first direct child node of this node, or null if this node has no child nodes. @type Node
26417      */
26418     this.firstChild = null;
26419     /**
26420      * The last direct child node of this node, or null if this node has no child nodes. @type Node
26421      */
26422     this.lastChild = null;
26423     /**
26424      * The node immediately preceding this node in the tree, or null if there is no sibling node. @type Node
26425      */
26426     this.previousSibling = null;
26427     /**
26428      * The node immediately following this node in the tree, or null if there is no sibling node. @type Node
26429      */
26430     this.nextSibling = null;
26431
26432     this.addEvents({
26433        /**
26434         * @event append
26435         * Fires when a new child node is appended
26436         * @param {Tree} tree The owner tree
26437         * @param {Node} this This node
26438         * @param {Node} node The newly appended node
26439         * @param {Number} index The index of the newly appended node
26440         */
26441        "append" : true,
26442        /**
26443         * @event remove
26444         * Fires when a child node is removed
26445         * @param {Tree} tree The owner tree
26446         * @param {Node} this This node
26447         * @param {Node} node The removed node
26448         */
26449        "remove" : true,
26450        /**
26451         * @event move
26452         * Fires when this node is moved to a new location in the tree
26453         * @param {Tree} tree The owner tree
26454         * @param {Node} this This node
26455         * @param {Node} oldParent The old parent of this node
26456         * @param {Node} newParent The new parent of this node
26457         * @param {Number} index The index it was moved to
26458         */
26459        "move" : true,
26460        /**
26461         * @event insert
26462         * Fires when a new child node is inserted.
26463         * @param {Tree} tree The owner tree
26464         * @param {Node} this This node
26465         * @param {Node} node The child node inserted
26466         * @param {Node} refNode The child node the node was inserted before
26467         */
26468        "insert" : true,
26469        /**
26470         * @event beforeappend
26471         * Fires before a new child is appended, return false to cancel the append.
26472         * @param {Tree} tree The owner tree
26473         * @param {Node} this This node
26474         * @param {Node} node The child node to be appended
26475         */
26476        "beforeappend" : true,
26477        /**
26478         * @event beforeremove
26479         * Fires before a child is removed, return false to cancel the remove.
26480         * @param {Tree} tree The owner tree
26481         * @param {Node} this This node
26482         * @param {Node} node The child node to be removed
26483         */
26484        "beforeremove" : true,
26485        /**
26486         * @event beforemove
26487         * Fires before this node is moved to a new location in the tree. Return false to cancel the move.
26488         * @param {Tree} tree The owner tree
26489         * @param {Node} this This node
26490         * @param {Node} oldParent The parent of this node
26491         * @param {Node} newParent The new parent this node is moving to
26492         * @param {Number} index The index it is being moved to
26493         */
26494        "beforemove" : true,
26495        /**
26496         * @event beforeinsert
26497         * Fires before a new child is inserted, return false to cancel the insert.
26498         * @param {Tree} tree The owner tree
26499         * @param {Node} this This node
26500         * @param {Node} node The child node to be inserted
26501         * @param {Node} refNode The child node the node is being inserted before
26502         */
26503        "beforeinsert" : true
26504    });
26505     this.listeners = this.attributes.listeners;
26506     Roo.data.Node.superclass.constructor.call(this);
26507 };
26508
26509 Roo.extend(Roo.data.Node, Roo.util.Observable, {
26510     fireEvent : function(evtName){
26511         // first do standard event for this node
26512         if(Roo.data.Node.superclass.fireEvent.apply(this, arguments) === false){
26513             return false;
26514         }
26515         // then bubble it up to the tree if the event wasn't cancelled
26516         var ot = this.getOwnerTree();
26517         if(ot){
26518             if(ot.proxyNodeEvent.apply(ot, arguments) === false){
26519                 return false;
26520             }
26521         }
26522         return true;
26523     },
26524
26525     /**
26526      * Returns true if this node is a leaf
26527      * @return {Boolean}
26528      */
26529     isLeaf : function(){
26530         return this.leaf === true;
26531     },
26532
26533     // private
26534     setFirstChild : function(node){
26535         this.firstChild = node;
26536     },
26537
26538     //private
26539     setLastChild : function(node){
26540         this.lastChild = node;
26541     },
26542
26543
26544     /**
26545      * Returns true if this node is the last child of its parent
26546      * @return {Boolean}
26547      */
26548     isLast : function(){
26549        return (!this.parentNode ? true : this.parentNode.lastChild == this);
26550     },
26551
26552     /**
26553      * Returns true if this node is the first child of its parent
26554      * @return {Boolean}
26555      */
26556     isFirst : function(){
26557        return (!this.parentNode ? true : this.parentNode.firstChild == this);
26558     },
26559
26560     hasChildNodes : function(){
26561         return !this.isLeaf() && this.childNodes.length > 0;
26562     },
26563
26564     /**
26565      * Insert node(s) as the last child node of this node.
26566      * @param {Node/Array} node The node or Array of nodes to append
26567      * @return {Node} The appended node if single append, or null if an array was passed
26568      */
26569     appendChild : function(node){
26570         var multi = false;
26571         if(node instanceof Array){
26572             multi = node;
26573         }else if(arguments.length > 1){
26574             multi = arguments;
26575         }
26576         
26577         // if passed an array or multiple args do them one by one
26578         if(multi){
26579             for(var i = 0, len = multi.length; i < len; i++) {
26580                 this.appendChild(multi[i]);
26581             }
26582         }else{
26583             if(this.fireEvent("beforeappend", this.ownerTree, this, node) === false){
26584                 return false;
26585             }
26586             var index = this.childNodes.length;
26587             var oldParent = node.parentNode;
26588             // it's a move, make sure we move it cleanly
26589             if(oldParent){
26590                 if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index) === false){
26591                     return false;
26592                 }
26593                 oldParent.removeChild(node);
26594             }
26595             
26596             index = this.childNodes.length;
26597             if(index == 0){
26598                 this.setFirstChild(node);
26599             }
26600             this.childNodes.push(node);
26601             node.parentNode = this;
26602             var ps = this.childNodes[index-1];
26603             if(ps){
26604                 node.previousSibling = ps;
26605                 ps.nextSibling = node;
26606             }else{
26607                 node.previousSibling = null;
26608             }
26609             node.nextSibling = null;
26610             this.setLastChild(node);
26611             node.setOwnerTree(this.getOwnerTree());
26612             this.fireEvent("append", this.ownerTree, this, node, index);
26613             if(this.ownerTree) {
26614                 this.ownerTree.fireEvent("appendnode", this, node, index);
26615             }
26616             if(oldParent){
26617                 node.fireEvent("move", this.ownerTree, node, oldParent, this, index);
26618             }
26619             return node;
26620         }
26621     },
26622
26623     /**
26624      * Removes a child node from this node.
26625      * @param {Node} node The node to remove
26626      * @return {Node} The removed node
26627      */
26628     removeChild : function(node){
26629         var index = this.childNodes.indexOf(node);
26630         if(index == -1){
26631             return false;
26632         }
26633         if(this.fireEvent("beforeremove", this.ownerTree, this, node) === false){
26634             return false;
26635         }
26636
26637         // remove it from childNodes collection
26638         this.childNodes.splice(index, 1);
26639
26640         // update siblings
26641         if(node.previousSibling){
26642             node.previousSibling.nextSibling = node.nextSibling;
26643         }
26644         if(node.nextSibling){
26645             node.nextSibling.previousSibling = node.previousSibling;
26646         }
26647
26648         // update child refs
26649         if(this.firstChild == node){
26650             this.setFirstChild(node.nextSibling);
26651         }
26652         if(this.lastChild == node){
26653             this.setLastChild(node.previousSibling);
26654         }
26655
26656         node.setOwnerTree(null);
26657         // clear any references from the node
26658         node.parentNode = null;
26659         node.previousSibling = null;
26660         node.nextSibling = null;
26661         this.fireEvent("remove", this.ownerTree, this, node);
26662         return node;
26663     },
26664
26665     /**
26666      * Inserts the first node before the second node in this nodes childNodes collection.
26667      * @param {Node} node The node to insert
26668      * @param {Node} refNode The node to insert before (if null the node is appended)
26669      * @return {Node} The inserted node
26670      */
26671     insertBefore : function(node, refNode){
26672         if(!refNode){ // like standard Dom, refNode can be null for append
26673             return this.appendChild(node);
26674         }
26675         // nothing to do
26676         if(node == refNode){
26677             return false;
26678         }
26679
26680         if(this.fireEvent("beforeinsert", this.ownerTree, this, node, refNode) === false){
26681             return false;
26682         }
26683         var index = this.childNodes.indexOf(refNode);
26684         var oldParent = node.parentNode;
26685         var refIndex = index;
26686
26687         // when moving internally, indexes will change after remove
26688         if(oldParent == this && this.childNodes.indexOf(node) < index){
26689             refIndex--;
26690         }
26691
26692         // it's a move, make sure we move it cleanly
26693         if(oldParent){
26694             if(node.fireEvent("beforemove", node.getOwnerTree(), node, oldParent, this, index, refNode) === false){
26695                 return false;
26696             }
26697             oldParent.removeChild(node);
26698         }
26699         if(refIndex == 0){
26700             this.setFirstChild(node);
26701         }
26702         this.childNodes.splice(refIndex, 0, node);
26703         node.parentNode = this;
26704         var ps = this.childNodes[refIndex-1];
26705         if(ps){
26706             node.previousSibling = ps;
26707             ps.nextSibling = node;
26708         }else{
26709             node.previousSibling = null;
26710         }
26711         node.nextSibling = refNode;
26712         refNode.previousSibling = node;
26713         node.setOwnerTree(this.getOwnerTree());
26714         this.fireEvent("insert", this.ownerTree, this, node, refNode);
26715         if(oldParent){
26716             node.fireEvent("move", this.ownerTree, node, oldParent, this, refIndex, refNode);
26717         }
26718         return node;
26719     },
26720
26721     /**
26722      * Returns the child node at the specified index.
26723      * @param {Number} index
26724      * @return {Node}
26725      */
26726     item : function(index){
26727         return this.childNodes[index];
26728     },
26729
26730     /**
26731      * Replaces one child node in this node with another.
26732      * @param {Node} newChild The replacement node
26733      * @param {Node} oldChild The node to replace
26734      * @return {Node} The replaced node
26735      */
26736     replaceChild : function(newChild, oldChild){
26737         this.insertBefore(newChild, oldChild);
26738         this.removeChild(oldChild);
26739         return oldChild;
26740     },
26741
26742     /**
26743      * Returns the index of a child node
26744      * @param {Node} node
26745      * @return {Number} The index of the node or -1 if it was not found
26746      */
26747     indexOf : function(child){
26748         return this.childNodes.indexOf(child);
26749     },
26750
26751     /**
26752      * Returns the tree this node is in.
26753      * @return {Tree}
26754      */
26755     getOwnerTree : function(){
26756         // if it doesn't have one, look for one
26757         if(!this.ownerTree){
26758             var p = this;
26759             while(p){
26760                 if(p.ownerTree){
26761                     this.ownerTree = p.ownerTree;
26762                     break;
26763                 }
26764                 p = p.parentNode;
26765             }
26766         }
26767         return this.ownerTree;
26768     },
26769
26770     /**
26771      * Returns depth of this node (the root node has a depth of 0)
26772      * @return {Number}
26773      */
26774     getDepth : function(){
26775         var depth = 0;
26776         var p = this;
26777         while(p.parentNode){
26778             ++depth;
26779             p = p.parentNode;
26780         }
26781         return depth;
26782     },
26783
26784     // private
26785     setOwnerTree : function(tree){
26786         // if it's move, we need to update everyone
26787         if(tree != this.ownerTree){
26788             if(this.ownerTree){
26789                 this.ownerTree.unregisterNode(this);
26790             }
26791             this.ownerTree = tree;
26792             var cs = this.childNodes;
26793             for(var i = 0, len = cs.length; i < len; i++) {
26794                 cs[i].setOwnerTree(tree);
26795             }
26796             if(tree){
26797                 tree.registerNode(this);
26798             }
26799         }
26800     },
26801
26802     /**
26803      * Returns the path for this node. The path can be used to expand or select this node programmatically.
26804      * @param {String} attr (optional) The attr to use for the path (defaults to the node's id)
26805      * @return {String} The path
26806      */
26807     getPath : function(attr){
26808         attr = attr || "id";
26809         var p = this.parentNode;
26810         var b = [this.attributes[attr]];
26811         while(p){
26812             b.unshift(p.attributes[attr]);
26813             p = p.parentNode;
26814         }
26815         var sep = this.getOwnerTree().pathSeparator;
26816         return sep + b.join(sep);
26817     },
26818
26819     /**
26820      * Bubbles up the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26821      * function call will be the scope provided or the current node. The arguments to the function
26822      * will be the args provided or the current node. If the function returns false at any point,
26823      * the bubble is stopped.
26824      * @param {Function} fn The function to call
26825      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26826      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26827      */
26828     bubble : function(fn, scope, args){
26829         var p = this;
26830         while(p){
26831             if(fn.call(scope || p, args || p) === false){
26832                 break;
26833             }
26834             p = p.parentNode;
26835         }
26836     },
26837
26838     /**
26839      * Cascades down the tree from this node, calling the specified function with each node. The scope (<i>this</i>) of
26840      * function call will be the scope provided or the current node. The arguments to the function
26841      * will be the args provided or the current node. If the function returns false at any point,
26842      * the cascade is stopped on that branch.
26843      * @param {Function} fn The function to call
26844      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26845      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26846      */
26847     cascade : function(fn, scope, args){
26848         if(fn.call(scope || this, args || this) !== false){
26849             var cs = this.childNodes;
26850             for(var i = 0, len = cs.length; i < len; i++) {
26851                 cs[i].cascade(fn, scope, args);
26852             }
26853         }
26854     },
26855
26856     /**
26857      * Interates the child nodes of this node, calling the specified function with each node. The scope (<i>this</i>) of
26858      * function call will be the scope provided or the current node. The arguments to the function
26859      * will be the args provided or the current node. If the function returns false at any point,
26860      * the iteration stops.
26861      * @param {Function} fn The function to call
26862      * @param {Object} scope (optional) The scope of the function (defaults to current node)
26863      * @param {Array} args (optional) The args to call the function with (default to passing the current node)
26864      */
26865     eachChild : function(fn, scope, args){
26866         var cs = this.childNodes;
26867         for(var i = 0, len = cs.length; i < len; i++) {
26868                 if(fn.call(scope || this, args || cs[i]) === false){
26869                     break;
26870                 }
26871         }
26872     },
26873
26874     /**
26875      * Finds the first child that has the attribute with the specified value.
26876      * @param {String} attribute The attribute name
26877      * @param {Mixed} value The value to search for
26878      * @return {Node} The found child or null if none was found
26879      */
26880     findChild : function(attribute, value){
26881         var cs = this.childNodes;
26882         for(var i = 0, len = cs.length; i < len; i++) {
26883                 if(cs[i].attributes[attribute] == value){
26884                     return cs[i];
26885                 }
26886         }
26887         return null;
26888     },
26889
26890     /**
26891      * Finds the first child by a custom function. The child matches if the function passed
26892      * returns true.
26893      * @param {Function} fn
26894      * @param {Object} scope (optional)
26895      * @return {Node} The found child or null if none was found
26896      */
26897     findChildBy : function(fn, scope){
26898         var cs = this.childNodes;
26899         for(var i = 0, len = cs.length; i < len; i++) {
26900                 if(fn.call(scope||cs[i], cs[i]) === true){
26901                     return cs[i];
26902                 }
26903         }
26904         return null;
26905     },
26906
26907     /**
26908      * Sorts this nodes children using the supplied sort function
26909      * @param {Function} fn
26910      * @param {Object} scope (optional)
26911      */
26912     sort : function(fn, scope){
26913         var cs = this.childNodes;
26914         var len = cs.length;
26915         if(len > 0){
26916             var sortFn = scope ? function(){fn.apply(scope, arguments);} : fn;
26917             cs.sort(sortFn);
26918             for(var i = 0; i < len; i++){
26919                 var n = cs[i];
26920                 n.previousSibling = cs[i-1];
26921                 n.nextSibling = cs[i+1];
26922                 if(i == 0){
26923                     this.setFirstChild(n);
26924                 }
26925                 if(i == len-1){
26926                     this.setLastChild(n);
26927                 }
26928             }
26929         }
26930     },
26931
26932     /**
26933      * Returns true if this node is an ancestor (at any point) of the passed node.
26934      * @param {Node} node
26935      * @return {Boolean}
26936      */
26937     contains : function(node){
26938         return node.isAncestor(this);
26939     },
26940
26941     /**
26942      * Returns true if the passed node is an ancestor (at any point) of this node.
26943      * @param {Node} node
26944      * @return {Boolean}
26945      */
26946     isAncestor : function(node){
26947         var p = this.parentNode;
26948         while(p){
26949             if(p == node){
26950                 return true;
26951             }
26952             p = p.parentNode;
26953         }
26954         return false;
26955     },
26956
26957     toString : function(){
26958         return "[Node"+(this.id?" "+this.id:"")+"]";
26959     }
26960 });/*
26961  * Based on:
26962  * Ext JS Library 1.1.1
26963  * Copyright(c) 2006-2007, Ext JS, LLC.
26964  *
26965  * Originally Released Under LGPL - original licence link has changed is not relivant.
26966  *
26967  * Fork - LGPL
26968  * <script type="text/javascript">
26969  */
26970
26971
26972 /**
26973  * @class Roo.Shadow
26974  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
26975  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
26976  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
26977  * @constructor
26978  * Create a new Shadow
26979  * @param {Object} config The config object
26980  */
26981 Roo.Shadow = function(config){
26982     Roo.apply(this, config);
26983     if(typeof this.mode != "string"){
26984         this.mode = this.defaultMode;
26985     }
26986     var o = this.offset, a = {h: 0};
26987     var rad = Math.floor(this.offset/2);
26988     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
26989         case "drop":
26990             a.w = 0;
26991             a.l = a.t = o;
26992             a.t -= 1;
26993             if(Roo.isIE){
26994                 a.l -= this.offset + rad;
26995                 a.t -= this.offset + rad;
26996                 a.w -= rad;
26997                 a.h -= rad;
26998                 a.t += 1;
26999             }
27000         break;
27001         case "sides":
27002             a.w = (o*2);
27003             a.l = -o;
27004             a.t = o-1;
27005             if(Roo.isIE){
27006                 a.l -= (this.offset - rad);
27007                 a.t -= this.offset + rad;
27008                 a.l += 1;
27009                 a.w -= (this.offset - rad)*2;
27010                 a.w -= rad + 1;
27011                 a.h -= 1;
27012             }
27013         break;
27014         case "frame":
27015             a.w = a.h = (o*2);
27016             a.l = a.t = -o;
27017             a.t += 1;
27018             a.h -= 2;
27019             if(Roo.isIE){
27020                 a.l -= (this.offset - rad);
27021                 a.t -= (this.offset - rad);
27022                 a.l += 1;
27023                 a.w -= (this.offset + rad + 1);
27024                 a.h -= (this.offset + rad);
27025                 a.h += 1;
27026             }
27027         break;
27028     };
27029
27030     this.adjusts = a;
27031 };
27032
27033 Roo.Shadow.prototype = {
27034     /**
27035      * @cfg {String} mode
27036      * The shadow display mode.  Supports the following options:<br />
27037      * sides: Shadow displays on both sides and bottom only<br />
27038      * frame: Shadow displays equally on all four sides<br />
27039      * drop: Traditional bottom-right drop shadow (default)
27040      */
27041     mode: false,
27042     /**
27043      * @cfg {String} offset
27044      * The number of pixels to offset the shadow from the element (defaults to 4)
27045      */
27046     offset: 4,
27047
27048     // private
27049     defaultMode: "drop",
27050
27051     /**
27052      * Displays the shadow under the target element
27053      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
27054      */
27055     show : function(target){
27056         target = Roo.get(target);
27057         if(!this.el){
27058             this.el = Roo.Shadow.Pool.pull();
27059             if(this.el.dom.nextSibling != target.dom){
27060                 this.el.insertBefore(target);
27061             }
27062         }
27063         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
27064         if(Roo.isIE){
27065             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
27066         }
27067         this.realign(
27068             target.getLeft(true),
27069             target.getTop(true),
27070             target.getWidth(),
27071             target.getHeight()
27072         );
27073         this.el.dom.style.display = "block";
27074     },
27075
27076     /**
27077      * Returns true if the shadow is visible, else false
27078      */
27079     isVisible : function(){
27080         return this.el ? true : false;  
27081     },
27082
27083     /**
27084      * Direct alignment when values are already available. Show must be called at least once before
27085      * calling this method to ensure it is initialized.
27086      * @param {Number} left The target element left position
27087      * @param {Number} top The target element top position
27088      * @param {Number} width The target element width
27089      * @param {Number} height The target element height
27090      */
27091     realign : function(l, t, w, h){
27092         if(!this.el){
27093             return;
27094         }
27095         var a = this.adjusts, d = this.el.dom, s = d.style;
27096         var iea = 0;
27097         s.left = (l+a.l)+"px";
27098         s.top = (t+a.t)+"px";
27099         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
27100  
27101         if(s.width != sws || s.height != shs){
27102             s.width = sws;
27103             s.height = shs;
27104             if(!Roo.isIE){
27105                 var cn = d.childNodes;
27106                 var sww = Math.max(0, (sw-12))+"px";
27107                 cn[0].childNodes[1].style.width = sww;
27108                 cn[1].childNodes[1].style.width = sww;
27109                 cn[2].childNodes[1].style.width = sww;
27110                 cn[1].style.height = Math.max(0, (sh-12))+"px";
27111             }
27112         }
27113     },
27114
27115     /**
27116      * Hides this shadow
27117      */
27118     hide : function(){
27119         if(this.el){
27120             this.el.dom.style.display = "none";
27121             Roo.Shadow.Pool.push(this.el);
27122             delete this.el;
27123         }
27124     },
27125
27126     /**
27127      * Adjust the z-index of this shadow
27128      * @param {Number} zindex The new z-index
27129      */
27130     setZIndex : function(z){
27131         this.zIndex = z;
27132         if(this.el){
27133             this.el.setStyle("z-index", z);
27134         }
27135     }
27136 };
27137
27138 // Private utility class that manages the internal Shadow cache
27139 Roo.Shadow.Pool = function(){
27140     var p = [];
27141     var markup = Roo.isIE ?
27142                  '<div class="x-ie-shadow"></div>' :
27143                  '<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>';
27144     return {
27145         pull : function(){
27146             var sh = p.shift();
27147             if(!sh){
27148                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
27149                 sh.autoBoxAdjust = false;
27150             }
27151             return sh;
27152         },
27153
27154         push : function(sh){
27155             p.push(sh);
27156         }
27157     };
27158 }();/*
27159  * Based on:
27160  * Ext JS Library 1.1.1
27161  * Copyright(c) 2006-2007, Ext JS, LLC.
27162  *
27163  * Originally Released Under LGPL - original licence link has changed is not relivant.
27164  *
27165  * Fork - LGPL
27166  * <script type="text/javascript">
27167  */
27168
27169
27170 /**
27171  * @class Roo.SplitBar
27172  * @extends Roo.util.Observable
27173  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
27174  * <br><br>
27175  * Usage:
27176  * <pre><code>
27177 var split = new Roo.SplitBar("elementToDrag", "elementToSize",
27178                    Roo.SplitBar.HORIZONTAL, Roo.SplitBar.LEFT);
27179 split.setAdapter(new Roo.SplitBar.AbsoluteLayoutAdapter("container"));
27180 split.minSize = 100;
27181 split.maxSize = 600;
27182 split.animate = true;
27183 split.on('moved', splitterMoved);
27184 </code></pre>
27185  * @constructor
27186  * Create a new SplitBar
27187  * @param {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
27188  * @param {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
27189  * @param {Number} orientation (optional) Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27190  * @param {Number} placement (optional) Either Roo.SplitBar.LEFT or Roo.SplitBar.RIGHT for horizontal or  
27191                         Roo.SplitBar.TOP or Roo.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
27192                         position of the SplitBar).
27193  */
27194 Roo.SplitBar = function(dragElement, resizingElement, orientation, placement, existingProxy){
27195     
27196     /** @private */
27197     this.el = Roo.get(dragElement, true);
27198     this.el.dom.unselectable = "on";
27199     /** @private */
27200     this.resizingEl = Roo.get(resizingElement, true);
27201
27202     /**
27203      * @private
27204      * The orientation of the split. Either Roo.SplitBar.HORIZONTAL or Roo.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
27205      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
27206      * @type Number
27207      */
27208     this.orientation = orientation || Roo.SplitBar.HORIZONTAL;
27209     
27210     /**
27211      * The minimum size of the resizing element. (Defaults to 0)
27212      * @type Number
27213      */
27214     this.minSize = 0;
27215     
27216     /**
27217      * The maximum size of the resizing element. (Defaults to 2000)
27218      * @type Number
27219      */
27220     this.maxSize = 2000;
27221     
27222     /**
27223      * Whether to animate the transition to the new size
27224      * @type Boolean
27225      */
27226     this.animate = false;
27227     
27228     /**
27229      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
27230      * @type Boolean
27231      */
27232     this.useShim = false;
27233     
27234     /** @private */
27235     this.shim = null;
27236     
27237     if(!existingProxy){
27238         /** @private */
27239         this.proxy = Roo.SplitBar.createProxy(this.orientation);
27240     }else{
27241         this.proxy = Roo.get(existingProxy).dom;
27242     }
27243     /** @private */
27244     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
27245     
27246     /** @private */
27247     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
27248     
27249     /** @private */
27250     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
27251     
27252     /** @private */
27253     this.dragSpecs = {};
27254     
27255     /**
27256      * @private The adapter to use to positon and resize elements
27257      */
27258     this.adapter = new Roo.SplitBar.BasicLayoutAdapter();
27259     this.adapter.init(this);
27260     
27261     if(this.orientation == Roo.SplitBar.HORIZONTAL){
27262         /** @private */
27263         this.placement = placement || (this.el.getX() > this.resizingEl.getX() ? Roo.SplitBar.LEFT : Roo.SplitBar.RIGHT);
27264         this.el.addClass("x-splitbar-h");
27265     }else{
27266         /** @private */
27267         this.placement = placement || (this.el.getY() > this.resizingEl.getY() ? Roo.SplitBar.TOP : Roo.SplitBar.BOTTOM);
27268         this.el.addClass("x-splitbar-v");
27269     }
27270     
27271     this.addEvents({
27272         /**
27273          * @event resize
27274          * Fires when the splitter is moved (alias for {@link #event-moved})
27275          * @param {Roo.SplitBar} this
27276          * @param {Number} newSize the new width or height
27277          */
27278         "resize" : true,
27279         /**
27280          * @event moved
27281          * Fires when the splitter is moved
27282          * @param {Roo.SplitBar} this
27283          * @param {Number} newSize the new width or height
27284          */
27285         "moved" : true,
27286         /**
27287          * @event beforeresize
27288          * Fires before the splitter is dragged
27289          * @param {Roo.SplitBar} this
27290          */
27291         "beforeresize" : true,
27292
27293         "beforeapply" : true
27294     });
27295
27296     Roo.util.Observable.call(this);
27297 };
27298
27299 Roo.extend(Roo.SplitBar, Roo.util.Observable, {
27300     onStartProxyDrag : function(x, y){
27301         this.fireEvent("beforeresize", this);
27302         if(!this.overlay){
27303             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "x-drag-overlay", html: "&#160;"}, true);
27304             o.unselectable();
27305             o.enableDisplayMode("block");
27306             // all splitbars share the same overlay
27307             Roo.SplitBar.prototype.overlay = o;
27308         }
27309         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
27310         this.overlay.show();
27311         Roo.get(this.proxy).setDisplayed("block");
27312         var size = this.adapter.getElementSize(this);
27313         this.activeMinSize = this.getMinimumSize();;
27314         this.activeMaxSize = this.getMaximumSize();;
27315         var c1 = size - this.activeMinSize;
27316         var c2 = Math.max(this.activeMaxSize - size, 0);
27317         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27318             this.dd.resetConstraints();
27319             this.dd.setXConstraint(
27320                 this.placement == Roo.SplitBar.LEFT ? c1 : c2, 
27321                 this.placement == Roo.SplitBar.LEFT ? c2 : c1
27322             );
27323             this.dd.setYConstraint(0, 0);
27324         }else{
27325             this.dd.resetConstraints();
27326             this.dd.setXConstraint(0, 0);
27327             this.dd.setYConstraint(
27328                 this.placement == Roo.SplitBar.TOP ? c1 : c2, 
27329                 this.placement == Roo.SplitBar.TOP ? c2 : c1
27330             );
27331          }
27332         this.dragSpecs.startSize = size;
27333         this.dragSpecs.startPoint = [x, y];
27334         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
27335     },
27336     
27337     /** 
27338      * @private Called after the drag operation by the DDProxy
27339      */
27340     onEndProxyDrag : function(e){
27341         Roo.get(this.proxy).setDisplayed(false);
27342         var endPoint = Roo.lib.Event.getXY(e);
27343         if(this.overlay){
27344             this.overlay.hide();
27345         }
27346         var newSize;
27347         if(this.orientation == Roo.SplitBar.HORIZONTAL){
27348             newSize = this.dragSpecs.startSize + 
27349                 (this.placement == Roo.SplitBar.LEFT ?
27350                     endPoint[0] - this.dragSpecs.startPoint[0] :
27351                     this.dragSpecs.startPoint[0] - endPoint[0]
27352                 );
27353         }else{
27354             newSize = this.dragSpecs.startSize + 
27355                 (this.placement == Roo.SplitBar.TOP ?
27356                     endPoint[1] - this.dragSpecs.startPoint[1] :
27357                     this.dragSpecs.startPoint[1] - endPoint[1]
27358                 );
27359         }
27360         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
27361         if(newSize != this.dragSpecs.startSize){
27362             if(this.fireEvent('beforeapply', this, newSize) !== false){
27363                 this.adapter.setElementSize(this, newSize);
27364                 this.fireEvent("moved", this, newSize);
27365                 this.fireEvent("resize", this, newSize);
27366             }
27367         }
27368     },
27369     
27370     /**
27371      * Get the adapter this SplitBar uses
27372      * @return The adapter object
27373      */
27374     getAdapter : function(){
27375         return this.adapter;
27376     },
27377     
27378     /**
27379      * Set the adapter this SplitBar uses
27380      * @param {Object} adapter A SplitBar adapter object
27381      */
27382     setAdapter : function(adapter){
27383         this.adapter = adapter;
27384         this.adapter.init(this);
27385     },
27386     
27387     /**
27388      * Gets the minimum size for the resizing element
27389      * @return {Number} The minimum size
27390      */
27391     getMinimumSize : function(){
27392         return this.minSize;
27393     },
27394     
27395     /**
27396      * Sets the minimum size for the resizing element
27397      * @param {Number} minSize The minimum size
27398      */
27399     setMinimumSize : function(minSize){
27400         this.minSize = minSize;
27401     },
27402     
27403     /**
27404      * Gets the maximum size for the resizing element
27405      * @return {Number} The maximum size
27406      */
27407     getMaximumSize : function(){
27408         return this.maxSize;
27409     },
27410     
27411     /**
27412      * Sets the maximum size for the resizing element
27413      * @param {Number} maxSize The maximum size
27414      */
27415     setMaximumSize : function(maxSize){
27416         this.maxSize = maxSize;
27417     },
27418     
27419     /**
27420      * Sets the initialize size for the resizing element
27421      * @param {Number} size The initial size
27422      */
27423     setCurrentSize : function(size){
27424         var oldAnimate = this.animate;
27425         this.animate = false;
27426         this.adapter.setElementSize(this, size);
27427         this.animate = oldAnimate;
27428     },
27429     
27430     /**
27431      * Destroy this splitbar. 
27432      * @param {Boolean} removeEl True to remove the element
27433      */
27434     destroy : function(removeEl){
27435         if(this.shim){
27436             this.shim.remove();
27437         }
27438         this.dd.unreg();
27439         this.proxy.parentNode.removeChild(this.proxy);
27440         if(removeEl){
27441             this.el.remove();
27442         }
27443     }
27444 });
27445
27446 /**
27447  * @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.
27448  */
27449 Roo.SplitBar.createProxy = function(dir){
27450     var proxy = new Roo.Element(document.createElement("div"));
27451     proxy.unselectable();
27452     var cls = 'x-splitbar-proxy';
27453     proxy.addClass(cls + ' ' + (dir == Roo.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
27454     document.body.appendChild(proxy.dom);
27455     return proxy.dom;
27456 };
27457
27458 /** 
27459  * @class Roo.SplitBar.BasicLayoutAdapter
27460  * Default Adapter. It assumes the splitter and resizing element are not positioned
27461  * elements and only gets/sets the width of the element. Generally used for table based layouts.
27462  */
27463 Roo.SplitBar.BasicLayoutAdapter = function(){
27464 };
27465
27466 Roo.SplitBar.BasicLayoutAdapter.prototype = {
27467     // do nothing for now
27468     init : function(s){
27469     
27470     },
27471     /**
27472      * Called before drag operations to get the current size of the resizing element. 
27473      * @param {Roo.SplitBar} s The SplitBar using this adapter
27474      */
27475      getElementSize : function(s){
27476         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27477             return s.resizingEl.getWidth();
27478         }else{
27479             return s.resizingEl.getHeight();
27480         }
27481     },
27482     
27483     /**
27484      * Called after drag operations to set the size of the resizing element.
27485      * @param {Roo.SplitBar} s The SplitBar using this adapter
27486      * @param {Number} newSize The new size to set
27487      * @param {Function} onComplete A function to be invoked when resizing is complete
27488      */
27489     setElementSize : function(s, newSize, onComplete){
27490         if(s.orientation == Roo.SplitBar.HORIZONTAL){
27491             if(!s.animate){
27492                 s.resizingEl.setWidth(newSize);
27493                 if(onComplete){
27494                     onComplete(s, newSize);
27495                 }
27496             }else{
27497                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
27498             }
27499         }else{
27500             
27501             if(!s.animate){
27502                 s.resizingEl.setHeight(newSize);
27503                 if(onComplete){
27504                     onComplete(s, newSize);
27505                 }
27506             }else{
27507                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
27508             }
27509         }
27510     }
27511 };
27512
27513 /** 
27514  *@class Roo.SplitBar.AbsoluteLayoutAdapter
27515  * @extends Roo.SplitBar.BasicLayoutAdapter
27516  * Adapter that  moves the splitter element to align with the resized sizing element. 
27517  * Used with an absolute positioned SplitBar.
27518  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
27519  * document.body, make sure you assign an id to the body element.
27520  */
27521 Roo.SplitBar.AbsoluteLayoutAdapter = function(container){
27522     this.basic = new Roo.SplitBar.BasicLayoutAdapter();
27523     this.container = Roo.get(container);
27524 };
27525
27526 Roo.SplitBar.AbsoluteLayoutAdapter.prototype = {
27527     init : function(s){
27528         this.basic.init(s);
27529     },
27530     
27531     getElementSize : function(s){
27532         return this.basic.getElementSize(s);
27533     },
27534     
27535     setElementSize : function(s, newSize, onComplete){
27536         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
27537     },
27538     
27539     moveSplitter : function(s){
27540         var yes = Roo.SplitBar;
27541         switch(s.placement){
27542             case yes.LEFT:
27543                 s.el.setX(s.resizingEl.getRight());
27544                 break;
27545             case yes.RIGHT:
27546                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
27547                 break;
27548             case yes.TOP:
27549                 s.el.setY(s.resizingEl.getBottom());
27550                 break;
27551             case yes.BOTTOM:
27552                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
27553                 break;
27554         }
27555     }
27556 };
27557
27558 /**
27559  * Orientation constant - Create a vertical SplitBar
27560  * @static
27561  * @type Number
27562  */
27563 Roo.SplitBar.VERTICAL = 1;
27564
27565 /**
27566  * Orientation constant - Create a horizontal SplitBar
27567  * @static
27568  * @type Number
27569  */
27570 Roo.SplitBar.HORIZONTAL = 2;
27571
27572 /**
27573  * Placement constant - The resizing element is to the left of the splitter element
27574  * @static
27575  * @type Number
27576  */
27577 Roo.SplitBar.LEFT = 1;
27578
27579 /**
27580  * Placement constant - The resizing element is to the right of the splitter element
27581  * @static
27582  * @type Number
27583  */
27584 Roo.SplitBar.RIGHT = 2;
27585
27586 /**
27587  * Placement constant - The resizing element is positioned above the splitter element
27588  * @static
27589  * @type Number
27590  */
27591 Roo.SplitBar.TOP = 3;
27592
27593 /**
27594  * Placement constant - The resizing element is positioned under splitter element
27595  * @static
27596  * @type Number
27597  */
27598 Roo.SplitBar.BOTTOM = 4;
27599 /*
27600  * Based on:
27601  * Ext JS Library 1.1.1
27602  * Copyright(c) 2006-2007, Ext JS, LLC.
27603  *
27604  * Originally Released Under LGPL - original licence link has changed is not relivant.
27605  *
27606  * Fork - LGPL
27607  * <script type="text/javascript">
27608  */
27609
27610 /**
27611  * @class Roo.View
27612  * @extends Roo.util.Observable
27613  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
27614  * This class also supports single and multi selection modes. <br>
27615  * Create a data model bound view:
27616  <pre><code>
27617  var store = new Roo.data.Store(...);
27618
27619  var view = new Roo.View({
27620     el : "my-element",
27621     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
27622  
27623     singleSelect: true,
27624     selectedClass: "ydataview-selected",
27625     store: store
27626  });
27627
27628  // listen for node click?
27629  view.on("click", function(vw, index, node, e){
27630  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
27631  });
27632
27633  // load XML data
27634  dataModel.load("foobar.xml");
27635  </code></pre>
27636  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
27637  * <br><br>
27638  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
27639  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
27640  * 
27641  * Note: old style constructor is still suported (container, template, config)
27642  * 
27643  * @constructor
27644  * Create a new View
27645  * @param {Object} config The config object
27646  * 
27647  */
27648 Roo.View = function(config, depreciated_tpl, depreciated_config){
27649     
27650     this.parent = false;
27651     
27652     if (typeof(depreciated_tpl) == 'undefined') {
27653         // new way.. - universal constructor.
27654         Roo.apply(this, config);
27655         this.el  = Roo.get(this.el);
27656     } else {
27657         // old format..
27658         this.el  = Roo.get(config);
27659         this.tpl = depreciated_tpl;
27660         Roo.apply(this, depreciated_config);
27661     }
27662     this.wrapEl  = this.el.wrap().wrap();
27663     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
27664     
27665     
27666     if(typeof(this.tpl) == "string"){
27667         this.tpl = new Roo.Template(this.tpl);
27668     } else {
27669         // support xtype ctors..
27670         this.tpl = new Roo.factory(this.tpl, Roo);
27671     }
27672     
27673     
27674     this.tpl.compile();
27675     
27676     /** @private */
27677     this.addEvents({
27678         /**
27679          * @event beforeclick
27680          * Fires before a click is processed. Returns false to cancel the default action.
27681          * @param {Roo.View} this
27682          * @param {Number} index The index of the target node
27683          * @param {HTMLElement} node The target node
27684          * @param {Roo.EventObject} e The raw event object
27685          */
27686             "beforeclick" : true,
27687         /**
27688          * @event click
27689          * Fires when a template node is clicked.
27690          * @param {Roo.View} this
27691          * @param {Number} index The index of the target node
27692          * @param {HTMLElement} node The target node
27693          * @param {Roo.EventObject} e The raw event object
27694          */
27695             "click" : true,
27696         /**
27697          * @event dblclick
27698          * Fires when a template node is double clicked.
27699          * @param {Roo.View} this
27700          * @param {Number} index The index of the target node
27701          * @param {HTMLElement} node The target node
27702          * @param {Roo.EventObject} e The raw event object
27703          */
27704             "dblclick" : true,
27705         /**
27706          * @event contextmenu
27707          * Fires when a template node is right clicked.
27708          * @param {Roo.View} this
27709          * @param {Number} index The index of the target node
27710          * @param {HTMLElement} node The target node
27711          * @param {Roo.EventObject} e The raw event object
27712          */
27713             "contextmenu" : true,
27714         /**
27715          * @event selectionchange
27716          * Fires when the selected nodes change.
27717          * @param {Roo.View} this
27718          * @param {Array} selections Array of the selected nodes
27719          */
27720             "selectionchange" : true,
27721     
27722         /**
27723          * @event beforeselect
27724          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
27725          * @param {Roo.View} this
27726          * @param {HTMLElement} node The node to be selected
27727          * @param {Array} selections Array of currently selected nodes
27728          */
27729             "beforeselect" : true,
27730         /**
27731          * @event preparedata
27732          * Fires on every row to render, to allow you to change the data.
27733          * @param {Roo.View} this
27734          * @param {Object} data to be rendered (change this)
27735          */
27736           "preparedata" : true
27737           
27738           
27739         });
27740
27741
27742
27743     this.el.on({
27744         "click": this.onClick,
27745         "dblclick": this.onDblClick,
27746         "contextmenu": this.onContextMenu,
27747         scope:this
27748     });
27749
27750     this.selections = [];
27751     this.nodes = [];
27752     this.cmp = new Roo.CompositeElementLite([]);
27753     if(this.store){
27754         this.store = Roo.factory(this.store, Roo.data);
27755         this.setStore(this.store, true);
27756     }
27757     
27758     if ( this.footer && this.footer.xtype) {
27759            
27760          var fctr = this.wrapEl.appendChild(document.createElement("div"));
27761         
27762         this.footer.dataSource = this.store;
27763         this.footer.container = fctr;
27764         this.footer = Roo.factory(this.footer, Roo);
27765         fctr.insertFirst(this.el);
27766         
27767         // this is a bit insane - as the paging toolbar seems to detach the el..
27768 //        dom.parentNode.parentNode.parentNode
27769          // they get detached?
27770     }
27771     
27772     
27773     Roo.View.superclass.constructor.call(this);
27774     
27775     
27776 };
27777
27778 Roo.extend(Roo.View, Roo.util.Observable, {
27779     
27780      /**
27781      * @cfg {Roo.data.Store} store Data store to load data from.
27782      */
27783     store : false,
27784     
27785     /**
27786      * @cfg {String|Roo.Element} el The container element.
27787      */
27788     el : '',
27789     
27790     /**
27791      * @cfg {String|Roo.Template} tpl The template used by this View 
27792      */
27793     tpl : false,
27794     /**
27795      * @cfg {String} dataName the named area of the template to use as the data area
27796      *                          Works with domtemplates roo-name="name"
27797      */
27798     dataName: false,
27799     /**
27800      * @cfg {String} selectedClass The css class to add to selected nodes
27801      */
27802     selectedClass : "x-view-selected",
27803      /**
27804      * @cfg {String} emptyText The empty text to show when nothing is loaded.
27805      */
27806     emptyText : "",
27807     
27808     /**
27809      * @cfg {String} text to display on mask (default Loading)
27810      */
27811     mask : false,
27812     /**
27813      * @cfg {Boolean} multiSelect Allow multiple selection
27814      */
27815     multiSelect : false,
27816     /**
27817      * @cfg {Boolean} singleSelect Allow single selection
27818      */
27819     singleSelect:  false,
27820     
27821     /**
27822      * @cfg {Boolean} toggleSelect - selecting 
27823      */
27824     toggleSelect : false,
27825     
27826     /**
27827      * @cfg {Boolean} tickable - selecting 
27828      */
27829     tickable : false,
27830     
27831     /**
27832      * Returns the element this view is bound to.
27833      * @return {Roo.Element}
27834      */
27835     getEl : function(){
27836         return this.wrapEl;
27837     },
27838     
27839     
27840
27841     /**
27842      * Refreshes the view. - called by datachanged on the store. - do not call directly.
27843      */
27844     refresh : function(){
27845         //Roo.log('refresh');
27846         var t = this.tpl;
27847         
27848         // if we are using something like 'domtemplate', then
27849         // the what gets used is:
27850         // t.applySubtemplate(NAME, data, wrapping data..)
27851         // the outer template then get' applied with
27852         //     the store 'extra data'
27853         // and the body get's added to the
27854         //      roo-name="data" node?
27855         //      <span class='roo-tpl-{name}'></span> ?????
27856         
27857         
27858         
27859         this.clearSelections();
27860         this.el.update("");
27861         var html = [];
27862         var records = this.store.getRange();
27863         if(records.length < 1) {
27864             
27865             // is this valid??  = should it render a template??
27866             
27867             this.el.update(this.emptyText);
27868             return;
27869         }
27870         var el = this.el;
27871         if (this.dataName) {
27872             this.el.update(t.apply(this.store.meta)); //????
27873             el = this.el.child('.roo-tpl-' + this.dataName);
27874         }
27875         
27876         for(var i = 0, len = records.length; i < len; i++){
27877             var data = this.prepareData(records[i].data, i, records[i]);
27878             this.fireEvent("preparedata", this, data, i, records[i]);
27879             
27880             var d = Roo.apply({}, data);
27881             
27882             if(this.tickable){
27883                 Roo.apply(d, {'roo-id' : Roo.id()});
27884                 
27885                 var _this = this;
27886             
27887                 Roo.each(this.parent.item, function(item){
27888                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
27889                         return;
27890                     }
27891                     Roo.apply(d, {'roo-data-checked' : 'checked'});
27892                 });
27893             }
27894             
27895             html[html.length] = Roo.util.Format.trim(
27896                 this.dataName ?
27897                     t.applySubtemplate(this.dataName, d, this.store.meta) :
27898                     t.apply(d)
27899             );
27900         }
27901         
27902         
27903         
27904         el.update(html.join(""));
27905         this.nodes = el.dom.childNodes;
27906         this.updateIndexes(0);
27907     },
27908     
27909
27910     /**
27911      * Function to override to reformat the data that is sent to
27912      * the template for each node.
27913      * DEPRICATED - use the preparedata event handler.
27914      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
27915      * a JSON object for an UpdateManager bound view).
27916      */
27917     prepareData : function(data, index, record)
27918     {
27919         this.fireEvent("preparedata", this, data, index, record);
27920         return data;
27921     },
27922
27923     onUpdate : function(ds, record){
27924         // Roo.log('on update');   
27925         this.clearSelections();
27926         var index = this.store.indexOf(record);
27927         var n = this.nodes[index];
27928         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
27929         n.parentNode.removeChild(n);
27930         this.updateIndexes(index, index);
27931     },
27932
27933     
27934     
27935 // --------- FIXME     
27936     onAdd : function(ds, records, index)
27937     {
27938         //Roo.log(['on Add', ds, records, index] );        
27939         this.clearSelections();
27940         if(this.nodes.length == 0){
27941             this.refresh();
27942             return;
27943         }
27944         var n = this.nodes[index];
27945         for(var i = 0, len = records.length; i < len; i++){
27946             var d = this.prepareData(records[i].data, i, records[i]);
27947             if(n){
27948                 this.tpl.insertBefore(n, d);
27949             }else{
27950                 
27951                 this.tpl.append(this.el, d);
27952             }
27953         }
27954         this.updateIndexes(index);
27955     },
27956
27957     onRemove : function(ds, record, index){
27958        // Roo.log('onRemove');
27959         this.clearSelections();
27960         var el = this.dataName  ?
27961             this.el.child('.roo-tpl-' + this.dataName) :
27962             this.el; 
27963         
27964         el.dom.removeChild(this.nodes[index]);
27965         this.updateIndexes(index);
27966     },
27967
27968     /**
27969      * Refresh an individual node.
27970      * @param {Number} index
27971      */
27972     refreshNode : function(index){
27973         this.onUpdate(this.store, this.store.getAt(index));
27974     },
27975
27976     updateIndexes : function(startIndex, endIndex){
27977         var ns = this.nodes;
27978         startIndex = startIndex || 0;
27979         endIndex = endIndex || ns.length - 1;
27980         for(var i = startIndex; i <= endIndex; i++){
27981             ns[i].nodeIndex = i;
27982         }
27983     },
27984
27985     /**
27986      * Changes the data store this view uses and refresh the view.
27987      * @param {Store} store
27988      */
27989     setStore : function(store, initial){
27990         if(!initial && this.store){
27991             this.store.un("datachanged", this.refresh);
27992             this.store.un("add", this.onAdd);
27993             this.store.un("remove", this.onRemove);
27994             this.store.un("update", this.onUpdate);
27995             this.store.un("clear", this.refresh);
27996             this.store.un("beforeload", this.onBeforeLoad);
27997             this.store.un("load", this.onLoad);
27998             this.store.un("loadexception", this.onLoad);
27999         }
28000         if(store){
28001           
28002             store.on("datachanged", this.refresh, this);
28003             store.on("add", this.onAdd, this);
28004             store.on("remove", this.onRemove, this);
28005             store.on("update", this.onUpdate, this);
28006             store.on("clear", this.refresh, this);
28007             store.on("beforeload", this.onBeforeLoad, this);
28008             store.on("load", this.onLoad, this);
28009             store.on("loadexception", this.onLoad, this);
28010         }
28011         
28012         if(store){
28013             this.refresh();
28014         }
28015     },
28016     /**
28017      * onbeforeLoad - masks the loading area.
28018      *
28019      */
28020     onBeforeLoad : function(store,opts)
28021     {
28022          //Roo.log('onBeforeLoad');   
28023         if (!opts.add) {
28024             this.el.update("");
28025         }
28026         this.el.mask(this.mask ? this.mask : "Loading" ); 
28027     },
28028     onLoad : function ()
28029     {
28030         this.el.unmask();
28031     },
28032     
28033
28034     /**
28035      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
28036      * @param {HTMLElement} node
28037      * @return {HTMLElement} The template node
28038      */
28039     findItemFromChild : function(node){
28040         var el = this.dataName  ?
28041             this.el.child('.roo-tpl-' + this.dataName,true) :
28042             this.el.dom; 
28043         
28044         if(!node || node.parentNode == el){
28045                     return node;
28046             }
28047             var p = node.parentNode;
28048             while(p && p != el){
28049             if(p.parentNode == el){
28050                 return p;
28051             }
28052             p = p.parentNode;
28053         }
28054             return null;
28055     },
28056
28057     /** @ignore */
28058     onClick : function(e){
28059         var item = this.findItemFromChild(e.getTarget());
28060         if(item){
28061             var index = this.indexOf(item);
28062             if(this.onItemClick(item, index, e) !== false){
28063                 this.fireEvent("click", this, index, item, e);
28064             }
28065         }else{
28066             this.clearSelections();
28067         }
28068     },
28069
28070     /** @ignore */
28071     onContextMenu : function(e){
28072         var item = this.findItemFromChild(e.getTarget());
28073         if(item){
28074             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
28075         }
28076     },
28077
28078     /** @ignore */
28079     onDblClick : function(e){
28080         var item = this.findItemFromChild(e.getTarget());
28081         if(item){
28082             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
28083         }
28084     },
28085
28086     onItemClick : function(item, index, e)
28087     {
28088         if(this.fireEvent("beforeclick", this, index, item, e) === false){
28089             return false;
28090         }
28091         if (this.toggleSelect) {
28092             var m = this.isSelected(item) ? 'unselect' : 'select';
28093             //Roo.log(m);
28094             var _t = this;
28095             _t[m](item, true, false);
28096             return true;
28097         }
28098         if(this.multiSelect || this.singleSelect){
28099             if(this.multiSelect && e.shiftKey && this.lastSelection){
28100                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
28101             }else{
28102                 this.select(item, this.multiSelect && e.ctrlKey);
28103                 this.lastSelection = item;
28104             }
28105             
28106             if(!this.tickable){
28107                 e.preventDefault();
28108             }
28109             
28110         }
28111         return true;
28112     },
28113
28114     /**
28115      * Get the number of selected nodes.
28116      * @return {Number}
28117      */
28118     getSelectionCount : function(){
28119         return this.selections.length;
28120     },
28121
28122     /**
28123      * Get the currently selected nodes.
28124      * @return {Array} An array of HTMLElements
28125      */
28126     getSelectedNodes : function(){
28127         return this.selections;
28128     },
28129
28130     /**
28131      * Get the indexes of the selected nodes.
28132      * @return {Array}
28133      */
28134     getSelectedIndexes : function(){
28135         var indexes = [], s = this.selections;
28136         for(var i = 0, len = s.length; i < len; i++){
28137             indexes.push(s[i].nodeIndex);
28138         }
28139         return indexes;
28140     },
28141
28142     /**
28143      * Clear all selections
28144      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
28145      */
28146     clearSelections : function(suppressEvent){
28147         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
28148             this.cmp.elements = this.selections;
28149             this.cmp.removeClass(this.selectedClass);
28150             this.selections = [];
28151             if(!suppressEvent){
28152                 this.fireEvent("selectionchange", this, this.selections);
28153             }
28154         }
28155     },
28156
28157     /**
28158      * Returns true if the passed node is selected
28159      * @param {HTMLElement/Number} node The node or node index
28160      * @return {Boolean}
28161      */
28162     isSelected : function(node){
28163         var s = this.selections;
28164         if(s.length < 1){
28165             return false;
28166         }
28167         node = this.getNode(node);
28168         return s.indexOf(node) !== -1;
28169     },
28170
28171     /**
28172      * Selects nodes.
28173      * @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
28174      * @param {Boolean} keepExisting (optional) true to keep existing selections
28175      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28176      */
28177     select : function(nodeInfo, keepExisting, suppressEvent){
28178         if(nodeInfo instanceof Array){
28179             if(!keepExisting){
28180                 this.clearSelections(true);
28181             }
28182             for(var i = 0, len = nodeInfo.length; i < len; i++){
28183                 this.select(nodeInfo[i], true, true);
28184             }
28185             return;
28186         } 
28187         var node = this.getNode(nodeInfo);
28188         if(!node || this.isSelected(node)){
28189             return; // already selected.
28190         }
28191         if(!keepExisting){
28192             this.clearSelections(true);
28193         }
28194         
28195         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
28196             Roo.fly(node).addClass(this.selectedClass);
28197             this.selections.push(node);
28198             if(!suppressEvent){
28199                 this.fireEvent("selectionchange", this, this.selections);
28200             }
28201         }
28202         
28203         
28204     },
28205       /**
28206      * Unselects nodes.
28207      * @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
28208      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
28209      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
28210      */
28211     unselect : function(nodeInfo, keepExisting, suppressEvent)
28212     {
28213         if(nodeInfo instanceof Array){
28214             Roo.each(this.selections, function(s) {
28215                 this.unselect(s, nodeInfo);
28216             }, this);
28217             return;
28218         }
28219         var node = this.getNode(nodeInfo);
28220         if(!node || !this.isSelected(node)){
28221             //Roo.log("not selected");
28222             return; // not selected.
28223         }
28224         // fireevent???
28225         var ns = [];
28226         Roo.each(this.selections, function(s) {
28227             if (s == node ) {
28228                 Roo.fly(node).removeClass(this.selectedClass);
28229
28230                 return;
28231             }
28232             ns.push(s);
28233         },this);
28234         
28235         this.selections= ns;
28236         this.fireEvent("selectionchange", this, this.selections);
28237     },
28238
28239     /**
28240      * Gets a template node.
28241      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28242      * @return {HTMLElement} The node or null if it wasn't found
28243      */
28244     getNode : function(nodeInfo){
28245         if(typeof nodeInfo == "string"){
28246             return document.getElementById(nodeInfo);
28247         }else if(typeof nodeInfo == "number"){
28248             return this.nodes[nodeInfo];
28249         }
28250         return nodeInfo;
28251     },
28252
28253     /**
28254      * Gets a range template nodes.
28255      * @param {Number} startIndex
28256      * @param {Number} endIndex
28257      * @return {Array} An array of nodes
28258      */
28259     getNodes : function(start, end){
28260         var ns = this.nodes;
28261         start = start || 0;
28262         end = typeof end == "undefined" ? ns.length - 1 : end;
28263         var nodes = [];
28264         if(start <= end){
28265             for(var i = start; i <= end; i++){
28266                 nodes.push(ns[i]);
28267             }
28268         } else{
28269             for(var i = start; i >= end; i--){
28270                 nodes.push(ns[i]);
28271             }
28272         }
28273         return nodes;
28274     },
28275
28276     /**
28277      * Finds the index of the passed node
28278      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
28279      * @return {Number} The index of the node or -1
28280      */
28281     indexOf : function(node){
28282         node = this.getNode(node);
28283         if(typeof node.nodeIndex == "number"){
28284             return node.nodeIndex;
28285         }
28286         var ns = this.nodes;
28287         for(var i = 0, len = ns.length; i < len; i++){
28288             if(ns[i] == node){
28289                 return i;
28290             }
28291         }
28292         return -1;
28293     }
28294 });
28295 /*
28296  * Based on:
28297  * Ext JS Library 1.1.1
28298  * Copyright(c) 2006-2007, Ext JS, LLC.
28299  *
28300  * Originally Released Under LGPL - original licence link has changed is not relivant.
28301  *
28302  * Fork - LGPL
28303  * <script type="text/javascript">
28304  */
28305
28306 /**
28307  * @class Roo.JsonView
28308  * @extends Roo.View
28309  * Shortcut class to create a JSON + {@link Roo.UpdateManager} template view. Usage:
28310 <pre><code>
28311 var view = new Roo.JsonView({
28312     container: "my-element",
28313     tpl: '&lt;div id="{id}"&gt;{foo} - {bar}&lt;/div&gt;', // auto create template
28314     multiSelect: true, 
28315     jsonRoot: "data" 
28316 });
28317
28318 // listen for node click?
28319 view.on("click", function(vw, index, node, e){
28320     alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
28321 });
28322
28323 // direct load of JSON data
28324 view.load("foobar.php");
28325
28326 // Example from my blog list
28327 var tpl = new Roo.Template(
28328     '&lt;div class="entry"&gt;' +
28329     '&lt;a class="entry-title" href="{link}"&gt;{title}&lt;/a&gt;' +
28330     "&lt;h4&gt;{date} by {author} | {comments} Comments&lt;/h4&gt;{description}" +
28331     "&lt;/div&gt;&lt;hr /&gt;"
28332 );
28333
28334 var moreView = new Roo.JsonView({
28335     container :  "entry-list", 
28336     template : tpl,
28337     jsonRoot: "posts"
28338 });
28339 moreView.on("beforerender", this.sortEntries, this);
28340 moreView.load({
28341     url: "/blog/get-posts.php",
28342     params: "allposts=true",
28343     text: "Loading Blog Entries..."
28344 });
28345 </code></pre>
28346
28347 * Note: old code is supported with arguments : (container, template, config)
28348
28349
28350  * @constructor
28351  * Create a new JsonView
28352  * 
28353  * @param {Object} config The config object
28354  * 
28355  */
28356 Roo.JsonView = function(config, depreciated_tpl, depreciated_config){
28357     
28358     
28359     Roo.JsonView.superclass.constructor.call(this, config, depreciated_tpl, depreciated_config);
28360
28361     var um = this.el.getUpdateManager();
28362     um.setRenderer(this);
28363     um.on("update", this.onLoad, this);
28364     um.on("failure", this.onLoadException, this);
28365
28366     /**
28367      * @event beforerender
28368      * Fires before rendering of the downloaded JSON data.
28369      * @param {Roo.JsonView} this
28370      * @param {Object} data The JSON data loaded
28371      */
28372     /**
28373      * @event load
28374      * Fires when data is loaded.
28375      * @param {Roo.JsonView} this
28376      * @param {Object} data The JSON data loaded
28377      * @param {Object} response The raw Connect response object
28378      */
28379     /**
28380      * @event loadexception
28381      * Fires when loading fails.
28382      * @param {Roo.JsonView} this
28383      * @param {Object} response The raw Connect response object
28384      */
28385     this.addEvents({
28386         'beforerender' : true,
28387         'load' : true,
28388         'loadexception' : true
28389     });
28390 };
28391 Roo.extend(Roo.JsonView, Roo.View, {
28392     /**
28393      * @type {String} The root property in the loaded JSON object that contains the data
28394      */
28395     jsonRoot : "",
28396
28397     /**
28398      * Refreshes the view.
28399      */
28400     refresh : function(){
28401         this.clearSelections();
28402         this.el.update("");
28403         var html = [];
28404         var o = this.jsonData;
28405         if(o && o.length > 0){
28406             for(var i = 0, len = o.length; i < len; i++){
28407                 var data = this.prepareData(o[i], i, o);
28408                 html[html.length] = this.tpl.apply(data);
28409             }
28410         }else{
28411             html.push(this.emptyText);
28412         }
28413         this.el.update(html.join(""));
28414         this.nodes = this.el.dom.childNodes;
28415         this.updateIndexes(0);
28416     },
28417
28418     /**
28419      * 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.
28420      * @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:
28421      <pre><code>
28422      view.load({
28423          url: "your-url.php",
28424          params: {param1: "foo", param2: "bar"}, // or a URL encoded string
28425          callback: yourFunction,
28426          scope: yourObject, //(optional scope)
28427          discardUrl: false,
28428          nocache: false,
28429          text: "Loading...",
28430          timeout: 30,
28431          scripts: false
28432      });
28433      </code></pre>
28434      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
28435      * 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.
28436      * @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}
28437      * @param {Function} callback (optional) Callback when transaction is complete - called with signature (oElement, bSuccess)
28438      * @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.
28439      */
28440     load : function(){
28441         var um = this.el.getUpdateManager();
28442         um.update.apply(um, arguments);
28443     },
28444
28445     // note - render is a standard framework call...
28446     // using it for the response is really flaky... - it's called by UpdateManager normally, except when called by the XComponent/addXtype.
28447     render : function(el, response){
28448         
28449         this.clearSelections();
28450         this.el.update("");
28451         var o;
28452         try{
28453             if (response != '') {
28454                 o = Roo.util.JSON.decode(response.responseText);
28455                 if(this.jsonRoot){
28456                     
28457                     o = o[this.jsonRoot];
28458                 }
28459             }
28460         } catch(e){
28461         }
28462         /**
28463          * The current JSON data or null
28464          */
28465         this.jsonData = o;
28466         this.beforeRender();
28467         this.refresh();
28468     },
28469
28470 /**
28471  * Get the number of records in the current JSON dataset
28472  * @return {Number}
28473  */
28474     getCount : function(){
28475         return this.jsonData ? this.jsonData.length : 0;
28476     },
28477
28478 /**
28479  * Returns the JSON object for the specified node(s)
28480  * @param {HTMLElement/Array} node The node or an array of nodes
28481  * @return {Object/Array} If you pass in an array, you get an array back, otherwise
28482  * you get the JSON object for the node
28483  */
28484     getNodeData : function(node){
28485         if(node instanceof Array){
28486             var data = [];
28487             for(var i = 0, len = node.length; i < len; i++){
28488                 data.push(this.getNodeData(node[i]));
28489             }
28490             return data;
28491         }
28492         return this.jsonData[this.indexOf(node)] || null;
28493     },
28494
28495     beforeRender : function(){
28496         this.snapshot = this.jsonData;
28497         if(this.sortInfo){
28498             this.sort.apply(this, this.sortInfo);
28499         }
28500         this.fireEvent("beforerender", this, this.jsonData);
28501     },
28502
28503     onLoad : function(el, o){
28504         this.fireEvent("load", this, this.jsonData, o);
28505     },
28506
28507     onLoadException : function(el, o){
28508         this.fireEvent("loadexception", this, o);
28509     },
28510
28511 /**
28512  * Filter the data by a specific property.
28513  * @param {String} property A property on your JSON objects
28514  * @param {String/RegExp} value Either string that the property values
28515  * should start with, or a RegExp to test against the property
28516  */
28517     filter : function(property, value){
28518         if(this.jsonData){
28519             var data = [];
28520             var ss = this.snapshot;
28521             if(typeof value == "string"){
28522                 var vlen = value.length;
28523                 if(vlen == 0){
28524                     this.clearFilter();
28525                     return;
28526                 }
28527                 value = value.toLowerCase();
28528                 for(var i = 0, len = ss.length; i < len; i++){
28529                     var o = ss[i];
28530                     if(o[property].substr(0, vlen).toLowerCase() == value){
28531                         data.push(o);
28532                     }
28533                 }
28534             } else if(value.exec){ // regex?
28535                 for(var i = 0, len = ss.length; i < len; i++){
28536                     var o = ss[i];
28537                     if(value.test(o[property])){
28538                         data.push(o);
28539                     }
28540                 }
28541             } else{
28542                 return;
28543             }
28544             this.jsonData = data;
28545             this.refresh();
28546         }
28547     },
28548
28549 /**
28550  * Filter by a function. The passed function will be called with each
28551  * object in the current dataset. If the function returns true the value is kept,
28552  * otherwise it is filtered.
28553  * @param {Function} fn
28554  * @param {Object} scope (optional) The scope of the function (defaults to this JsonView)
28555  */
28556     filterBy : function(fn, scope){
28557         if(this.jsonData){
28558             var data = [];
28559             var ss = this.snapshot;
28560             for(var i = 0, len = ss.length; i < len; i++){
28561                 var o = ss[i];
28562                 if(fn.call(scope || this, o)){
28563                     data.push(o);
28564                 }
28565             }
28566             this.jsonData = data;
28567             this.refresh();
28568         }
28569     },
28570
28571 /**
28572  * Clears the current filter.
28573  */
28574     clearFilter : function(){
28575         if(this.snapshot && this.jsonData != this.snapshot){
28576             this.jsonData = this.snapshot;
28577             this.refresh();
28578         }
28579     },
28580
28581
28582 /**
28583  * Sorts the data for this view and refreshes it.
28584  * @param {String} property A property on your JSON objects to sort on
28585  * @param {String} direction (optional) "desc" or "asc" (defaults to "asc")
28586  * @param {Function} sortType (optional) A function to call to convert the data to a sortable value.
28587  */
28588     sort : function(property, dir, sortType){
28589         this.sortInfo = Array.prototype.slice.call(arguments, 0);
28590         if(this.jsonData){
28591             var p = property;
28592             var dsc = dir && dir.toLowerCase() == "desc";
28593             var f = function(o1, o2){
28594                 var v1 = sortType ? sortType(o1[p]) : o1[p];
28595                 var v2 = sortType ? sortType(o2[p]) : o2[p];
28596                 ;
28597                 if(v1 < v2){
28598                     return dsc ? +1 : -1;
28599                 } else if(v1 > v2){
28600                     return dsc ? -1 : +1;
28601                 } else{
28602                     return 0;
28603                 }
28604             };
28605             this.jsonData.sort(f);
28606             this.refresh();
28607             if(this.jsonData != this.snapshot){
28608                 this.snapshot.sort(f);
28609             }
28610         }
28611     }
28612 });/*
28613  * Based on:
28614  * Ext JS Library 1.1.1
28615  * Copyright(c) 2006-2007, Ext JS, LLC.
28616  *
28617  * Originally Released Under LGPL - original licence link has changed is not relivant.
28618  *
28619  * Fork - LGPL
28620  * <script type="text/javascript">
28621  */
28622  
28623
28624 /**
28625  * @class Roo.ColorPalette
28626  * @extends Roo.Component
28627  * Simple color palette class for choosing colors.  The palette can be rendered to any container.<br />
28628  * Here's an example of typical usage:
28629  * <pre><code>
28630 var cp = new Roo.ColorPalette({value:'993300'});  // initial selected color
28631 cp.render('my-div');
28632
28633 cp.on('select', function(palette, selColor){
28634     // do something with selColor
28635 });
28636 </code></pre>
28637  * @constructor
28638  * Create a new ColorPalette
28639  * @param {Object} config The config object
28640  */
28641 Roo.ColorPalette = function(config){
28642     Roo.ColorPalette.superclass.constructor.call(this, config);
28643     this.addEvents({
28644         /**
28645              * @event select
28646              * Fires when a color is selected
28647              * @param {ColorPalette} this
28648              * @param {String} color The 6-digit color hex code (without the # symbol)
28649              */
28650         select: true
28651     });
28652
28653     if(this.handler){
28654         this.on("select", this.handler, this.scope, true);
28655     }
28656 };
28657 Roo.extend(Roo.ColorPalette, Roo.Component, {
28658     /**
28659      * @cfg {String} itemCls
28660      * The CSS class to apply to the containing element (defaults to "x-color-palette")
28661      */
28662     itemCls : "x-color-palette",
28663     /**
28664      * @cfg {String} value
28665      * The initial color to highlight (should be a valid 6-digit color hex code without the # symbol).  Note that
28666      * the hex codes are case-sensitive.
28667      */
28668     value : null,
28669     clickEvent:'click',
28670     // private
28671     ctype: "Roo.ColorPalette",
28672
28673     /**
28674      * @cfg {Boolean} allowReselect If set to true then reselecting a color that is already selected fires the selection event
28675      */
28676     allowReselect : false,
28677
28678     /**
28679      * <p>An array of 6-digit color hex code strings (without the # symbol).  This array can contain any number
28680      * of colors, and each hex code should be unique.  The width of the palette is controlled via CSS by adjusting
28681      * the width property of the 'x-color-palette' class (or assigning a custom class), so you can balance the number
28682      * of colors with the width setting until the box is symmetrical.</p>
28683      * <p>You can override individual colors if needed:</p>
28684      * <pre><code>
28685 var cp = new Roo.ColorPalette();
28686 cp.colors[0] = "FF0000";  // change the first box to red
28687 </code></pre>
28688
28689 Or you can provide a custom array of your own for complete control:
28690 <pre><code>
28691 var cp = new Roo.ColorPalette();
28692 cp.colors = ["000000", "993300", "333300"];
28693 </code></pre>
28694      * @type Array
28695      */
28696     colors : [
28697         "000000", "993300", "333300", "003300", "003366", "000080", "333399", "333333",
28698         "800000", "FF6600", "808000", "008000", "008080", "0000FF", "666699", "808080",
28699         "FF0000", "FF9900", "99CC00", "339966", "33CCCC", "3366FF", "800080", "969696",
28700         "FF00FF", "FFCC00", "FFFF00", "00FF00", "00FFFF", "00CCFF", "993366", "C0C0C0",
28701         "FF99CC", "FFCC99", "FFFF99", "CCFFCC", "CCFFFF", "99CCFF", "CC99FF", "FFFFFF"
28702     ],
28703
28704     // private
28705     onRender : function(container, position){
28706         var t = new Roo.MasterTemplate(
28707             '<tpl><a href="#" class="color-{0}" hidefocus="on"><em><span style="background:#{0}" unselectable="on">&#160;</span></em></a></tpl>'
28708         );
28709         var c = this.colors;
28710         for(var i = 0, len = c.length; i < len; i++){
28711             t.add([c[i]]);
28712         }
28713         var el = document.createElement("div");
28714         el.className = this.itemCls;
28715         t.overwrite(el);
28716         container.dom.insertBefore(el, position);
28717         this.el = Roo.get(el);
28718         this.el.on(this.clickEvent, this.handleClick,  this, {delegate: "a"});
28719         if(this.clickEvent != 'click'){
28720             this.el.on('click', Roo.emptyFn,  this, {delegate: "a", preventDefault:true});
28721         }
28722     },
28723
28724     // private
28725     afterRender : function(){
28726         Roo.ColorPalette.superclass.afterRender.call(this);
28727         if(this.value){
28728             var s = this.value;
28729             this.value = null;
28730             this.select(s);
28731         }
28732     },
28733
28734     // private
28735     handleClick : function(e, t){
28736         e.preventDefault();
28737         if(!this.disabled){
28738             var c = t.className.match(/(?:^|\s)color-(.{6})(?:\s|$)/)[1];
28739             this.select(c.toUpperCase());
28740         }
28741     },
28742
28743     /**
28744      * Selects the specified color in the palette (fires the select event)
28745      * @param {String} color A valid 6-digit color hex code (# will be stripped if included)
28746      */
28747     select : function(color){
28748         color = color.replace("#", "");
28749         if(color != this.value || this.allowReselect){
28750             var el = this.el;
28751             if(this.value){
28752                 el.child("a.color-"+this.value).removeClass("x-color-palette-sel");
28753             }
28754             el.child("a.color-"+color).addClass("x-color-palette-sel");
28755             this.value = color;
28756             this.fireEvent("select", this, color);
28757         }
28758     }
28759 });/*
28760  * Based on:
28761  * Ext JS Library 1.1.1
28762  * Copyright(c) 2006-2007, Ext JS, LLC.
28763  *
28764  * Originally Released Under LGPL - original licence link has changed is not relivant.
28765  *
28766  * Fork - LGPL
28767  * <script type="text/javascript">
28768  */
28769  
28770 /**
28771  * @class Roo.DatePicker
28772  * @extends Roo.Component
28773  * Simple date picker class.
28774  * @constructor
28775  * Create a new DatePicker
28776  * @param {Object} config The config object
28777  */
28778 Roo.DatePicker = function(config){
28779     Roo.DatePicker.superclass.constructor.call(this, config);
28780
28781     this.value = config && config.value ?
28782                  config.value.clearTime() : new Date().clearTime();
28783
28784     this.addEvents({
28785         /**
28786              * @event select
28787              * Fires when a date is selected
28788              * @param {DatePicker} this
28789              * @param {Date} date The selected date
28790              */
28791         'select': true,
28792         /**
28793              * @event monthchange
28794              * Fires when the displayed month changes 
28795              * @param {DatePicker} this
28796              * @param {Date} date The selected month
28797              */
28798         'monthchange': true
28799     });
28800
28801     if(this.handler){
28802         this.on("select", this.handler,  this.scope || this);
28803     }
28804     // build the disabledDatesRE
28805     if(!this.disabledDatesRE && this.disabledDates){
28806         var dd = this.disabledDates;
28807         var re = "(?:";
28808         for(var i = 0; i < dd.length; i++){
28809             re += dd[i];
28810             if(i != dd.length-1) {
28811                 re += "|";
28812             }
28813         }
28814         this.disabledDatesRE = new RegExp(re + ")");
28815     }
28816 };
28817
28818 Roo.extend(Roo.DatePicker, Roo.Component, {
28819     /**
28820      * @cfg {String} todayText
28821      * The text to display on the button that selects the current date (defaults to "Today")
28822      */
28823     todayText : "Today",
28824     /**
28825      * @cfg {String} okText
28826      * The text to display on the ok button
28827      */
28828     okText : "&#160;OK&#160;", // &#160; to give the user extra clicking room
28829     /**
28830      * @cfg {String} cancelText
28831      * The text to display on the cancel button
28832      */
28833     cancelText : "Cancel",
28834     /**
28835      * @cfg {String} todayTip
28836      * The tooltip to display for the button that selects the current date (defaults to "{current date} (Spacebar)")
28837      */
28838     todayTip : "{0} (Spacebar)",
28839     /**
28840      * @cfg {Date} minDate
28841      * Minimum allowable date (JavaScript date object, defaults to null)
28842      */
28843     minDate : null,
28844     /**
28845      * @cfg {Date} maxDate
28846      * Maximum allowable date (JavaScript date object, defaults to null)
28847      */
28848     maxDate : null,
28849     /**
28850      * @cfg {String} minText
28851      * The error text to display if the minDate validation fails (defaults to "This date is before the minimum date")
28852      */
28853     minText : "This date is before the minimum date",
28854     /**
28855      * @cfg {String} maxText
28856      * The error text to display if the maxDate validation fails (defaults to "This date is after the maximum date")
28857      */
28858     maxText : "This date is after the maximum date",
28859     /**
28860      * @cfg {String} format
28861      * The default date format string which can be overriden for localization support.  The format must be
28862      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
28863      */
28864     format : "m/d/y",
28865     /**
28866      * @cfg {Array} disabledDays
28867      * An array of days to disable, 0-based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
28868      */
28869     disabledDays : null,
28870     /**
28871      * @cfg {String} disabledDaysText
28872      * The tooltip to display when the date falls on a disabled day (defaults to "")
28873      */
28874     disabledDaysText : "",
28875     /**
28876      * @cfg {RegExp} disabledDatesRE
28877      * JavaScript regular expression used to disable a pattern of dates (defaults to null)
28878      */
28879     disabledDatesRE : null,
28880     /**
28881      * @cfg {String} disabledDatesText
28882      * The tooltip text to display when the date falls on a disabled date (defaults to "")
28883      */
28884     disabledDatesText : "",
28885     /**
28886      * @cfg {Boolean} constrainToViewport
28887      * True to constrain the date picker to the viewport (defaults to true)
28888      */
28889     constrainToViewport : true,
28890     /**
28891      * @cfg {Array} monthNames
28892      * An array of textual month names which can be overriden for localization support (defaults to Date.monthNames)
28893      */
28894     monthNames : Date.monthNames,
28895     /**
28896      * @cfg {Array} dayNames
28897      * An array of textual day names which can be overriden for localization support (defaults to Date.dayNames)
28898      */
28899     dayNames : Date.dayNames,
28900     /**
28901      * @cfg {String} nextText
28902      * The next month navigation button tooltip (defaults to 'Next Month (Control+Right)')
28903      */
28904     nextText: 'Next Month (Control+Right)',
28905     /**
28906      * @cfg {String} prevText
28907      * The previous month navigation button tooltip (defaults to 'Previous Month (Control+Left)')
28908      */
28909     prevText: 'Previous Month (Control+Left)',
28910     /**
28911      * @cfg {String} monthYearText
28912      * The header month selector tooltip (defaults to 'Choose a month (Control+Up/Down to move years)')
28913      */
28914     monthYearText: 'Choose a month (Control+Up/Down to move years)',
28915     /**
28916      * @cfg {Number} startDay
28917      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
28918      */
28919     startDay : 0,
28920     /**
28921      * @cfg {Bool} showClear
28922      * Show a clear button (usefull for date form elements that can be blank.)
28923      */
28924     
28925     showClear: false,
28926     
28927     /**
28928      * Sets the value of the date field
28929      * @param {Date} value The date to set
28930      */
28931     setValue : function(value){
28932         var old = this.value;
28933         
28934         if (typeof(value) == 'string') {
28935          
28936             value = Date.parseDate(value, this.format);
28937         }
28938         if (!value) {
28939             value = new Date();
28940         }
28941         
28942         this.value = value.clearTime(true);
28943         if(this.el){
28944             this.update(this.value);
28945         }
28946     },
28947
28948     /**
28949      * Gets the current selected value of the date field
28950      * @return {Date} The selected date
28951      */
28952     getValue : function(){
28953         return this.value;
28954     },
28955
28956     // private
28957     focus : function(){
28958         if(this.el){
28959             this.update(this.activeDate);
28960         }
28961     },
28962
28963     // privateval
28964     onRender : function(container, position){
28965         
28966         var m = [
28967              '<table cellspacing="0">',
28968                 '<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>',
28969                 '<tr><td colspan="3"><table class="x-date-inner" cellspacing="0"><thead><tr>'];
28970         var dn = this.dayNames;
28971         for(var i = 0; i < 7; i++){
28972             var d = this.startDay+i;
28973             if(d > 6){
28974                 d = d-7;
28975             }
28976             m.push("<th><span>", dn[d].substr(0,1), "</span></th>");
28977         }
28978         m[m.length] = "</tr></thead><tbody><tr>";
28979         for(var i = 0; i < 42; i++) {
28980             if(i % 7 == 0 && i != 0){
28981                 m[m.length] = "</tr><tr>";
28982             }
28983             m[m.length] = '<td><a href="#" hidefocus="on" class="x-date-date" tabIndex="1"><em><span></span></em></a></td>';
28984         }
28985         m[m.length] = '</tr></tbody></table></td></tr><tr>'+
28986             '<td colspan="3" class="x-date-bottom" align="center"></td></tr></table><div class="x-date-mp"></div>';
28987
28988         var el = document.createElement("div");
28989         el.className = "x-date-picker";
28990         el.innerHTML = m.join("");
28991
28992         container.dom.insertBefore(el, position);
28993
28994         this.el = Roo.get(el);
28995         this.eventEl = Roo.get(el.firstChild);
28996
28997         new Roo.util.ClickRepeater(this.el.child("td.x-date-left a"), {
28998             handler: this.showPrevMonth,
28999             scope: this,
29000             preventDefault:true,
29001             stopDefault:true
29002         });
29003
29004         new Roo.util.ClickRepeater(this.el.child("td.x-date-right a"), {
29005             handler: this.showNextMonth,
29006             scope: this,
29007             preventDefault:true,
29008             stopDefault:true
29009         });
29010
29011         this.eventEl.on("mousewheel", this.handleMouseWheel,  this);
29012
29013         this.monthPicker = this.el.down('div.x-date-mp');
29014         this.monthPicker.enableDisplayMode('block');
29015         
29016         var kn = new Roo.KeyNav(this.eventEl, {
29017             "left" : function(e){
29018                 e.ctrlKey ?
29019                     this.showPrevMonth() :
29020                     this.update(this.activeDate.add("d", -1));
29021             },
29022
29023             "right" : function(e){
29024                 e.ctrlKey ?
29025                     this.showNextMonth() :
29026                     this.update(this.activeDate.add("d", 1));
29027             },
29028
29029             "up" : function(e){
29030                 e.ctrlKey ?
29031                     this.showNextYear() :
29032                     this.update(this.activeDate.add("d", -7));
29033             },
29034
29035             "down" : function(e){
29036                 e.ctrlKey ?
29037                     this.showPrevYear() :
29038                     this.update(this.activeDate.add("d", 7));
29039             },
29040
29041             "pageUp" : function(e){
29042                 this.showNextMonth();
29043             },
29044
29045             "pageDown" : function(e){
29046                 this.showPrevMonth();
29047             },
29048
29049             "enter" : function(e){
29050                 e.stopPropagation();
29051                 return true;
29052             },
29053
29054             scope : this
29055         });
29056
29057         this.eventEl.on("click", this.handleDateClick,  this, {delegate: "a.x-date-date"});
29058
29059         this.eventEl.addKeyListener(Roo.EventObject.SPACE, this.selectToday,  this);
29060
29061         this.el.unselectable();
29062         
29063         this.cells = this.el.select("table.x-date-inner tbody td");
29064         this.textNodes = this.el.query("table.x-date-inner tbody span");
29065
29066         this.mbtn = new Roo.Button(this.el.child("td.x-date-middle", true), {
29067             text: "&#160;",
29068             tooltip: this.monthYearText
29069         });
29070
29071         this.mbtn.on('click', this.showMonthPicker, this);
29072         this.mbtn.el.child(this.mbtn.menuClassTarget).addClass("x-btn-with-menu");
29073
29074
29075         var today = (new Date()).dateFormat(this.format);
29076         
29077         var baseTb = new Roo.Toolbar(this.el.child("td.x-date-bottom", true));
29078         if (this.showClear) {
29079             baseTb.add( new Roo.Toolbar.Fill());
29080         }
29081         baseTb.add({
29082             text: String.format(this.todayText, today),
29083             tooltip: String.format(this.todayTip, today),
29084             handler: this.selectToday,
29085             scope: this
29086         });
29087         
29088         //var todayBtn = new Roo.Button(this.el.child("td.x-date-bottom", true), {
29089             
29090         //});
29091         if (this.showClear) {
29092             
29093             baseTb.add( new Roo.Toolbar.Fill());
29094             baseTb.add({
29095                 text: '&#160;',
29096                 cls: 'x-btn-icon x-btn-clear',
29097                 handler: function() {
29098                     //this.value = '';
29099                     this.fireEvent("select", this, '');
29100                 },
29101                 scope: this
29102             });
29103         }
29104         
29105         
29106         if(Roo.isIE){
29107             this.el.repaint();
29108         }
29109         this.update(this.value);
29110     },
29111
29112     createMonthPicker : function(){
29113         if(!this.monthPicker.dom.firstChild){
29114             var buf = ['<table border="0" cellspacing="0">'];
29115             for(var i = 0; i < 6; i++){
29116                 buf.push(
29117                     '<tr><td class="x-date-mp-month"><a href="#">', this.monthNames[i].substr(0, 3), '</a></td>',
29118                     '<td class="x-date-mp-month x-date-mp-sep"><a href="#">', this.monthNames[i+6].substr(0, 3), '</a></td>',
29119                     i == 0 ?
29120                     '<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>' :
29121                     '<td class="x-date-mp-year"><a href="#"></a></td><td class="x-date-mp-year"><a href="#"></a></td></tr>'
29122                 );
29123             }
29124             buf.push(
29125                 '<tr class="x-date-mp-btns"><td colspan="4"><button type="button" class="x-date-mp-ok">',
29126                     this.okText,
29127                     '</button><button type="button" class="x-date-mp-cancel">',
29128                     this.cancelText,
29129                     '</button></td></tr>',
29130                 '</table>'
29131             );
29132             this.monthPicker.update(buf.join(''));
29133             this.monthPicker.on('click', this.onMonthClick, this);
29134             this.monthPicker.on('dblclick', this.onMonthDblClick, this);
29135
29136             this.mpMonths = this.monthPicker.select('td.x-date-mp-month');
29137             this.mpYears = this.monthPicker.select('td.x-date-mp-year');
29138
29139             this.mpMonths.each(function(m, a, i){
29140                 i += 1;
29141                 if((i%2) == 0){
29142                     m.dom.xmonth = 5 + Math.round(i * .5);
29143                 }else{
29144                     m.dom.xmonth = Math.round((i-1) * .5);
29145                 }
29146             });
29147         }
29148     },
29149
29150     showMonthPicker : function(){
29151         this.createMonthPicker();
29152         var size = this.el.getSize();
29153         this.monthPicker.setSize(size);
29154         this.monthPicker.child('table').setSize(size);
29155
29156         this.mpSelMonth = (this.activeDate || this.value).getMonth();
29157         this.updateMPMonth(this.mpSelMonth);
29158         this.mpSelYear = (this.activeDate || this.value).getFullYear();
29159         this.updateMPYear(this.mpSelYear);
29160
29161         this.monthPicker.slideIn('t', {duration:.2});
29162     },
29163
29164     updateMPYear : function(y){
29165         this.mpyear = y;
29166         var ys = this.mpYears.elements;
29167         for(var i = 1; i <= 10; i++){
29168             var td = ys[i-1], y2;
29169             if((i%2) == 0){
29170                 y2 = y + Math.round(i * .5);
29171                 td.firstChild.innerHTML = y2;
29172                 td.xyear = y2;
29173             }else{
29174                 y2 = y - (5-Math.round(i * .5));
29175                 td.firstChild.innerHTML = y2;
29176                 td.xyear = y2;
29177             }
29178             this.mpYears.item(i-1)[y2 == this.mpSelYear ? 'addClass' : 'removeClass']('x-date-mp-sel');
29179         }
29180     },
29181
29182     updateMPMonth : function(sm){
29183         this.mpMonths.each(function(m, a, i){
29184             m[m.dom.xmonth == sm ? 'addClass' : 'removeClass']('x-date-mp-sel');
29185         });
29186     },
29187
29188     selectMPMonth: function(m){
29189         
29190     },
29191
29192     onMonthClick : function(e, t){
29193         e.stopEvent();
29194         var el = new Roo.Element(t), pn;
29195         if(el.is('button.x-date-mp-cancel')){
29196             this.hideMonthPicker();
29197         }
29198         else if(el.is('button.x-date-mp-ok')){
29199             this.update(new Date(this.mpSelYear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29200             this.hideMonthPicker();
29201         }
29202         else if(pn = el.up('td.x-date-mp-month', 2)){
29203             this.mpMonths.removeClass('x-date-mp-sel');
29204             pn.addClass('x-date-mp-sel');
29205             this.mpSelMonth = pn.dom.xmonth;
29206         }
29207         else if(pn = el.up('td.x-date-mp-year', 2)){
29208             this.mpYears.removeClass('x-date-mp-sel');
29209             pn.addClass('x-date-mp-sel');
29210             this.mpSelYear = pn.dom.xyear;
29211         }
29212         else if(el.is('a.x-date-mp-prev')){
29213             this.updateMPYear(this.mpyear-10);
29214         }
29215         else if(el.is('a.x-date-mp-next')){
29216             this.updateMPYear(this.mpyear+10);
29217         }
29218     },
29219
29220     onMonthDblClick : function(e, t){
29221         e.stopEvent();
29222         var el = new Roo.Element(t), pn;
29223         if(pn = el.up('td.x-date-mp-month', 2)){
29224             this.update(new Date(this.mpSelYear, pn.dom.xmonth, (this.activeDate || this.value).getDate()));
29225             this.hideMonthPicker();
29226         }
29227         else if(pn = el.up('td.x-date-mp-year', 2)){
29228             this.update(new Date(pn.dom.xyear, this.mpSelMonth, (this.activeDate || this.value).getDate()));
29229             this.hideMonthPicker();
29230         }
29231     },
29232
29233     hideMonthPicker : function(disableAnim){
29234         if(this.monthPicker){
29235             if(disableAnim === true){
29236                 this.monthPicker.hide();
29237             }else{
29238                 this.monthPicker.slideOut('t', {duration:.2});
29239             }
29240         }
29241     },
29242
29243     // private
29244     showPrevMonth : function(e){
29245         this.update(this.activeDate.add("mo", -1));
29246     },
29247
29248     // private
29249     showNextMonth : function(e){
29250         this.update(this.activeDate.add("mo", 1));
29251     },
29252
29253     // private
29254     showPrevYear : function(){
29255         this.update(this.activeDate.add("y", -1));
29256     },
29257
29258     // private
29259     showNextYear : function(){
29260         this.update(this.activeDate.add("y", 1));
29261     },
29262
29263     // private
29264     handleMouseWheel : function(e){
29265         var delta = e.getWheelDelta();
29266         if(delta > 0){
29267             this.showPrevMonth();
29268             e.stopEvent();
29269         } else if(delta < 0){
29270             this.showNextMonth();
29271             e.stopEvent();
29272         }
29273     },
29274
29275     // private
29276     handleDateClick : function(e, t){
29277         e.stopEvent();
29278         if(t.dateValue && !Roo.fly(t.parentNode).hasClass("x-date-disabled")){
29279             this.setValue(new Date(t.dateValue));
29280             this.fireEvent("select", this, this.value);
29281         }
29282     },
29283
29284     // private
29285     selectToday : function(){
29286         this.setValue(new Date().clearTime());
29287         this.fireEvent("select", this, this.value);
29288     },
29289
29290     // private
29291     update : function(date)
29292     {
29293         var vd = this.activeDate;
29294         this.activeDate = date;
29295         if(vd && this.el){
29296             var t = date.getTime();
29297             if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
29298                 this.cells.removeClass("x-date-selected");
29299                 this.cells.each(function(c){
29300                    if(c.dom.firstChild.dateValue == t){
29301                        c.addClass("x-date-selected");
29302                        setTimeout(function(){
29303                             try{c.dom.firstChild.focus();}catch(e){}
29304                        }, 50);
29305                        return false;
29306                    }
29307                 });
29308                 return;
29309             }
29310         }
29311         
29312         var days = date.getDaysInMonth();
29313         var firstOfMonth = date.getFirstDateOfMonth();
29314         var startingPos = firstOfMonth.getDay()-this.startDay;
29315
29316         if(startingPos <= this.startDay){
29317             startingPos += 7;
29318         }
29319
29320         var pm = date.add("mo", -1);
29321         var prevStart = pm.getDaysInMonth()-startingPos;
29322
29323         var cells = this.cells.elements;
29324         var textEls = this.textNodes;
29325         days += startingPos;
29326
29327         // convert everything to numbers so it's fast
29328         var day = 86400000;
29329         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
29330         var today = new Date().clearTime().getTime();
29331         var sel = date.clearTime().getTime();
29332         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
29333         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
29334         var ddMatch = this.disabledDatesRE;
29335         var ddText = this.disabledDatesText;
29336         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
29337         var ddaysText = this.disabledDaysText;
29338         var format = this.format;
29339
29340         var setCellClass = function(cal, cell){
29341             cell.title = "";
29342             var t = d.getTime();
29343             cell.firstChild.dateValue = t;
29344             if(t == today){
29345                 cell.className += " x-date-today";
29346                 cell.title = cal.todayText;
29347             }
29348             if(t == sel){
29349                 cell.className += " x-date-selected";
29350                 setTimeout(function(){
29351                     try{cell.firstChild.focus();}catch(e){}
29352                 }, 50);
29353             }
29354             // disabling
29355             if(t < min) {
29356                 cell.className = " x-date-disabled";
29357                 cell.title = cal.minText;
29358                 return;
29359             }
29360             if(t > max) {
29361                 cell.className = " x-date-disabled";
29362                 cell.title = cal.maxText;
29363                 return;
29364             }
29365             if(ddays){
29366                 if(ddays.indexOf(d.getDay()) != -1){
29367                     cell.title = ddaysText;
29368                     cell.className = " x-date-disabled";
29369                 }
29370             }
29371             if(ddMatch && format){
29372                 var fvalue = d.dateFormat(format);
29373                 if(ddMatch.test(fvalue)){
29374                     cell.title = ddText.replace("%0", fvalue);
29375                     cell.className = " x-date-disabled";
29376                 }
29377             }
29378         };
29379
29380         var i = 0;
29381         for(; i < startingPos; i++) {
29382             textEls[i].innerHTML = (++prevStart);
29383             d.setDate(d.getDate()+1);
29384             cells[i].className = "x-date-prevday";
29385             setCellClass(this, cells[i]);
29386         }
29387         for(; i < days; i++){
29388             intDay = i - startingPos + 1;
29389             textEls[i].innerHTML = (intDay);
29390             d.setDate(d.getDate()+1);
29391             cells[i].className = "x-date-active";
29392             setCellClass(this, cells[i]);
29393         }
29394         var extraDays = 0;
29395         for(; i < 42; i++) {
29396              textEls[i].innerHTML = (++extraDays);
29397              d.setDate(d.getDate()+1);
29398              cells[i].className = "x-date-nextday";
29399              setCellClass(this, cells[i]);
29400         }
29401
29402         this.mbtn.setText(this.monthNames[date.getMonth()] + " " + date.getFullYear());
29403         this.fireEvent('monthchange', this, date);
29404         
29405         if(!this.internalRender){
29406             var main = this.el.dom.firstChild;
29407             var w = main.offsetWidth;
29408             this.el.setWidth(w + this.el.getBorderWidth("lr"));
29409             Roo.fly(main).setWidth(w);
29410             this.internalRender = true;
29411             // opera does not respect the auto grow header center column
29412             // then, after it gets a width opera refuses to recalculate
29413             // without a second pass
29414             if(Roo.isOpera && !this.secondPass){
29415                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
29416                 this.secondPass = true;
29417                 this.update.defer(10, this, [date]);
29418             }
29419         }
29420         
29421         
29422     }
29423 });        /*
29424  * Based on:
29425  * Ext JS Library 1.1.1
29426  * Copyright(c) 2006-2007, Ext JS, LLC.
29427  *
29428  * Originally Released Under LGPL - original licence link has changed is not relivant.
29429  *
29430  * Fork - LGPL
29431  * <script type="text/javascript">
29432  */
29433 /**
29434  * @class Roo.TabPanel
29435  * @extends Roo.util.Observable
29436  * A lightweight tab container.
29437  * <br><br>
29438  * Usage:
29439  * <pre><code>
29440 // basic tabs 1, built from existing content
29441 var tabs = new Roo.TabPanel("tabs1");
29442 tabs.addTab("script", "View Script");
29443 tabs.addTab("markup", "View Markup");
29444 tabs.activate("script");
29445
29446 // more advanced tabs, built from javascript
29447 var jtabs = new Roo.TabPanel("jtabs");
29448 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
29449
29450 // set up the UpdateManager
29451 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
29452 var updater = tab2.getUpdateManager();
29453 updater.setDefaultUrl("ajax1.htm");
29454 tab2.on('activate', updater.refresh, updater, true);
29455
29456 // Use setUrl for Ajax loading
29457 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
29458 tab3.setUrl("ajax2.htm", null, true);
29459
29460 // Disabled tab
29461 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
29462 tab4.disable();
29463
29464 jtabs.activate("jtabs-1");
29465  * </code></pre>
29466  * @constructor
29467  * Create a new TabPanel.
29468  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
29469  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
29470  */
29471 Roo.TabPanel = function(container, config){
29472     /**
29473     * The container element for this TabPanel.
29474     * @type Roo.Element
29475     */
29476     this.el = Roo.get(container, true);
29477     if(config){
29478         if(typeof config == "boolean"){
29479             this.tabPosition = config ? "bottom" : "top";
29480         }else{
29481             Roo.apply(this, config);
29482         }
29483     }
29484     if(this.tabPosition == "bottom"){
29485         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29486         this.el.addClass("x-tabs-bottom");
29487     }
29488     this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
29489     this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
29490     this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
29491     if(Roo.isIE){
29492         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
29493     }
29494     if(this.tabPosition != "bottom"){
29495         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
29496          * @type Roo.Element
29497          */
29498         this.bodyEl = Roo.get(this.createBody(this.el.dom));
29499         this.el.addClass("x-tabs-top");
29500     }
29501     this.items = [];
29502
29503     this.bodyEl.setStyle("position", "relative");
29504
29505     this.active = null;
29506     this.activateDelegate = this.activate.createDelegate(this);
29507
29508     this.addEvents({
29509         /**
29510          * @event tabchange
29511          * Fires when the active tab changes
29512          * @param {Roo.TabPanel} this
29513          * @param {Roo.TabPanelItem} activePanel The new active tab
29514          */
29515         "tabchange": true,
29516         /**
29517          * @event beforetabchange
29518          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
29519          * @param {Roo.TabPanel} this
29520          * @param {Object} e Set cancel to true on this object to cancel the tab change
29521          * @param {Roo.TabPanelItem} tab The tab being changed to
29522          */
29523         "beforetabchange" : true
29524     });
29525
29526     Roo.EventManager.onWindowResize(this.onResize, this);
29527     this.cpad = this.el.getPadding("lr");
29528     this.hiddenCount = 0;
29529
29530
29531     // toolbar on the tabbar support...
29532     if (this.toolbar) {
29533         var tcfg = this.toolbar;
29534         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
29535         this.toolbar = new Roo.Toolbar(tcfg);
29536         if (Roo.isSafari) {
29537             var tbl = tcfg.container.child('table', true);
29538             tbl.setAttribute('width', '100%');
29539         }
29540         
29541     }
29542    
29543
29544
29545     Roo.TabPanel.superclass.constructor.call(this);
29546 };
29547
29548 Roo.extend(Roo.TabPanel, Roo.util.Observable, {
29549     /*
29550      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
29551      */
29552     tabPosition : "top",
29553     /*
29554      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
29555      */
29556     currentTabWidth : 0,
29557     /*
29558      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
29559      */
29560     minTabWidth : 40,
29561     /*
29562      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
29563      */
29564     maxTabWidth : 250,
29565     /*
29566      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
29567      */
29568     preferredTabWidth : 175,
29569     /*
29570      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
29571      */
29572     resizeTabs : false,
29573     /*
29574      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
29575      */
29576     monitorResize : true,
29577     /*
29578      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
29579      */
29580     toolbar : false,
29581
29582     /**
29583      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
29584      * @param {String} id The id of the div to use <b>or create</b>
29585      * @param {String} text The text for the tab
29586      * @param {String} content (optional) Content to put in the TabPanelItem body
29587      * @param {Boolean} closable (optional) True to create a close icon on the tab
29588      * @return {Roo.TabPanelItem} The created TabPanelItem
29589      */
29590     addTab : function(id, text, content, closable){
29591         var item = new Roo.TabPanelItem(this, id, text, closable);
29592         this.addTabItem(item);
29593         if(content){
29594             item.setContent(content);
29595         }
29596         return item;
29597     },
29598
29599     /**
29600      * Returns the {@link Roo.TabPanelItem} with the specified id/index
29601      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
29602      * @return {Roo.TabPanelItem}
29603      */
29604     getTab : function(id){
29605         return this.items[id];
29606     },
29607
29608     /**
29609      * Hides the {@link Roo.TabPanelItem} with the specified id/index
29610      * @param {String/Number} id The id or index of the TabPanelItem to hide.
29611      */
29612     hideTab : function(id){
29613         var t = this.items[id];
29614         if(!t.isHidden()){
29615            t.setHidden(true);
29616            this.hiddenCount++;
29617            this.autoSizeTabs();
29618         }
29619     },
29620
29621     /**
29622      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
29623      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
29624      */
29625     unhideTab : function(id){
29626         var t = this.items[id];
29627         if(t.isHidden()){
29628            t.setHidden(false);
29629            this.hiddenCount--;
29630            this.autoSizeTabs();
29631         }
29632     },
29633
29634     /**
29635      * Adds an existing {@link Roo.TabPanelItem}.
29636      * @param {Roo.TabPanelItem} item The TabPanelItem to add
29637      */
29638     addTabItem : function(item){
29639         this.items[item.id] = item;
29640         this.items.push(item);
29641         if(this.resizeTabs){
29642            item.setWidth(this.currentTabWidth || this.preferredTabWidth);
29643            this.autoSizeTabs();
29644         }else{
29645             item.autoSize();
29646         }
29647     },
29648
29649     /**
29650      * Removes a {@link Roo.TabPanelItem}.
29651      * @param {String/Number} id The id or index of the TabPanelItem to remove.
29652      */
29653     removeTab : function(id){
29654         var items = this.items;
29655         var tab = items[id];
29656         if(!tab) { return; }
29657         var index = items.indexOf(tab);
29658         if(this.active == tab && items.length > 1){
29659             var newTab = this.getNextAvailable(index);
29660             if(newTab) {
29661                 newTab.activate();
29662             }
29663         }
29664         this.stripEl.dom.removeChild(tab.pnode.dom);
29665         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
29666             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
29667         }
29668         items.splice(index, 1);
29669         delete this.items[tab.id];
29670         tab.fireEvent("close", tab);
29671         tab.purgeListeners();
29672         this.autoSizeTabs();
29673     },
29674
29675     getNextAvailable : function(start){
29676         var items = this.items;
29677         var index = start;
29678         // look for a next tab that will slide over to
29679         // replace the one being removed
29680         while(index < items.length){
29681             var item = items[++index];
29682             if(item && !item.isHidden()){
29683                 return item;
29684             }
29685         }
29686         // if one isn't found select the previous tab (on the left)
29687         index = start;
29688         while(index >= 0){
29689             var item = items[--index];
29690             if(item && !item.isHidden()){
29691                 return item;
29692             }
29693         }
29694         return null;
29695     },
29696
29697     /**
29698      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
29699      * @param {String/Number} id The id or index of the TabPanelItem to disable.
29700      */
29701     disableTab : function(id){
29702         var tab = this.items[id];
29703         if(tab && this.active != tab){
29704             tab.disable();
29705         }
29706     },
29707
29708     /**
29709      * Enables a {@link Roo.TabPanelItem} that is disabled.
29710      * @param {String/Number} id The id or index of the TabPanelItem to enable.
29711      */
29712     enableTab : function(id){
29713         var tab = this.items[id];
29714         tab.enable();
29715     },
29716
29717     /**
29718      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
29719      * @param {String/Number} id The id or index of the TabPanelItem to activate.
29720      * @return {Roo.TabPanelItem} The TabPanelItem.
29721      */
29722     activate : function(id){
29723         var tab = this.items[id];
29724         if(!tab){
29725             return null;
29726         }
29727         if(tab == this.active || tab.disabled){
29728             return tab;
29729         }
29730         var e = {};
29731         this.fireEvent("beforetabchange", this, e, tab);
29732         if(e.cancel !== true && !tab.disabled){
29733             if(this.active){
29734                 this.active.hide();
29735             }
29736             this.active = this.items[id];
29737             this.active.show();
29738             this.fireEvent("tabchange", this, this.active);
29739         }
29740         return tab;
29741     },
29742
29743     /**
29744      * Gets the active {@link Roo.TabPanelItem}.
29745      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
29746      */
29747     getActiveTab : function(){
29748         return this.active;
29749     },
29750
29751     /**
29752      * Updates the tab body element to fit the height of the container element
29753      * for overflow scrolling
29754      * @param {Number} targetHeight (optional) Override the starting height from the elements height
29755      */
29756     syncHeight : function(targetHeight){
29757         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
29758         var bm = this.bodyEl.getMargins();
29759         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
29760         this.bodyEl.setHeight(newHeight);
29761         return newHeight;
29762     },
29763
29764     onResize : function(){
29765         if(this.monitorResize){
29766             this.autoSizeTabs();
29767         }
29768     },
29769
29770     /**
29771      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
29772      */
29773     beginUpdate : function(){
29774         this.updating = true;
29775     },
29776
29777     /**
29778      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
29779      */
29780     endUpdate : function(){
29781         this.updating = false;
29782         this.autoSizeTabs();
29783     },
29784
29785     /**
29786      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
29787      */
29788     autoSizeTabs : function(){
29789         var count = this.items.length;
29790         var vcount = count - this.hiddenCount;
29791         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
29792             return;
29793         }
29794         var w = Math.max(this.el.getWidth() - this.cpad, 10);
29795         var availWidth = Math.floor(w / vcount);
29796         var b = this.stripBody;
29797         if(b.getWidth() > w){
29798             var tabs = this.items;
29799             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
29800             if(availWidth < this.minTabWidth){
29801                 /*if(!this.sleft){    // incomplete scrolling code
29802                     this.createScrollButtons();
29803                 }
29804                 this.showScroll();
29805                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
29806             }
29807         }else{
29808             if(this.currentTabWidth < this.preferredTabWidth){
29809                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
29810             }
29811         }
29812     },
29813
29814     /**
29815      * Returns the number of tabs in this TabPanel.
29816      * @return {Number}
29817      */
29818      getCount : function(){
29819          return this.items.length;
29820      },
29821
29822     /**
29823      * Resizes all the tabs to the passed width
29824      * @param {Number} The new width
29825      */
29826     setTabWidth : function(width){
29827         this.currentTabWidth = width;
29828         for(var i = 0, len = this.items.length; i < len; i++) {
29829                 if(!this.items[i].isHidden()) {
29830                 this.items[i].setWidth(width);
29831             }
29832         }
29833     },
29834
29835     /**
29836      * Destroys this TabPanel
29837      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
29838      */
29839     destroy : function(removeEl){
29840         Roo.EventManager.removeResizeListener(this.onResize, this);
29841         for(var i = 0, len = this.items.length; i < len; i++){
29842             this.items[i].purgeListeners();
29843         }
29844         if(removeEl === true){
29845             this.el.update("");
29846             this.el.remove();
29847         }
29848     }
29849 });
29850
29851 /**
29852  * @class Roo.TabPanelItem
29853  * @extends Roo.util.Observable
29854  * Represents an individual item (tab plus body) in a TabPanel.
29855  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
29856  * @param {String} id The id of this TabPanelItem
29857  * @param {String} text The text for the tab of this TabPanelItem
29858  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
29859  */
29860 Roo.TabPanelItem = function(tabPanel, id, text, closable){
29861     /**
29862      * The {@link Roo.TabPanel} this TabPanelItem belongs to
29863      * @type Roo.TabPanel
29864      */
29865     this.tabPanel = tabPanel;
29866     /**
29867      * The id for this TabPanelItem
29868      * @type String
29869      */
29870     this.id = id;
29871     /** @private */
29872     this.disabled = false;
29873     /** @private */
29874     this.text = text;
29875     /** @private */
29876     this.loaded = false;
29877     this.closable = closable;
29878
29879     /**
29880      * The body element for this TabPanelItem.
29881      * @type Roo.Element
29882      */
29883     this.bodyEl = Roo.get(tabPanel.createItemBody(tabPanel.bodyEl.dom, id));
29884     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
29885     this.bodyEl.setStyle("display", "block");
29886     this.bodyEl.setStyle("zoom", "1");
29887     this.hideAction();
29888
29889     var els = tabPanel.createStripElements(tabPanel.stripEl.dom, text, closable);
29890     /** @private */
29891     this.el = Roo.get(els.el, true);
29892     this.inner = Roo.get(els.inner, true);
29893     this.textEl = Roo.get(this.el.dom.firstChild.firstChild.firstChild, true);
29894     this.pnode = Roo.get(els.el.parentNode, true);
29895     this.el.on("mousedown", this.onTabMouseDown, this);
29896     this.el.on("click", this.onTabClick, this);
29897     /** @private */
29898     if(closable){
29899         var c = Roo.get(els.close, true);
29900         c.dom.title = this.closeText;
29901         c.addClassOnOver("close-over");
29902         c.on("click", this.closeClick, this);
29903      }
29904
29905     this.addEvents({
29906          /**
29907          * @event activate
29908          * Fires when this tab becomes the active tab.
29909          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29910          * @param {Roo.TabPanelItem} this
29911          */
29912         "activate": true,
29913         /**
29914          * @event beforeclose
29915          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
29916          * @param {Roo.TabPanelItem} this
29917          * @param {Object} e Set cancel to true on this object to cancel the close.
29918          */
29919         "beforeclose": true,
29920         /**
29921          * @event close
29922          * Fires when this tab is closed.
29923          * @param {Roo.TabPanelItem} this
29924          */
29925          "close": true,
29926         /**
29927          * @event deactivate
29928          * Fires when this tab is no longer the active tab.
29929          * @param {Roo.TabPanel} tabPanel The parent TabPanel
29930          * @param {Roo.TabPanelItem} this
29931          */
29932          "deactivate" : true
29933     });
29934     this.hidden = false;
29935
29936     Roo.TabPanelItem.superclass.constructor.call(this);
29937 };
29938
29939 Roo.extend(Roo.TabPanelItem, Roo.util.Observable, {
29940     purgeListeners : function(){
29941        Roo.util.Observable.prototype.purgeListeners.call(this);
29942        this.el.removeAllListeners();
29943     },
29944     /**
29945      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
29946      */
29947     show : function(){
29948         this.pnode.addClass("on");
29949         this.showAction();
29950         if(Roo.isOpera){
29951             this.tabPanel.stripWrap.repaint();
29952         }
29953         this.fireEvent("activate", this.tabPanel, this);
29954     },
29955
29956     /**
29957      * Returns true if this tab is the active tab.
29958      * @return {Boolean}
29959      */
29960     isActive : function(){
29961         return this.tabPanel.getActiveTab() == this;
29962     },
29963
29964     /**
29965      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
29966      */
29967     hide : function(){
29968         this.pnode.removeClass("on");
29969         this.hideAction();
29970         this.fireEvent("deactivate", this.tabPanel, this);
29971     },
29972
29973     hideAction : function(){
29974         this.bodyEl.hide();
29975         this.bodyEl.setStyle("position", "absolute");
29976         this.bodyEl.setLeft("-20000px");
29977         this.bodyEl.setTop("-20000px");
29978     },
29979
29980     showAction : function(){
29981         this.bodyEl.setStyle("position", "relative");
29982         this.bodyEl.setTop("");
29983         this.bodyEl.setLeft("");
29984         this.bodyEl.show();
29985     },
29986
29987     /**
29988      * Set the tooltip for the tab.
29989      * @param {String} tooltip The tab's tooltip
29990      */
29991     setTooltip : function(text){
29992         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
29993             this.textEl.dom.qtip = text;
29994             this.textEl.dom.removeAttribute('title');
29995         }else{
29996             this.textEl.dom.title = text;
29997         }
29998     },
29999
30000     onTabClick : function(e){
30001         e.preventDefault();
30002         this.tabPanel.activate(this.id);
30003     },
30004
30005     onTabMouseDown : function(e){
30006         e.preventDefault();
30007         this.tabPanel.activate(this.id);
30008     },
30009
30010     getWidth : function(){
30011         return this.inner.getWidth();
30012     },
30013
30014     setWidth : function(width){
30015         var iwidth = width - this.pnode.getPadding("lr");
30016         this.inner.setWidth(iwidth);
30017         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
30018         this.pnode.setWidth(width);
30019     },
30020
30021     /**
30022      * Show or hide the tab
30023      * @param {Boolean} hidden True to hide or false to show.
30024      */
30025     setHidden : function(hidden){
30026         this.hidden = hidden;
30027         this.pnode.setStyle("display", hidden ? "none" : "");
30028     },
30029
30030     /**
30031      * Returns true if this tab is "hidden"
30032      * @return {Boolean}
30033      */
30034     isHidden : function(){
30035         return this.hidden;
30036     },
30037
30038     /**
30039      * Returns the text for this tab
30040      * @return {String}
30041      */
30042     getText : function(){
30043         return this.text;
30044     },
30045
30046     autoSize : function(){
30047         //this.el.beginMeasure();
30048         this.textEl.setWidth(1);
30049         /*
30050          *  #2804 [new] Tabs in Roojs
30051          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
30052          */
30053         this.setWidth(this.textEl.dom.scrollWidth+this.pnode.getPadding("lr")+this.inner.getPadding("lr") + 2);
30054         //this.el.endMeasure();
30055     },
30056
30057     /**
30058      * Sets the text for the tab (Note: this also sets the tooltip text)
30059      * @param {String} text The tab's text and tooltip
30060      */
30061     setText : function(text){
30062         this.text = text;
30063         this.textEl.update(text);
30064         this.setTooltip(text);
30065         if(!this.tabPanel.resizeTabs){
30066             this.autoSize();
30067         }
30068     },
30069     /**
30070      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
30071      */
30072     activate : function(){
30073         this.tabPanel.activate(this.id);
30074     },
30075
30076     /**
30077      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
30078      */
30079     disable : function(){
30080         if(this.tabPanel.active != this){
30081             this.disabled = true;
30082             this.pnode.addClass("disabled");
30083         }
30084     },
30085
30086     /**
30087      * Enables this TabPanelItem if it was previously disabled.
30088      */
30089     enable : function(){
30090         this.disabled = false;
30091         this.pnode.removeClass("disabled");
30092     },
30093
30094     /**
30095      * Sets the content for this TabPanelItem.
30096      * @param {String} content The content
30097      * @param {Boolean} loadScripts true to look for and load scripts
30098      */
30099     setContent : function(content, loadScripts){
30100         this.bodyEl.update(content, loadScripts);
30101     },
30102
30103     /**
30104      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
30105      * @return {Roo.UpdateManager} The UpdateManager
30106      */
30107     getUpdateManager : function(){
30108         return this.bodyEl.getUpdateManager();
30109     },
30110
30111     /**
30112      * Set a URL to be used to load the content for this TabPanelItem.
30113      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
30114      * @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)
30115      * @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)
30116      * @return {Roo.UpdateManager} The UpdateManager
30117      */
30118     setUrl : function(url, params, loadOnce){
30119         if(this.refreshDelegate){
30120             this.un('activate', this.refreshDelegate);
30121         }
30122         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
30123         this.on("activate", this.refreshDelegate);
30124         return this.bodyEl.getUpdateManager();
30125     },
30126
30127     /** @private */
30128     _handleRefresh : function(url, params, loadOnce){
30129         if(!loadOnce || !this.loaded){
30130             var updater = this.bodyEl.getUpdateManager();
30131             updater.update(url, params, this._setLoaded.createDelegate(this));
30132         }
30133     },
30134
30135     /**
30136      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
30137      *   Will fail silently if the setUrl method has not been called.
30138      *   This does not activate the panel, just updates its content.
30139      */
30140     refresh : function(){
30141         if(this.refreshDelegate){
30142            this.loaded = false;
30143            this.refreshDelegate();
30144         }
30145     },
30146
30147     /** @private */
30148     _setLoaded : function(){
30149         this.loaded = true;
30150     },
30151
30152     /** @private */
30153     closeClick : function(e){
30154         var o = {};
30155         e.stopEvent();
30156         this.fireEvent("beforeclose", this, o);
30157         if(o.cancel !== true){
30158             this.tabPanel.removeTab(this.id);
30159         }
30160     },
30161     /**
30162      * The text displayed in the tooltip for the close icon.
30163      * @type String
30164      */
30165     closeText : "Close this tab"
30166 });
30167
30168 /** @private */
30169 Roo.TabPanel.prototype.createStrip = function(container){
30170     var strip = document.createElement("div");
30171     strip.className = "x-tabs-wrap";
30172     container.appendChild(strip);
30173     return strip;
30174 };
30175 /** @private */
30176 Roo.TabPanel.prototype.createStripList = function(strip){
30177     // div wrapper for retard IE
30178     // returns the "tr" element.
30179     strip.innerHTML = '<div class="x-tabs-strip-wrap">'+
30180         '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
30181         '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
30182     return strip.firstChild.firstChild.firstChild.firstChild;
30183 };
30184 /** @private */
30185 Roo.TabPanel.prototype.createBody = function(container){
30186     var body = document.createElement("div");
30187     Roo.id(body, "tab-body");
30188     Roo.fly(body).addClass("x-tabs-body");
30189     container.appendChild(body);
30190     return body;
30191 };
30192 /** @private */
30193 Roo.TabPanel.prototype.createItemBody = function(bodyEl, id){
30194     var body = Roo.getDom(id);
30195     if(!body){
30196         body = document.createElement("div");
30197         body.id = id;
30198     }
30199     Roo.fly(body).addClass("x-tabs-item-body");
30200     bodyEl.insertBefore(body, bodyEl.firstChild);
30201     return body;
30202 };
30203 /** @private */
30204 Roo.TabPanel.prototype.createStripElements = function(stripEl, text, closable){
30205     var td = document.createElement("td");
30206     stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
30207     //stripEl.appendChild(td);
30208     if(closable){
30209         td.className = "x-tabs-closable";
30210         if(!this.closeTpl){
30211             this.closeTpl = new Roo.Template(
30212                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30213                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
30214                '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
30215             );
30216         }
30217         var el = this.closeTpl.overwrite(td, {"text": text});
30218         var close = el.getElementsByTagName("div")[0];
30219         var inner = el.getElementsByTagName("em")[0];
30220         return {"el": el, "close": close, "inner": inner};
30221     } else {
30222         if(!this.tabTpl){
30223             this.tabTpl = new Roo.Template(
30224                '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
30225                '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
30226             );
30227         }
30228         var el = this.tabTpl.overwrite(td, {"text": text});
30229         var inner = el.getElementsByTagName("em")[0];
30230         return {"el": el, "inner": inner};
30231     }
30232 };/*
30233  * Based on:
30234  * Ext JS Library 1.1.1
30235  * Copyright(c) 2006-2007, Ext JS, LLC.
30236  *
30237  * Originally Released Under LGPL - original licence link has changed is not relivant.
30238  *
30239  * Fork - LGPL
30240  * <script type="text/javascript">
30241  */
30242
30243 /**
30244  * @class Roo.Button
30245  * @extends Roo.util.Observable
30246  * Simple Button class
30247  * @cfg {String} text The button text
30248  * @cfg {String} icon The path to an image to display in the button (the image will be set as the background-image
30249  * CSS property of the button by default, so if you want a mixed icon/text button, set cls:"x-btn-text-icon")
30250  * @cfg {Function} handler A function called when the button is clicked (can be used instead of click event)
30251  * @cfg {Object} scope The scope of the handler
30252  * @cfg {Number} minWidth The minimum width for this button (used to give a set of buttons a common width)
30253  * @cfg {String/Object} tooltip The tooltip for the button - can be a string or QuickTips config object
30254  * @cfg {Boolean} hidden True to start hidden (defaults to false)
30255  * @cfg {Boolean} disabled True to start disabled (defaults to false)
30256  * @cfg {Boolean} pressed True to start pressed (only if enableToggle = true)
30257  * @cfg {String} toggleGroup The group this toggle button is a member of (only 1 per group can be pressed, only
30258    applies if enableToggle = true)
30259  * @cfg {String/HTMLElement/Element} renderTo The element to append the button to
30260  * @cfg {Boolean/Object} repeat True to repeat fire the click event while the mouse is down. This can also be
30261   an {@link Roo.util.ClickRepeater} config object (defaults to false).
30262  * @constructor
30263  * Create a new button
30264  * @param {Object} config The config object
30265  */
30266 Roo.Button = function(renderTo, config)
30267 {
30268     if (!config) {
30269         config = renderTo;
30270         renderTo = config.renderTo || false;
30271     }
30272     
30273     Roo.apply(this, config);
30274     this.addEvents({
30275         /**
30276              * @event click
30277              * Fires when this button is clicked
30278              * @param {Button} this
30279              * @param {EventObject} e The click event
30280              */
30281             "click" : true,
30282         /**
30283              * @event toggle
30284              * Fires when the "pressed" state of this button changes (only if enableToggle = true)
30285              * @param {Button} this
30286              * @param {Boolean} pressed
30287              */
30288             "toggle" : true,
30289         /**
30290              * @event mouseover
30291              * Fires when the mouse hovers over the button
30292              * @param {Button} this
30293              * @param {Event} e The event object
30294              */
30295         'mouseover' : true,
30296         /**
30297              * @event mouseout
30298              * Fires when the mouse exits the button
30299              * @param {Button} this
30300              * @param {Event} e The event object
30301              */
30302         'mouseout': true,
30303          /**
30304              * @event render
30305              * Fires when the button is rendered
30306              * @param {Button} this
30307              */
30308         'render': true
30309     });
30310     if(this.menu){
30311         this.menu = Roo.menu.MenuMgr.get(this.menu);
30312     }
30313     // register listeners first!!  - so render can be captured..
30314     Roo.util.Observable.call(this);
30315     if(renderTo){
30316         this.render(renderTo);
30317     }
30318     
30319   
30320 };
30321
30322 Roo.extend(Roo.Button, Roo.util.Observable, {
30323     /**
30324      * 
30325      */
30326     
30327     /**
30328      * Read-only. True if this button is hidden
30329      * @type Boolean
30330      */
30331     hidden : false,
30332     /**
30333      * Read-only. True if this button is disabled
30334      * @type Boolean
30335      */
30336     disabled : false,
30337     /**
30338      * Read-only. True if this button is pressed (only if enableToggle = true)
30339      * @type Boolean
30340      */
30341     pressed : false,
30342
30343     /**
30344      * @cfg {Number} tabIndex 
30345      * The DOM tabIndex for this button (defaults to undefined)
30346      */
30347     tabIndex : undefined,
30348
30349     /**
30350      * @cfg {Boolean} enableToggle
30351      * True to enable pressed/not pressed toggling (defaults to false)
30352      */
30353     enableToggle: false,
30354     /**
30355      * @cfg {Roo.menu.Menu} menu
30356      * Standard menu attribute consisting of a reference to a menu object, a menu id or a menu config blob (defaults to undefined).
30357      */
30358     menu : undefined,
30359     /**
30360      * @cfg {String} menuAlign
30361      * The position to align the menu to (see {@link Roo.Element#alignTo} for more details, defaults to 'tl-bl?').
30362      */
30363     menuAlign : "tl-bl?",
30364
30365     /**
30366      * @cfg {String} iconCls
30367      * A css class which sets a background image to be used as the icon for this button (defaults to undefined).
30368      */
30369     iconCls : undefined,
30370     /**
30371      * @cfg {String} type
30372      * The button's type, corresponding to the DOM input element type attribute.  Either "submit," "reset" or "button" (default).
30373      */
30374     type : 'button',
30375
30376     // private
30377     menuClassTarget: 'tr',
30378
30379     /**
30380      * @cfg {String} clickEvent
30381      * The type of event to map to the button's event handler (defaults to 'click')
30382      */
30383     clickEvent : 'click',
30384
30385     /**
30386      * @cfg {Boolean} handleMouseEvents
30387      * False to disable visual cues on mouseover, mouseout and mousedown (defaults to true)
30388      */
30389     handleMouseEvents : true,
30390
30391     /**
30392      * @cfg {String} tooltipType
30393      * The type of tooltip to use. Either "qtip" (default) for QuickTips or "title" for title attribute.
30394      */
30395     tooltipType : 'qtip',
30396
30397     /**
30398      * @cfg {String} cls
30399      * A CSS class to apply to the button's main element.
30400      */
30401     
30402     /**
30403      * @cfg {Roo.Template} template (Optional)
30404      * An {@link Roo.Template} with which to create the Button's main element. This Template must
30405      * contain numeric substitution parameter 0 if it is to display the tRoo property. Changing the template could
30406      * require code modifications if required elements (e.g. a button) aren't present.
30407      */
30408
30409     // private
30410     render : function(renderTo){
30411         var btn;
30412         if(this.hideParent){
30413             this.parentEl = Roo.get(renderTo);
30414         }
30415         if(!this.dhconfig){
30416             if(!this.template){
30417                 if(!Roo.Button.buttonTemplate){
30418                     // hideous table template
30419                     Roo.Button.buttonTemplate = new Roo.Template(
30420                         '<table border="0" cellpadding="0" cellspacing="0" class="x-btn-wrap"><tbody><tr>',
30421                         '<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>',
30422                         "</tr></tbody></table>");
30423                 }
30424                 this.template = Roo.Button.buttonTemplate;
30425             }
30426             btn = this.template.append(renderTo, [this.text || '&#160;', this.type], true);
30427             var btnEl = btn.child("button:first");
30428             btnEl.on('focus', this.onFocus, this);
30429             btnEl.on('blur', this.onBlur, this);
30430             if(this.cls){
30431                 btn.addClass(this.cls);
30432             }
30433             if(this.icon){
30434                 btnEl.setStyle('background-image', 'url(' +this.icon +')');
30435             }
30436             if(this.iconCls){
30437                 btnEl.addClass(this.iconCls);
30438                 if(!this.cls){
30439                     btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30440                 }
30441             }
30442             if(this.tabIndex !== undefined){
30443                 btnEl.dom.tabIndex = this.tabIndex;
30444             }
30445             if(this.tooltip){
30446                 if(typeof this.tooltip == 'object'){
30447                     Roo.QuickTips.tips(Roo.apply({
30448                           target: btnEl.id
30449                     }, this.tooltip));
30450                 } else {
30451                     btnEl.dom[this.tooltipType] = this.tooltip;
30452                 }
30453             }
30454         }else{
30455             btn = Roo.DomHelper.append(Roo.get(renderTo).dom, this.dhconfig, true);
30456         }
30457         this.el = btn;
30458         if(this.id){
30459             this.el.dom.id = this.el.id = this.id;
30460         }
30461         if(this.menu){
30462             this.el.child(this.menuClassTarget).addClass("x-btn-with-menu");
30463             this.menu.on("show", this.onMenuShow, this);
30464             this.menu.on("hide", this.onMenuHide, this);
30465         }
30466         btn.addClass("x-btn");
30467         if(Roo.isIE && !Roo.isIE7){
30468             this.autoWidth.defer(1, this);
30469         }else{
30470             this.autoWidth();
30471         }
30472         if(this.handleMouseEvents){
30473             btn.on("mouseover", this.onMouseOver, this);
30474             btn.on("mouseout", this.onMouseOut, this);
30475             btn.on("mousedown", this.onMouseDown, this);
30476         }
30477         btn.on(this.clickEvent, this.onClick, this);
30478         //btn.on("mouseup", this.onMouseUp, this);
30479         if(this.hidden){
30480             this.hide();
30481         }
30482         if(this.disabled){
30483             this.disable();
30484         }
30485         Roo.ButtonToggleMgr.register(this);
30486         if(this.pressed){
30487             this.el.addClass("x-btn-pressed");
30488         }
30489         if(this.repeat){
30490             var repeater = new Roo.util.ClickRepeater(btn,
30491                 typeof this.repeat == "object" ? this.repeat : {}
30492             );
30493             repeater.on("click", this.onClick,  this);
30494         }
30495         
30496         this.fireEvent('render', this);
30497         
30498     },
30499     /**
30500      * Returns the button's underlying element
30501      * @return {Roo.Element} The element
30502      */
30503     getEl : function(){
30504         return this.el;  
30505     },
30506     
30507     /**
30508      * Destroys this Button and removes any listeners.
30509      */
30510     destroy : function(){
30511         Roo.ButtonToggleMgr.unregister(this);
30512         this.el.removeAllListeners();
30513         this.purgeListeners();
30514         this.el.remove();
30515     },
30516
30517     // private
30518     autoWidth : function(){
30519         if(this.el){
30520             this.el.setWidth("auto");
30521             if(Roo.isIE7 && Roo.isStrict){
30522                 var ib = this.el.child('button');
30523                 if(ib && ib.getWidth() > 20){
30524                     ib.clip();
30525                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30526                 }
30527             }
30528             if(this.minWidth){
30529                 if(this.hidden){
30530                     this.el.beginMeasure();
30531                 }
30532                 if(this.el.getWidth() < this.minWidth){
30533                     this.el.setWidth(this.minWidth);
30534                 }
30535                 if(this.hidden){
30536                     this.el.endMeasure();
30537                 }
30538             }
30539         }
30540     },
30541
30542     /**
30543      * Assigns this button's click handler
30544      * @param {Function} handler The function to call when the button is clicked
30545      * @param {Object} scope (optional) Scope for the function passed in
30546      */
30547     setHandler : function(handler, scope){
30548         this.handler = handler;
30549         this.scope = scope;  
30550     },
30551     
30552     /**
30553      * Sets this button's text
30554      * @param {String} text The button text
30555      */
30556     setText : function(text){
30557         this.text = text;
30558         if(this.el){
30559             this.el.child("td.x-btn-center button.x-btn-text").update(text);
30560         }
30561         this.autoWidth();
30562     },
30563     
30564     /**
30565      * Gets the text for this button
30566      * @return {String} The button text
30567      */
30568     getText : function(){
30569         return this.text;  
30570     },
30571     
30572     /**
30573      * Show this button
30574      */
30575     show: function(){
30576         this.hidden = false;
30577         if(this.el){
30578             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "");
30579         }
30580     },
30581     
30582     /**
30583      * Hide this button
30584      */
30585     hide: function(){
30586         this.hidden = true;
30587         if(this.el){
30588             this[this.hideParent? 'parentEl' : 'el'].setStyle("display", "none");
30589         }
30590     },
30591     
30592     /**
30593      * Convenience function for boolean show/hide
30594      * @param {Boolean} visible True to show, false to hide
30595      */
30596     setVisible: function(visible){
30597         if(visible) {
30598             this.show();
30599         }else{
30600             this.hide();
30601         }
30602     },
30603     
30604     /**
30605      * If a state it passed, it becomes the pressed state otherwise the current state is toggled.
30606      * @param {Boolean} state (optional) Force a particular state
30607      */
30608     toggle : function(state){
30609         state = state === undefined ? !this.pressed : state;
30610         if(state != this.pressed){
30611             if(state){
30612                 this.el.addClass("x-btn-pressed");
30613                 this.pressed = true;
30614                 this.fireEvent("toggle", this, true);
30615             }else{
30616                 this.el.removeClass("x-btn-pressed");
30617                 this.pressed = false;
30618                 this.fireEvent("toggle", this, false);
30619             }
30620             if(this.toggleHandler){
30621                 this.toggleHandler.call(this.scope || this, this, state);
30622             }
30623         }
30624     },
30625     
30626     /**
30627      * Focus the button
30628      */
30629     focus : function(){
30630         this.el.child('button:first').focus();
30631     },
30632     
30633     /**
30634      * Disable this button
30635      */
30636     disable : function(){
30637         if(this.el){
30638             this.el.addClass("x-btn-disabled");
30639         }
30640         this.disabled = true;
30641     },
30642     
30643     /**
30644      * Enable this button
30645      */
30646     enable : function(){
30647         if(this.el){
30648             this.el.removeClass("x-btn-disabled");
30649         }
30650         this.disabled = false;
30651     },
30652
30653     /**
30654      * Convenience function for boolean enable/disable
30655      * @param {Boolean} enabled True to enable, false to disable
30656      */
30657     setDisabled : function(v){
30658         this[v !== true ? "enable" : "disable"]();
30659     },
30660
30661     // private
30662     onClick : function(e)
30663     {
30664         if(e){
30665             e.preventDefault();
30666         }
30667         if(e.button != 0){
30668             return;
30669         }
30670         if(!this.disabled){
30671             if(this.enableToggle){
30672                 this.toggle();
30673             }
30674             if(this.menu && !this.menu.isVisible()){
30675                 this.menu.show(this.el, this.menuAlign);
30676             }
30677             this.fireEvent("click", this, e);
30678             if(this.handler){
30679                 this.el.removeClass("x-btn-over");
30680                 this.handler.call(this.scope || this, this, e);
30681             }
30682         }
30683     },
30684     // private
30685     onMouseOver : function(e){
30686         if(!this.disabled){
30687             this.el.addClass("x-btn-over");
30688             this.fireEvent('mouseover', this, e);
30689         }
30690     },
30691     // private
30692     onMouseOut : function(e){
30693         if(!e.within(this.el,  true)){
30694             this.el.removeClass("x-btn-over");
30695             this.fireEvent('mouseout', this, e);
30696         }
30697     },
30698     // private
30699     onFocus : function(e){
30700         if(!this.disabled){
30701             this.el.addClass("x-btn-focus");
30702         }
30703     },
30704     // private
30705     onBlur : function(e){
30706         this.el.removeClass("x-btn-focus");
30707     },
30708     // private
30709     onMouseDown : function(e){
30710         if(!this.disabled && e.button == 0){
30711             this.el.addClass("x-btn-click");
30712             Roo.get(document).on('mouseup', this.onMouseUp, this);
30713         }
30714     },
30715     // private
30716     onMouseUp : function(e){
30717         if(e.button == 0){
30718             this.el.removeClass("x-btn-click");
30719             Roo.get(document).un('mouseup', this.onMouseUp, this);
30720         }
30721     },
30722     // private
30723     onMenuShow : function(e){
30724         this.el.addClass("x-btn-menu-active");
30725     },
30726     // private
30727     onMenuHide : function(e){
30728         this.el.removeClass("x-btn-menu-active");
30729     }   
30730 });
30731
30732 // Private utility class used by Button
30733 Roo.ButtonToggleMgr = function(){
30734    var groups = {};
30735    
30736    function toggleGroup(btn, state){
30737        if(state){
30738            var g = groups[btn.toggleGroup];
30739            for(var i = 0, l = g.length; i < l; i++){
30740                if(g[i] != btn){
30741                    g[i].toggle(false);
30742                }
30743            }
30744        }
30745    }
30746    
30747    return {
30748        register : function(btn){
30749            if(!btn.toggleGroup){
30750                return;
30751            }
30752            var g = groups[btn.toggleGroup];
30753            if(!g){
30754                g = groups[btn.toggleGroup] = [];
30755            }
30756            g.push(btn);
30757            btn.on("toggle", toggleGroup);
30758        },
30759        
30760        unregister : function(btn){
30761            if(!btn.toggleGroup){
30762                return;
30763            }
30764            var g = groups[btn.toggleGroup];
30765            if(g){
30766                g.remove(btn);
30767                btn.un("toggle", toggleGroup);
30768            }
30769        }
30770    };
30771 }();/*
30772  * Based on:
30773  * Ext JS Library 1.1.1
30774  * Copyright(c) 2006-2007, Ext JS, LLC.
30775  *
30776  * Originally Released Under LGPL - original licence link has changed is not relivant.
30777  *
30778  * Fork - LGPL
30779  * <script type="text/javascript">
30780  */
30781  
30782 /**
30783  * @class Roo.SplitButton
30784  * @extends Roo.Button
30785  * A split button that provides a built-in dropdown arrow that can fire an event separately from the default
30786  * click event of the button.  Typically this would be used to display a dropdown menu that provides additional
30787  * options to the primary button action, but any custom handler can provide the arrowclick implementation.
30788  * @cfg {Function} arrowHandler A function called when the arrow button is clicked (can be used instead of click event)
30789  * @cfg {String} arrowTooltip The title attribute of the arrow
30790  * @constructor
30791  * Create a new menu button
30792  * @param {String/HTMLElement/Element} renderTo The element to append the button to
30793  * @param {Object} config The config object
30794  */
30795 Roo.SplitButton = function(renderTo, config){
30796     Roo.SplitButton.superclass.constructor.call(this, renderTo, config);
30797     /**
30798      * @event arrowclick
30799      * Fires when this button's arrow is clicked
30800      * @param {SplitButton} this
30801      * @param {EventObject} e The click event
30802      */
30803     this.addEvents({"arrowclick":true});
30804 };
30805
30806 Roo.extend(Roo.SplitButton, Roo.Button, {
30807     render : function(renderTo){
30808         // this is one sweet looking template!
30809         var tpl = new Roo.Template(
30810             '<table cellspacing="0" class="x-btn-menu-wrap x-btn"><tr><td>',
30811             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-text-wrap"><tbody>',
30812             '<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>',
30813             "</tbody></table></td><td>",
30814             '<table cellspacing="0" class="x-btn-wrap x-btn-menu-arrow-wrap"><tbody>',
30815             '<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>',
30816             "</tbody></table></td></tr></table>"
30817         );
30818         var btn = tpl.append(renderTo, [this.text, this.type], true);
30819         var btnEl = btn.child("button");
30820         if(this.cls){
30821             btn.addClass(this.cls);
30822         }
30823         if(this.icon){
30824             btnEl.setStyle('background-image', 'url(' +this.icon +')');
30825         }
30826         if(this.iconCls){
30827             btnEl.addClass(this.iconCls);
30828             if(!this.cls){
30829                 btn.addClass(this.text ? 'x-btn-text-icon' : 'x-btn-icon');
30830             }
30831         }
30832         this.el = btn;
30833         if(this.handleMouseEvents){
30834             btn.on("mouseover", this.onMouseOver, this);
30835             btn.on("mouseout", this.onMouseOut, this);
30836             btn.on("mousedown", this.onMouseDown, this);
30837             btn.on("mouseup", this.onMouseUp, this);
30838         }
30839         btn.on(this.clickEvent, this.onClick, this);
30840         if(this.tooltip){
30841             if(typeof this.tooltip == 'object'){
30842                 Roo.QuickTips.tips(Roo.apply({
30843                       target: btnEl.id
30844                 }, this.tooltip));
30845             } else {
30846                 btnEl.dom[this.tooltipType] = this.tooltip;
30847             }
30848         }
30849         if(this.arrowTooltip){
30850             btn.child("button:nth(2)").dom[this.tooltipType] = this.arrowTooltip;
30851         }
30852         if(this.hidden){
30853             this.hide();
30854         }
30855         if(this.disabled){
30856             this.disable();
30857         }
30858         if(this.pressed){
30859             this.el.addClass("x-btn-pressed");
30860         }
30861         if(Roo.isIE && !Roo.isIE7){
30862             this.autoWidth.defer(1, this);
30863         }else{
30864             this.autoWidth();
30865         }
30866         if(this.menu){
30867             this.menu.on("show", this.onMenuShow, this);
30868             this.menu.on("hide", this.onMenuHide, this);
30869         }
30870         this.fireEvent('render', this);
30871     },
30872
30873     // private
30874     autoWidth : function(){
30875         if(this.el){
30876             var tbl = this.el.child("table:first");
30877             var tbl2 = this.el.child("table:last");
30878             this.el.setWidth("auto");
30879             tbl.setWidth("auto");
30880             if(Roo.isIE7 && Roo.isStrict){
30881                 var ib = this.el.child('button:first');
30882                 if(ib && ib.getWidth() > 20){
30883                     ib.clip();
30884                     ib.setWidth(Roo.util.TextMetrics.measure(ib, this.text).width+ib.getFrameWidth('lr'));
30885                 }
30886             }
30887             if(this.minWidth){
30888                 if(this.hidden){
30889                     this.el.beginMeasure();
30890                 }
30891                 if((tbl.getWidth()+tbl2.getWidth()) < this.minWidth){
30892                     tbl.setWidth(this.minWidth-tbl2.getWidth());
30893                 }
30894                 if(this.hidden){
30895                     this.el.endMeasure();
30896                 }
30897             }
30898             this.el.setWidth(tbl.getWidth()+tbl2.getWidth());
30899         } 
30900     },
30901     /**
30902      * Sets this button's click handler
30903      * @param {Function} handler The function to call when the button is clicked
30904      * @param {Object} scope (optional) Scope for the function passed above
30905      */
30906     setHandler : function(handler, scope){
30907         this.handler = handler;
30908         this.scope = scope;  
30909     },
30910     
30911     /**
30912      * Sets this button's arrow click handler
30913      * @param {Function} handler The function to call when the arrow is clicked
30914      * @param {Object} scope (optional) Scope for the function passed above
30915      */
30916     setArrowHandler : function(handler, scope){
30917         this.arrowHandler = handler;
30918         this.scope = scope;  
30919     },
30920     
30921     /**
30922      * Focus the button
30923      */
30924     focus : function(){
30925         if(this.el){
30926             this.el.child("button:first").focus();
30927         }
30928     },
30929
30930     // private
30931     onClick : function(e){
30932         e.preventDefault();
30933         if(!this.disabled){
30934             if(e.getTarget(".x-btn-menu-arrow-wrap")){
30935                 if(this.menu && !this.menu.isVisible()){
30936                     this.menu.show(this.el, this.menuAlign);
30937                 }
30938                 this.fireEvent("arrowclick", this, e);
30939                 if(this.arrowHandler){
30940                     this.arrowHandler.call(this.scope || this, this, e);
30941                 }
30942             }else{
30943                 this.fireEvent("click", this, e);
30944                 if(this.handler){
30945                     this.handler.call(this.scope || this, this, e);
30946                 }
30947             }
30948         }
30949     },
30950     // private
30951     onMouseDown : function(e){
30952         if(!this.disabled){
30953             Roo.fly(e.getTarget("table")).addClass("x-btn-click");
30954         }
30955     },
30956     // private
30957     onMouseUp : function(e){
30958         Roo.fly(e.getTarget("table")).removeClass("x-btn-click");
30959     }   
30960 });
30961
30962
30963 // backwards compat
30964 Roo.MenuButton = Roo.SplitButton;/*
30965  * Based on:
30966  * Ext JS Library 1.1.1
30967  * Copyright(c) 2006-2007, Ext JS, LLC.
30968  *
30969  * Originally Released Under LGPL - original licence link has changed is not relivant.
30970  *
30971  * Fork - LGPL
30972  * <script type="text/javascript">
30973  */
30974
30975 /**
30976  * @class Roo.Toolbar
30977  * @children   Roo.Toolbar.Item Roo.form.Field
30978  * Basic Toolbar class.
30979  * @constructor
30980  * Creates a new Toolbar
30981  * @param {Object} container The config object
30982  */ 
30983 Roo.Toolbar = function(container, buttons, config)
30984 {
30985     /// old consturctor format still supported..
30986     if(container instanceof Array){ // omit the container for later rendering
30987         buttons = container;
30988         config = buttons;
30989         container = null;
30990     }
30991     if (typeof(container) == 'object' && container.xtype) {
30992         config = container;
30993         container = config.container;
30994         buttons = config.buttons || []; // not really - use items!!
30995     }
30996     var xitems = [];
30997     if (config && config.items) {
30998         xitems = config.items;
30999         delete config.items;
31000     }
31001     Roo.apply(this, config);
31002     this.buttons = buttons;
31003     
31004     if(container){
31005         this.render(container);
31006     }
31007     this.xitems = xitems;
31008     Roo.each(xitems, function(b) {
31009         this.add(b);
31010     }, this);
31011     
31012 };
31013
31014 Roo.Toolbar.prototype = {
31015     /**
31016      * @cfg {Array} items
31017      * array of button configs or elements to add (will be converted to a MixedCollection)
31018      */
31019     items: false,
31020     /**
31021      * @cfg {String/HTMLElement/Element} container
31022      * The id or element that will contain the toolbar
31023      */
31024     // private
31025     render : function(ct){
31026         this.el = Roo.get(ct);
31027         if(this.cls){
31028             this.el.addClass(this.cls);
31029         }
31030         // using a table allows for vertical alignment
31031         // 100% width is needed by Safari...
31032         this.el.update('<div class="x-toolbar x-small-editor"><table cellspacing="0"><tr></tr></table></div>');
31033         this.tr = this.el.child("tr", true);
31034         var autoId = 0;
31035         this.items = new Roo.util.MixedCollection(false, function(o){
31036             return o.id || ("item" + (++autoId));
31037         });
31038         if(this.buttons){
31039             this.add.apply(this, this.buttons);
31040             delete this.buttons;
31041         }
31042     },
31043
31044     /**
31045      * Adds element(s) to the toolbar -- this function takes a variable number of 
31046      * arguments of mixed type and adds them to the toolbar.
31047      * @param {Mixed} arg1 The following types of arguments are all valid:<br />
31048      * <ul>
31049      * <li>{@link Roo.Toolbar.Button} config: A valid button config object (equivalent to {@link #addButton})</li>
31050      * <li>HtmlElement: Any standard HTML element (equivalent to {@link #addElement})</li>
31051      * <li>Field: Any form field (equivalent to {@link #addField})</li>
31052      * <li>Item: Any subclass of {@link Roo.Toolbar.Item} (equivalent to {@link #addItem})</li>
31053      * <li>String: Any generic string (gets wrapped in a {@link Roo.Toolbar.TextItem}, equivalent to {@link #addText}).
31054      * Note that there are a few special strings that are treated differently as explained nRoo.</li>
31055      * <li>'separator' or '-': Creates a separator element (equivalent to {@link #addSeparator})</li>
31056      * <li>' ': Creates a spacer element (equivalent to {@link #addSpacer})</li>
31057      * <li>'->': Creates a fill element (equivalent to {@link #addFill})</li>
31058      * </ul>
31059      * @param {Mixed} arg2
31060      * @param {Mixed} etc.
31061      */
31062     add : function(){
31063         var a = arguments, l = a.length;
31064         for(var i = 0; i < l; i++){
31065             this._add(a[i]);
31066         }
31067     },
31068     // private..
31069     _add : function(el) {
31070         
31071         if (el.xtype) {
31072             el = Roo.factory(el, typeof(Roo.Toolbar[el.xtype]) == 'undefined' ? Roo.form : Roo.Toolbar);
31073         }
31074         
31075         if (el.applyTo){ // some kind of form field
31076             return this.addField(el);
31077         } 
31078         if (el.render){ // some kind of Toolbar.Item
31079             return this.addItem(el);
31080         }
31081         if (typeof el == "string"){ // string
31082             if(el == "separator" || el == "-"){
31083                 return this.addSeparator();
31084             }
31085             if (el == " "){
31086                 return this.addSpacer();
31087             }
31088             if(el == "->"){
31089                 return this.addFill();
31090             }
31091             return this.addText(el);
31092             
31093         }
31094         if(el.tagName){ // element
31095             return this.addElement(el);
31096         }
31097         if(typeof el == "object"){ // must be button config?
31098             return this.addButton(el);
31099         }
31100         // and now what?!?!
31101         return false;
31102         
31103     },
31104     
31105     /**
31106      * Add an Xtype element
31107      * @param {Object} xtype Xtype Object
31108      * @return {Object} created Object
31109      */
31110     addxtype : function(e){
31111         return this.add(e);  
31112     },
31113     
31114     /**
31115      * Returns the Element for this toolbar.
31116      * @return {Roo.Element}
31117      */
31118     getEl : function(){
31119         return this.el;  
31120     },
31121     
31122     /**
31123      * Adds a separator
31124      * @return {Roo.Toolbar.Item} The separator item
31125      */
31126     addSeparator : function(){
31127         return this.addItem(new Roo.Toolbar.Separator());
31128     },
31129
31130     /**
31131      * Adds a spacer element
31132      * @return {Roo.Toolbar.Spacer} The spacer item
31133      */
31134     addSpacer : function(){
31135         return this.addItem(new Roo.Toolbar.Spacer());
31136     },
31137
31138     /**
31139      * Adds a fill element that forces subsequent additions to the right side of the toolbar
31140      * @return {Roo.Toolbar.Fill} The fill item
31141      */
31142     addFill : function(){
31143         return this.addItem(new Roo.Toolbar.Fill());
31144     },
31145
31146     /**
31147      * Adds any standard HTML element to the toolbar
31148      * @param {String/HTMLElement/Element} el The element or id of the element to add
31149      * @return {Roo.Toolbar.Item} The element's item
31150      */
31151     addElement : function(el){
31152         return this.addItem(new Roo.Toolbar.Item(el));
31153     },
31154     /**
31155      * Collection of items on the toolbar.. (only Toolbar Items, so use fields to retrieve fields)
31156      * @type Roo.util.MixedCollection  
31157      */
31158     items : false,
31159      
31160     /**
31161      * Adds any Toolbar.Item or subclass
31162      * @param {Roo.Toolbar.Item} item
31163      * @return {Roo.Toolbar.Item} The item
31164      */
31165     addItem : function(item){
31166         var td = this.nextBlock();
31167         item.render(td);
31168         this.items.add(item);
31169         return item;
31170     },
31171     
31172     /**
31173      * Adds a button (or buttons). See {@link Roo.Toolbar.Button} for more info on the config.
31174      * @param {Object/Array} config A button config or array of configs
31175      * @return {Roo.Toolbar.Button/Array}
31176      */
31177     addButton : function(config){
31178         if(config instanceof Array){
31179             var buttons = [];
31180             for(var i = 0, len = config.length; i < len; i++) {
31181                 buttons.push(this.addButton(config[i]));
31182             }
31183             return buttons;
31184         }
31185         var b = config;
31186         if(!(config instanceof Roo.Toolbar.Button)){
31187             b = config.split ?
31188                 new Roo.Toolbar.SplitButton(config) :
31189                 new Roo.Toolbar.Button(config);
31190         }
31191         var td = this.nextBlock();
31192         b.render(td);
31193         this.items.add(b);
31194         return b;
31195     },
31196     
31197     /**
31198      * Adds text to the toolbar
31199      * @param {String} text The text to add
31200      * @return {Roo.Toolbar.Item} The element's item
31201      */
31202     addText : function(text){
31203         return this.addItem(new Roo.Toolbar.TextItem(text));
31204     },
31205     
31206     /**
31207      * Inserts any {@link Roo.Toolbar.Item}/{@link Roo.Toolbar.Button} at the specified index.
31208      * @param {Number} index The index where the item is to be inserted
31209      * @param {Object/Roo.Toolbar.Item/Roo.Toolbar.Button (may be Array)} item The button, or button config object to be inserted.
31210      * @return {Roo.Toolbar.Button/Item}
31211      */
31212     insertButton : function(index, item){
31213         if(item instanceof Array){
31214             var buttons = [];
31215             for(var i = 0, len = item.length; i < len; i++) {
31216                buttons.push(this.insertButton(index + i, item[i]));
31217             }
31218             return buttons;
31219         }
31220         if (!(item instanceof Roo.Toolbar.Button)){
31221            item = new Roo.Toolbar.Button(item);
31222         }
31223         var td = document.createElement("td");
31224         this.tr.insertBefore(td, this.tr.childNodes[index]);
31225         item.render(td);
31226         this.items.insert(index, item);
31227         return item;
31228     },
31229     
31230     /**
31231      * Adds a new element to the toolbar from the passed {@link Roo.DomHelper} config.
31232      * @param {Object} config
31233      * @return {Roo.Toolbar.Item} The element's item
31234      */
31235     addDom : function(config, returnEl){
31236         var td = this.nextBlock();
31237         Roo.DomHelper.overwrite(td, config);
31238         var ti = new Roo.Toolbar.Item(td.firstChild);
31239         ti.render(td);
31240         this.items.add(ti);
31241         return ti;
31242     },
31243
31244     /**
31245      * Collection of fields on the toolbar.. usefull for quering (value is false if there are no fields)
31246      * @type Roo.util.MixedCollection  
31247      */
31248     fields : false,
31249     
31250     /**
31251      * Adds a dynamically rendered Roo.form field (TextField, ComboBox, etc).
31252      * Note: the field should not have been rendered yet. For a field that has already been
31253      * rendered, use {@link #addElement}.
31254      * @param {Roo.form.Field} field
31255      * @return {Roo.ToolbarItem}
31256      */
31257      
31258       
31259     addField : function(field) {
31260         if (!this.fields) {
31261             var autoId = 0;
31262             this.fields = new Roo.util.MixedCollection(false, function(o){
31263                 return o.id || ("item" + (++autoId));
31264             });
31265
31266         }
31267         
31268         var td = this.nextBlock();
31269         field.render(td);
31270         var ti = new Roo.Toolbar.Item(td.firstChild);
31271         ti.render(td);
31272         this.items.add(ti);
31273         this.fields.add(field);
31274         return ti;
31275     },
31276     /**
31277      * Hide the toolbar
31278      * @method hide
31279      */
31280      
31281       
31282     hide : function()
31283     {
31284         this.el.child('div').setVisibilityMode(Roo.Element.DISPLAY);
31285         this.el.child('div').hide();
31286     },
31287     /**
31288      * Show the toolbar
31289      * @method show
31290      */
31291     show : function()
31292     {
31293         this.el.child('div').show();
31294     },
31295       
31296     // private
31297     nextBlock : function(){
31298         var td = document.createElement("td");
31299         this.tr.appendChild(td);
31300         return td;
31301     },
31302
31303     // private
31304     destroy : function(){
31305         if(this.items){ // rendered?
31306             Roo.destroy.apply(Roo, this.items.items);
31307         }
31308         if(this.fields){ // rendered?
31309             Roo.destroy.apply(Roo, this.fields.items);
31310         }
31311         Roo.Element.uncache(this.el, this.tr);
31312     }
31313 };
31314
31315 /**
31316  * @class Roo.Toolbar.Item
31317  * The base class that other classes should extend in order to get some basic common toolbar item functionality.
31318  * @constructor
31319  * Creates a new Item
31320  * @param {HTMLElement} el 
31321  */
31322 Roo.Toolbar.Item = function(el){
31323     var cfg = {};
31324     if (typeof (el.xtype) != 'undefined') {
31325         cfg = el;
31326         el = cfg.el;
31327     }
31328     
31329     this.el = Roo.getDom(el);
31330     this.id = Roo.id(this.el);
31331     this.hidden = false;
31332     
31333     this.addEvents({
31334          /**
31335              * @event render
31336              * Fires when the button is rendered
31337              * @param {Button} this
31338              */
31339         'render': true
31340     });
31341     Roo.Toolbar.Item.superclass.constructor.call(this,cfg);
31342 };
31343 Roo.extend(Roo.Toolbar.Item, Roo.util.Observable, {
31344 //Roo.Toolbar.Item.prototype = {
31345     
31346     /**
31347      * Get this item's HTML Element
31348      * @return {HTMLElement}
31349      */
31350     getEl : function(){
31351        return this.el;  
31352     },
31353
31354     // private
31355     render : function(td){
31356         
31357          this.td = td;
31358         td.appendChild(this.el);
31359         
31360         this.fireEvent('render', this);
31361     },
31362     
31363     /**
31364      * Removes and destroys this item.
31365      */
31366     destroy : function(){
31367         this.td.parentNode.removeChild(this.td);
31368     },
31369     
31370     /**
31371      * Shows this item.
31372      */
31373     show: function(){
31374         this.hidden = false;
31375         this.td.style.display = "";
31376     },
31377     
31378     /**
31379      * Hides this item.
31380      */
31381     hide: function(){
31382         this.hidden = true;
31383         this.td.style.display = "none";
31384     },
31385     
31386     /**
31387      * Convenience function for boolean show/hide.
31388      * @param {Boolean} visible true to show/false to hide
31389      */
31390     setVisible: function(visible){
31391         if(visible) {
31392             this.show();
31393         }else{
31394             this.hide();
31395         }
31396     },
31397     
31398     /**
31399      * Try to focus this item.
31400      */
31401     focus : function(){
31402         Roo.fly(this.el).focus();
31403     },
31404     
31405     /**
31406      * Disables this item.
31407      */
31408     disable : function(){
31409         Roo.fly(this.td).addClass("x-item-disabled");
31410         this.disabled = true;
31411         this.el.disabled = true;
31412     },
31413     
31414     /**
31415      * Enables this item.
31416      */
31417     enable : function(){
31418         Roo.fly(this.td).removeClass("x-item-disabled");
31419         this.disabled = false;
31420         this.el.disabled = false;
31421     }
31422 });
31423
31424
31425 /**
31426  * @class Roo.Toolbar.Separator
31427  * @extends Roo.Toolbar.Item
31428  * A simple toolbar separator class
31429  * @constructor
31430  * Creates a new Separator
31431  */
31432 Roo.Toolbar.Separator = function(cfg){
31433     
31434     var s = document.createElement("span");
31435     s.className = "ytb-sep";
31436     if (cfg) {
31437         cfg.el = s;
31438     }
31439     
31440     Roo.Toolbar.Separator.superclass.constructor.call(this, cfg || s);
31441 };
31442 Roo.extend(Roo.Toolbar.Separator, Roo.Toolbar.Item, {
31443     enable:Roo.emptyFn,
31444     disable:Roo.emptyFn,
31445     focus:Roo.emptyFn
31446 });
31447
31448 /**
31449  * @class Roo.Toolbar.Spacer
31450  * @extends Roo.Toolbar.Item
31451  * A simple element that adds extra horizontal space to a toolbar.
31452  * @constructor
31453  * Creates a new Spacer
31454  */
31455 Roo.Toolbar.Spacer = function(cfg){
31456     var s = document.createElement("div");
31457     s.className = "ytb-spacer";
31458     if (cfg) {
31459         cfg.el = s;
31460     }
31461     Roo.Toolbar.Spacer.superclass.constructor.call(this, cfg || s);
31462 };
31463 Roo.extend(Roo.Toolbar.Spacer, Roo.Toolbar.Item, {
31464     enable:Roo.emptyFn,
31465     disable:Roo.emptyFn,
31466     focus:Roo.emptyFn
31467 });
31468
31469 /**
31470  * @class Roo.Toolbar.Fill
31471  * @extends Roo.Toolbar.Spacer
31472  * A simple element that adds a greedy (100% width) horizontal space to a toolbar.
31473  * @constructor
31474  * Creates a new Spacer
31475  */
31476 Roo.Toolbar.Fill = Roo.extend(Roo.Toolbar.Spacer, {
31477     // private
31478     render : function(td){
31479         td.style.width = '100%';
31480         Roo.Toolbar.Fill.superclass.render.call(this, td);
31481     }
31482 });
31483
31484 /**
31485  * @class Roo.Toolbar.TextItem
31486  * @extends Roo.Toolbar.Item
31487  * A simple class that renders text directly into a toolbar.
31488  * @constructor
31489  * Creates a new TextItem
31490  * @cfg {string} text 
31491  */
31492 Roo.Toolbar.TextItem = function(cfg){
31493     var  text = cfg || "";
31494     if (typeof(cfg) == 'object') {
31495         text = cfg.text || "";
31496     }  else {
31497         cfg = null;
31498     }
31499     var s = document.createElement("span");
31500     s.className = "ytb-text";
31501     s.innerHTML = text;
31502     if (cfg) {
31503         cfg.el  = s;
31504     }
31505     
31506     Roo.Toolbar.TextItem.superclass.constructor.call(this, cfg ||  s);
31507 };
31508 Roo.extend(Roo.Toolbar.TextItem, Roo.Toolbar.Item, {
31509     
31510      
31511     enable:Roo.emptyFn,
31512     disable:Roo.emptyFn,
31513     focus:Roo.emptyFn
31514 });
31515
31516 /**
31517  * @class Roo.Toolbar.Button
31518  * @extends Roo.Button
31519  * A button that renders into a toolbar.
31520  * @constructor
31521  * Creates a new Button
31522  * @param {Object} config A standard {@link Roo.Button} config object
31523  */
31524 Roo.Toolbar.Button = function(config){
31525     Roo.Toolbar.Button.superclass.constructor.call(this, null, config);
31526 };
31527 Roo.extend(Roo.Toolbar.Button, Roo.Button,
31528 {
31529     
31530     
31531     render : function(td){
31532         this.td = td;
31533         Roo.Toolbar.Button.superclass.render.call(this, td);
31534     },
31535     
31536     /**
31537      * Removes and destroys this button
31538      */
31539     destroy : function(){
31540         Roo.Toolbar.Button.superclass.destroy.call(this);
31541         this.td.parentNode.removeChild(this.td);
31542     },
31543     
31544     /**
31545      * Shows this button
31546      */
31547     show: function(){
31548         this.hidden = false;
31549         this.td.style.display = "";
31550     },
31551     
31552     /**
31553      * Hides this button
31554      */
31555     hide: function(){
31556         this.hidden = true;
31557         this.td.style.display = "none";
31558     },
31559
31560     /**
31561      * Disables this item
31562      */
31563     disable : function(){
31564         Roo.fly(this.td).addClass("x-item-disabled");
31565         this.disabled = true;
31566     },
31567
31568     /**
31569      * Enables this item
31570      */
31571     enable : function(){
31572         Roo.fly(this.td).removeClass("x-item-disabled");
31573         this.disabled = false;
31574     }
31575 });
31576 // backwards compat
31577 Roo.ToolbarButton = Roo.Toolbar.Button;
31578
31579 /**
31580  * @class Roo.Toolbar.SplitButton
31581  * @extends Roo.SplitButton
31582  * A menu button that renders into a toolbar.
31583  * @constructor
31584  * Creates a new SplitButton
31585  * @param {Object} config A standard {@link Roo.SplitButton} config object
31586  */
31587 Roo.Toolbar.SplitButton = function(config){
31588     Roo.Toolbar.SplitButton.superclass.constructor.call(this, null, config);
31589 };
31590 Roo.extend(Roo.Toolbar.SplitButton, Roo.SplitButton, {
31591     render : function(td){
31592         this.td = td;
31593         Roo.Toolbar.SplitButton.superclass.render.call(this, td);
31594     },
31595     
31596     /**
31597      * Removes and destroys this button
31598      */
31599     destroy : function(){
31600         Roo.Toolbar.SplitButton.superclass.destroy.call(this);
31601         this.td.parentNode.removeChild(this.td);
31602     },
31603     
31604     /**
31605      * Shows this button
31606      */
31607     show: function(){
31608         this.hidden = false;
31609         this.td.style.display = "";
31610     },
31611     
31612     /**
31613      * Hides this button
31614      */
31615     hide: function(){
31616         this.hidden = true;
31617         this.td.style.display = "none";
31618     }
31619 });
31620
31621 // backwards compat
31622 Roo.Toolbar.MenuButton = Roo.Toolbar.SplitButton;/*
31623  * Based on:
31624  * Ext JS Library 1.1.1
31625  * Copyright(c) 2006-2007, Ext JS, LLC.
31626  *
31627  * Originally Released Under LGPL - original licence link has changed is not relivant.
31628  *
31629  * Fork - LGPL
31630  * <script type="text/javascript">
31631  */
31632  
31633 /**
31634  * @class Roo.PagingToolbar
31635  * @extends Roo.Toolbar
31636  * @children   Roo.Toolbar.Item Roo.form.Field
31637  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
31638  * @constructor
31639  * Create a new PagingToolbar
31640  * @param {Object} config The config object
31641  */
31642 Roo.PagingToolbar = function(el, ds, config)
31643 {
31644     // old args format still supported... - xtype is prefered..
31645     if (typeof(el) == 'object' && el.xtype) {
31646         // created from xtype...
31647         config = el;
31648         ds = el.dataSource;
31649         el = config.container;
31650     }
31651     var items = [];
31652     if (config.items) {
31653         items = config.items;
31654         config.items = [];
31655     }
31656     
31657     Roo.PagingToolbar.superclass.constructor.call(this, el, null, config);
31658     this.ds = ds;
31659     this.cursor = 0;
31660     this.renderButtons(this.el);
31661     this.bind(ds);
31662     
31663     // supprot items array.
31664    
31665     Roo.each(items, function(e) {
31666         this.add(Roo.factory(e));
31667     },this);
31668     
31669 };
31670
31671 Roo.extend(Roo.PagingToolbar, Roo.Toolbar, {
31672    
31673     /**
31674      * @cfg {String/HTMLElement/Element} container
31675      * container The id or element that will contain the toolbar
31676      */
31677     /**
31678      * @cfg {Boolean} displayInfo
31679      * True to display the displayMsg (defaults to false)
31680      */
31681     
31682     
31683     /**
31684      * @cfg {Number} pageSize
31685      * The number of records to display per page (defaults to 20)
31686      */
31687     pageSize: 20,
31688     /**
31689      * @cfg {String} displayMsg
31690      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
31691      */
31692     displayMsg : 'Displaying {0} - {1} of {2}',
31693     /**
31694      * @cfg {String} emptyMsg
31695      * The message to display when no records are found (defaults to "No data to display")
31696      */
31697     emptyMsg : 'No data to display',
31698     /**
31699      * Customizable piece of the default paging text (defaults to "Page")
31700      * @type String
31701      */
31702     beforePageText : "Page",
31703     /**
31704      * Customizable piece of the default paging text (defaults to "of %0")
31705      * @type String
31706      */
31707     afterPageText : "of {0}",
31708     /**
31709      * Customizable piece of the default paging text (defaults to "First Page")
31710      * @type String
31711      */
31712     firstText : "First Page",
31713     /**
31714      * Customizable piece of the default paging text (defaults to "Previous Page")
31715      * @type String
31716      */
31717     prevText : "Previous Page",
31718     /**
31719      * Customizable piece of the default paging text (defaults to "Next Page")
31720      * @type String
31721      */
31722     nextText : "Next Page",
31723     /**
31724      * Customizable piece of the default paging text (defaults to "Last Page")
31725      * @type String
31726      */
31727     lastText : "Last Page",
31728     /**
31729      * Customizable piece of the default paging text (defaults to "Refresh")
31730      * @type String
31731      */
31732     refreshText : "Refresh",
31733
31734     // private
31735     renderButtons : function(el){
31736         Roo.PagingToolbar.superclass.render.call(this, el);
31737         this.first = this.addButton({
31738             tooltip: this.firstText,
31739             cls: "x-btn-icon x-grid-page-first",
31740             disabled: true,
31741             handler: this.onClick.createDelegate(this, ["first"])
31742         });
31743         this.prev = this.addButton({
31744             tooltip: this.prevText,
31745             cls: "x-btn-icon x-grid-page-prev",
31746             disabled: true,
31747             handler: this.onClick.createDelegate(this, ["prev"])
31748         });
31749         //this.addSeparator();
31750         this.add(this.beforePageText);
31751         this.field = Roo.get(this.addDom({
31752            tag: "input",
31753            type: "text",
31754            size: "3",
31755            value: "1",
31756            cls: "x-grid-page-number"
31757         }).el);
31758         this.field.on("keydown", this.onPagingKeydown, this);
31759         this.field.on("focus", function(){this.dom.select();});
31760         this.afterTextEl = this.addText(String.format(this.afterPageText, 1));
31761         this.field.setHeight(18);
31762         //this.addSeparator();
31763         this.next = this.addButton({
31764             tooltip: this.nextText,
31765             cls: "x-btn-icon x-grid-page-next",
31766             disabled: true,
31767             handler: this.onClick.createDelegate(this, ["next"])
31768         });
31769         this.last = this.addButton({
31770             tooltip: this.lastText,
31771             cls: "x-btn-icon x-grid-page-last",
31772             disabled: true,
31773             handler: this.onClick.createDelegate(this, ["last"])
31774         });
31775         //this.addSeparator();
31776         this.loading = this.addButton({
31777             tooltip: this.refreshText,
31778             cls: "x-btn-icon x-grid-loading",
31779             handler: this.onClick.createDelegate(this, ["refresh"])
31780         });
31781
31782         if(this.displayInfo){
31783             this.displayEl = Roo.fly(this.el.dom.firstChild).createChild({cls:'x-paging-info'});
31784         }
31785     },
31786
31787     // private
31788     updateInfo : function(){
31789         if(this.displayEl){
31790             var count = this.ds.getCount();
31791             var msg = count == 0 ?
31792                 this.emptyMsg :
31793                 String.format(
31794                     this.displayMsg,
31795                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
31796                 );
31797             this.displayEl.update(msg);
31798         }
31799     },
31800
31801     // private
31802     onLoad : function(ds, r, o){
31803        this.cursor = o.params ? o.params.start : 0;
31804        var d = this.getPageData(), ap = d.activePage, ps = d.pages;
31805
31806        this.afterTextEl.el.innerHTML = String.format(this.afterPageText, d.pages);
31807        this.field.dom.value = ap;
31808        this.first.setDisabled(ap == 1);
31809        this.prev.setDisabled(ap == 1);
31810        this.next.setDisabled(ap == ps);
31811        this.last.setDisabled(ap == ps);
31812        this.loading.enable();
31813        this.updateInfo();
31814     },
31815
31816     // private
31817     getPageData : function(){
31818         var total = this.ds.getTotalCount();
31819         return {
31820             total : total,
31821             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
31822             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
31823         };
31824     },
31825
31826     // private
31827     onLoadError : function(){
31828         this.loading.enable();
31829     },
31830
31831     // private
31832     onPagingKeydown : function(e){
31833         var k = e.getKey();
31834         var d = this.getPageData();
31835         if(k == e.RETURN){
31836             var v = this.field.dom.value, pageNum;
31837             if(!v || isNaN(pageNum = parseInt(v, 10))){
31838                 this.field.dom.value = d.activePage;
31839                 return;
31840             }
31841             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
31842             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31843             e.stopEvent();
31844         }
31845         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))
31846         {
31847           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
31848           this.field.dom.value = pageNum;
31849           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
31850           e.stopEvent();
31851         }
31852         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
31853         {
31854           var v = this.field.dom.value, pageNum; 
31855           var increment = (e.shiftKey) ? 10 : 1;
31856           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
31857             increment *= -1;
31858           }
31859           if(!v || isNaN(pageNum = parseInt(v, 10))) {
31860             this.field.dom.value = d.activePage;
31861             return;
31862           }
31863           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
31864           {
31865             this.field.dom.value = parseInt(v, 10) + increment;
31866             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
31867             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
31868           }
31869           e.stopEvent();
31870         }
31871     },
31872
31873     // private
31874     beforeLoad : function(){
31875         if(this.loading){
31876             this.loading.disable();
31877         }
31878     },
31879
31880     // private
31881     onClick : function(which){
31882         var ds = this.ds;
31883         switch(which){
31884             case "first":
31885                 ds.load({params:{start: 0, limit: this.pageSize}});
31886             break;
31887             case "prev":
31888                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
31889             break;
31890             case "next":
31891                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
31892             break;
31893             case "last":
31894                 var total = ds.getTotalCount();
31895                 var extra = total % this.pageSize;
31896                 var lastStart = extra ? (total - extra) : total-this.pageSize;
31897                 ds.load({params:{start: lastStart, limit: this.pageSize}});
31898             break;
31899             case "refresh":
31900                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
31901             break;
31902         }
31903     },
31904
31905     /**
31906      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
31907      * @param {Roo.data.Store} store The data store to unbind
31908      */
31909     unbind : function(ds){
31910         ds.un("beforeload", this.beforeLoad, this);
31911         ds.un("load", this.onLoad, this);
31912         ds.un("loadexception", this.onLoadError, this);
31913         ds.un("remove", this.updateInfo, this);
31914         ds.un("add", this.updateInfo, this);
31915         this.ds = undefined;
31916     },
31917
31918     /**
31919      * Binds the paging toolbar to the specified {@link Roo.data.Store}
31920      * @param {Roo.data.Store} store The data store to bind
31921      */
31922     bind : function(ds){
31923         ds.on("beforeload", this.beforeLoad, this);
31924         ds.on("load", this.onLoad, this);
31925         ds.on("loadexception", this.onLoadError, this);
31926         ds.on("remove", this.updateInfo, this);
31927         ds.on("add", this.updateInfo, this);
31928         this.ds = ds;
31929     }
31930 });/*
31931  * Based on:
31932  * Ext JS Library 1.1.1
31933  * Copyright(c) 2006-2007, Ext JS, LLC.
31934  *
31935  * Originally Released Under LGPL - original licence link has changed is not relivant.
31936  *
31937  * Fork - LGPL
31938  * <script type="text/javascript">
31939  */
31940
31941 /**
31942  * @class Roo.Resizable
31943  * @extends Roo.util.Observable
31944  * <p>Applies drag handles to an element to make it resizable. The drag handles are inserted into the element
31945  * and positioned absolute. Some elements, such as a textarea or image, don't support this. To overcome that, you can wrap
31946  * 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
31947  * the element will be wrapped for you automatically.</p>
31948  * <p>Here is the list of valid resize handles:</p>
31949  * <pre>
31950 Value   Description
31951 ------  -------------------
31952  'n'     north
31953  's'     south
31954  'e'     east
31955  'w'     west
31956  'nw'    northwest
31957  'sw'    southwest
31958  'se'    southeast
31959  'ne'    northeast
31960  'hd'    horizontal drag
31961  'all'   all
31962 </pre>
31963  * <p>Here's an example showing the creation of a typical Resizable:</p>
31964  * <pre><code>
31965 var resizer = new Roo.Resizable("element-id", {
31966     handles: 'all',
31967     minWidth: 200,
31968     minHeight: 100,
31969     maxWidth: 500,
31970     maxHeight: 400,
31971     pinned: true
31972 });
31973 resizer.on("resize", myHandler);
31974 </code></pre>
31975  * <p>To hide a particular handle, set its display to none in CSS, or through script:<br>
31976  * resizer.east.setDisplayed(false);</p>
31977  * @cfg {Boolean/String/Element} resizeChild True to resize the first child, or id/element to resize (defaults to false)
31978  * @cfg {Array/String} adjustments String "auto" or an array [width, height] with values to be <b>added</b> to the
31979  * resize operation's new size (defaults to [0, 0])
31980  * @cfg {Number} minWidth The minimum width for the element (defaults to 5)
31981  * @cfg {Number} minHeight The minimum height for the element (defaults to 5)
31982  * @cfg {Number} maxWidth The maximum width for the element (defaults to 10000)
31983  * @cfg {Number} maxHeight The maximum height for the element (defaults to 10000)
31984  * @cfg {Boolean} enabled False to disable resizing (defaults to true)
31985  * @cfg {Boolean} wrap True to wrap an element with a div if needed (required for textareas and images, defaults to false)
31986  * @cfg {Number} width The width of the element in pixels (defaults to null)
31987  * @cfg {Number} height The height of the element in pixels (defaults to null)
31988  * @cfg {Boolean} animate True to animate the resize (not compatible with dynamic sizing, defaults to false)
31989  * @cfg {Number} duration Animation duration if animate = true (defaults to .35)
31990  * @cfg {Boolean} dynamic True to resize the element while dragging instead of using a proxy (defaults to false)
31991  * @cfg {String} handles String consisting of the resize handles to display (defaults to undefined)
31992  * @cfg {Boolean} multiDirectional <b>Deprecated</b>.  The old style of adding multi-direction resize handles, deprecated
31993  * in favor of the handles config option (defaults to false)
31994  * @cfg {Boolean} disableTrackOver True to disable mouse tracking. This is only applied at config time. (defaults to false)
31995  * @cfg {String} easing Animation easing if animate = true (defaults to 'easingOutStrong')
31996  * @cfg {Number} widthIncrement The increment to snap the width resize in pixels (dynamic must be true, defaults to 0)
31997  * @cfg {Number} heightIncrement The increment to snap the height resize in pixels (dynamic must be true, defaults to 0)
31998  * @cfg {Boolean} pinned True to ensure that the resize handles are always visible, false to display them only when the
31999  * user mouses over the resizable borders. This is only applied at config time. (defaults to false)
32000  * @cfg {Boolean} preserveRatio True to preserve the original ratio between height and width during resize (defaults to false)
32001  * @cfg {Boolean} transparent True for transparent handles. This is only applied at config time. (defaults to false)
32002  * @cfg {Number} minX The minimum allowed page X for the element (only used for west resizing, defaults to 0)
32003  * @cfg {Number} minY The minimum allowed page Y for the element (only used for north resizing, defaults to 0)
32004  * @cfg {Boolean} draggable Convenience to initialize drag drop (defaults to false)
32005  * @constructor
32006  * Create a new resizable component
32007  * @param {String/HTMLElement/Roo.Element} el The id or element to resize
32008  * @param {Object} config configuration options
32009   */
32010 Roo.Resizable = function(el, config)
32011 {
32012     this.el = Roo.get(el);
32013
32014     if(config && config.wrap){
32015         config.resizeChild = this.el;
32016         this.el = this.el.wrap(typeof config.wrap == "object" ? config.wrap : {cls:"xresizable-wrap"});
32017         this.el.id = this.el.dom.id = config.resizeChild.id + "-rzwrap";
32018         this.el.setStyle("overflow", "hidden");
32019         this.el.setPositioning(config.resizeChild.getPositioning());
32020         config.resizeChild.clearPositioning();
32021         if(!config.width || !config.height){
32022             var csize = config.resizeChild.getSize();
32023             this.el.setSize(csize.width, csize.height);
32024         }
32025         if(config.pinned && !config.adjustments){
32026             config.adjustments = "auto";
32027         }
32028     }
32029
32030     this.proxy = this.el.createProxy({tag: "div", cls: "x-resizable-proxy", id: this.el.id + "-rzproxy"});
32031     this.proxy.unselectable();
32032     this.proxy.enableDisplayMode('block');
32033
32034     Roo.apply(this, config);
32035
32036     if(this.pinned){
32037         this.disableTrackOver = true;
32038         this.el.addClass("x-resizable-pinned");
32039     }
32040     // if the element isn't positioned, make it relative
32041     var position = this.el.getStyle("position");
32042     if(position != "absolute" && position != "fixed"){
32043         this.el.setStyle("position", "relative");
32044     }
32045     if(!this.handles){ // no handles passed, must be legacy style
32046         this.handles = 's,e,se';
32047         if(this.multiDirectional){
32048             this.handles += ',n,w';
32049         }
32050     }
32051     if(this.handles == "all"){
32052         this.handles = "n s e w ne nw se sw";
32053     }
32054     var hs = this.handles.split(/\s*?[,;]\s*?| /);
32055     var ps = Roo.Resizable.positions;
32056     for(var i = 0, len = hs.length; i < len; i++){
32057         if(hs[i] && ps[hs[i]]){
32058             var pos = ps[hs[i]];
32059             this[pos] = new Roo.Resizable.Handle(this, pos, this.disableTrackOver, this.transparent);
32060         }
32061     }
32062     // legacy
32063     this.corner = this.southeast;
32064     
32065     // updateBox = the box can move..
32066     if(this.handles.indexOf("n") != -1 || this.handles.indexOf("w") != -1 || this.handles.indexOf("hd") != -1) {
32067         this.updateBox = true;
32068     }
32069
32070     this.activeHandle = null;
32071
32072     if(this.resizeChild){
32073         if(typeof this.resizeChild == "boolean"){
32074             this.resizeChild = Roo.get(this.el.dom.firstChild, true);
32075         }else{
32076             this.resizeChild = Roo.get(this.resizeChild, true);
32077         }
32078     }
32079     
32080     if(this.adjustments == "auto"){
32081         var rc = this.resizeChild;
32082         var hw = this.west, he = this.east, hn = this.north, hs = this.south;
32083         if(rc && (hw || hn)){
32084             rc.position("relative");
32085             rc.setLeft(hw ? hw.el.getWidth() : 0);
32086             rc.setTop(hn ? hn.el.getHeight() : 0);
32087         }
32088         this.adjustments = [
32089             (he ? -he.el.getWidth() : 0) + (hw ? -hw.el.getWidth() : 0),
32090             (hn ? -hn.el.getHeight() : 0) + (hs ? -hs.el.getHeight() : 0) -1
32091         ];
32092     }
32093
32094     if(this.draggable){
32095         this.dd = this.dynamic ?
32096             this.el.initDD(null) : this.el.initDDProxy(null, {dragElId: this.proxy.id});
32097         this.dd.setHandleElId(this.resizeChild ? this.resizeChild.id : this.el.id);
32098     }
32099
32100     // public events
32101     this.addEvents({
32102         /**
32103          * @event beforeresize
32104          * Fired before resize is allowed. Set enabled to false to cancel resize.
32105          * @param {Roo.Resizable} this
32106          * @param {Roo.EventObject} e The mousedown event
32107          */
32108         "beforeresize" : true,
32109         /**
32110          * @event resizing
32111          * Fired a resizing.
32112          * @param {Roo.Resizable} this
32113          * @param {Number} x The new x position
32114          * @param {Number} y The new y position
32115          * @param {Number} w The new w width
32116          * @param {Number} h The new h hight
32117          * @param {Roo.EventObject} e The mouseup event
32118          */
32119         "resizing" : true,
32120         /**
32121          * @event resize
32122          * Fired after a resize.
32123          * @param {Roo.Resizable} this
32124          * @param {Number} width The new width
32125          * @param {Number} height The new height
32126          * @param {Roo.EventObject} e The mouseup event
32127          */
32128         "resize" : true
32129     });
32130
32131     if(this.width !== null && this.height !== null){
32132         this.resizeTo(this.width, this.height);
32133     }else{
32134         this.updateChildSize();
32135     }
32136     if(Roo.isIE){
32137         this.el.dom.style.zoom = 1;
32138     }
32139     Roo.Resizable.superclass.constructor.call(this);
32140 };
32141
32142 Roo.extend(Roo.Resizable, Roo.util.Observable, {
32143         resizeChild : false,
32144         adjustments : [0, 0],
32145         minWidth : 5,
32146         minHeight : 5,
32147         maxWidth : 10000,
32148         maxHeight : 10000,
32149         enabled : true,
32150         animate : false,
32151         duration : .35,
32152         dynamic : false,
32153         handles : false,
32154         multiDirectional : false,
32155         disableTrackOver : false,
32156         easing : 'easeOutStrong',
32157         widthIncrement : 0,
32158         heightIncrement : 0,
32159         pinned : false,
32160         width : null,
32161         height : null,
32162         preserveRatio : false,
32163         transparent: false,
32164         minX: 0,
32165         minY: 0,
32166         draggable: false,
32167
32168         /**
32169          * @cfg {String/HTMLElement/Element} constrainTo Constrain the resize to a particular element
32170          */
32171         constrainTo: undefined,
32172         /**
32173          * @cfg {Roo.lib.Region} resizeRegion Constrain the resize to a particular region
32174          */
32175         resizeRegion: undefined,
32176
32177
32178     /**
32179      * Perform a manual resize
32180      * @param {Number} width
32181      * @param {Number} height
32182      */
32183     resizeTo : function(width, height){
32184         this.el.setSize(width, height);
32185         this.updateChildSize();
32186         this.fireEvent("resize", this, width, height, null);
32187     },
32188
32189     // private
32190     startSizing : function(e, handle){
32191         this.fireEvent("beforeresize", this, e);
32192         if(this.enabled){ // 2nd enabled check in case disabled before beforeresize handler
32193
32194             if(!this.overlay){
32195                 this.overlay = this.el.createProxy({tag: "div", cls: "x-resizable-overlay", html: "&#160;"});
32196                 this.overlay.unselectable();
32197                 this.overlay.enableDisplayMode("block");
32198                 this.overlay.on("mousemove", this.onMouseMove, this);
32199                 this.overlay.on("mouseup", this.onMouseUp, this);
32200             }
32201             this.overlay.setStyle("cursor", handle.el.getStyle("cursor"));
32202
32203             this.resizing = true;
32204             this.startBox = this.el.getBox();
32205             this.startPoint = e.getXY();
32206             this.offsets = [(this.startBox.x + this.startBox.width) - this.startPoint[0],
32207                             (this.startBox.y + this.startBox.height) - this.startPoint[1]];
32208
32209             this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
32210             this.overlay.show();
32211
32212             if(this.constrainTo) {
32213                 var ct = Roo.get(this.constrainTo);
32214                 this.resizeRegion = ct.getRegion().adjust(
32215                     ct.getFrameWidth('t'),
32216                     ct.getFrameWidth('l'),
32217                     -ct.getFrameWidth('b'),
32218                     -ct.getFrameWidth('r')
32219                 );
32220             }
32221
32222             this.proxy.setStyle('visibility', 'hidden'); // workaround display none
32223             this.proxy.show();
32224             this.proxy.setBox(this.startBox);
32225             if(!this.dynamic){
32226                 this.proxy.setStyle('visibility', 'visible');
32227             }
32228         }
32229     },
32230
32231     // private
32232     onMouseDown : function(handle, e){
32233         if(this.enabled){
32234             e.stopEvent();
32235             this.activeHandle = handle;
32236             this.startSizing(e, handle);
32237         }
32238     },
32239
32240     // private
32241     onMouseUp : function(e){
32242         var size = this.resizeElement();
32243         this.resizing = false;
32244         this.handleOut();
32245         this.overlay.hide();
32246         this.proxy.hide();
32247         this.fireEvent("resize", this, size.width, size.height, e);
32248     },
32249
32250     // private
32251     updateChildSize : function(){
32252         
32253         if(this.resizeChild){
32254             var el = this.el;
32255             var child = this.resizeChild;
32256             var adj = this.adjustments;
32257             if(el.dom.offsetWidth){
32258                 var b = el.getSize(true);
32259                 child.setSize(b.width+adj[0], b.height+adj[1]);
32260             }
32261             // Second call here for IE
32262             // The first call enables instant resizing and
32263             // the second call corrects scroll bars if they
32264             // exist
32265             if(Roo.isIE){
32266                 setTimeout(function(){
32267                     if(el.dom.offsetWidth){
32268                         var b = el.getSize(true);
32269                         child.setSize(b.width+adj[0], b.height+adj[1]);
32270                     }
32271                 }, 10);
32272             }
32273         }
32274     },
32275
32276     // private
32277     snap : function(value, inc, min){
32278         if(!inc || !value) {
32279             return value;
32280         }
32281         var newValue = value;
32282         var m = value % inc;
32283         if(m > 0){
32284             if(m > (inc/2)){
32285                 newValue = value + (inc-m);
32286             }else{
32287                 newValue = value - m;
32288             }
32289         }
32290         return Math.max(min, newValue);
32291     },
32292
32293     // private
32294     resizeElement : function(){
32295         var box = this.proxy.getBox();
32296         if(this.updateBox){
32297             this.el.setBox(box, false, this.animate, this.duration, null, this.easing);
32298         }else{
32299             this.el.setSize(box.width, box.height, this.animate, this.duration, null, this.easing);
32300         }
32301         this.updateChildSize();
32302         if(!this.dynamic){
32303             this.proxy.hide();
32304         }
32305         return box;
32306     },
32307
32308     // private
32309     constrain : function(v, diff, m, mx){
32310         if(v - diff < m){
32311             diff = v - m;
32312         }else if(v - diff > mx){
32313             diff = mx - v;
32314         }
32315         return diff;
32316     },
32317
32318     // private
32319     onMouseMove : function(e){
32320         
32321         if(this.enabled){
32322             try{// try catch so if something goes wrong the user doesn't get hung
32323
32324             if(this.resizeRegion && !this.resizeRegion.contains(e.getPoint())) {
32325                 return;
32326             }
32327
32328             //var curXY = this.startPoint;
32329             var curSize = this.curSize || this.startBox;
32330             var x = this.startBox.x, y = this.startBox.y;
32331             var ox = x, oy = y;
32332             var w = curSize.width, h = curSize.height;
32333             var ow = w, oh = h;
32334             var mw = this.minWidth, mh = this.minHeight;
32335             var mxw = this.maxWidth, mxh = this.maxHeight;
32336             var wi = this.widthIncrement;
32337             var hi = this.heightIncrement;
32338
32339             var eventXY = e.getXY();
32340             var diffX = -(this.startPoint[0] - Math.max(this.minX, eventXY[0]));
32341             var diffY = -(this.startPoint[1] - Math.max(this.minY, eventXY[1]));
32342
32343             var pos = this.activeHandle.position;
32344
32345             switch(pos){
32346                 case "east":
32347                     w += diffX;
32348                     w = Math.min(Math.max(mw, w), mxw);
32349                     break;
32350              
32351                 case "south":
32352                     h += diffY;
32353                     h = Math.min(Math.max(mh, h), mxh);
32354                     break;
32355                 case "southeast":
32356                     w += diffX;
32357                     h += diffY;
32358                     w = Math.min(Math.max(mw, w), mxw);
32359                     h = Math.min(Math.max(mh, h), mxh);
32360                     break;
32361                 case "north":
32362                     diffY = this.constrain(h, diffY, mh, mxh);
32363                     y += diffY;
32364                     h -= diffY;
32365                     break;
32366                 case "hdrag":
32367                     
32368                     if (wi) {
32369                         var adiffX = Math.abs(diffX);
32370                         var sub = (adiffX % wi); // how much 
32371                         if (sub > (wi/2)) { // far enough to snap
32372                             diffX = (diffX > 0) ? diffX-sub + wi : diffX+sub - wi;
32373                         } else {
32374                             // remove difference.. 
32375                             diffX = (diffX > 0) ? diffX-sub : diffX+sub;
32376                         }
32377                     }
32378                     x += diffX;
32379                     x = Math.max(this.minX, x);
32380                     break;
32381                 case "west":
32382                     diffX = this.constrain(w, diffX, mw, mxw);
32383                     x += diffX;
32384                     w -= diffX;
32385                     break;
32386                 case "northeast":
32387                     w += diffX;
32388                     w = Math.min(Math.max(mw, w), mxw);
32389                     diffY = this.constrain(h, diffY, mh, mxh);
32390                     y += diffY;
32391                     h -= diffY;
32392                     break;
32393                 case "northwest":
32394                     diffX = this.constrain(w, diffX, mw, mxw);
32395                     diffY = this.constrain(h, diffY, mh, mxh);
32396                     y += diffY;
32397                     h -= diffY;
32398                     x += diffX;
32399                     w -= diffX;
32400                     break;
32401                case "southwest":
32402                     diffX = this.constrain(w, diffX, mw, mxw);
32403                     h += diffY;
32404                     h = Math.min(Math.max(mh, h), mxh);
32405                     x += diffX;
32406                     w -= diffX;
32407                     break;
32408             }
32409
32410             var sw = this.snap(w, wi, mw);
32411             var sh = this.snap(h, hi, mh);
32412             if(sw != w || sh != h){
32413                 switch(pos){
32414                     case "northeast":
32415                         y -= sh - h;
32416                     break;
32417                     case "north":
32418                         y -= sh - h;
32419                         break;
32420                     case "southwest":
32421                         x -= sw - w;
32422                     break;
32423                     case "west":
32424                         x -= sw - w;
32425                         break;
32426                     case "northwest":
32427                         x -= sw - w;
32428                         y -= sh - h;
32429                     break;
32430                 }
32431                 w = sw;
32432                 h = sh;
32433             }
32434
32435             if(this.preserveRatio){
32436                 switch(pos){
32437                     case "southeast":
32438                     case "east":
32439                         h = oh * (w/ow);
32440                         h = Math.min(Math.max(mh, h), mxh);
32441                         w = ow * (h/oh);
32442                        break;
32443                     case "south":
32444                         w = ow * (h/oh);
32445                         w = Math.min(Math.max(mw, w), mxw);
32446                         h = oh * (w/ow);
32447                         break;
32448                     case "northeast":
32449                         w = ow * (h/oh);
32450                         w = Math.min(Math.max(mw, w), mxw);
32451                         h = oh * (w/ow);
32452                     break;
32453                     case "north":
32454                         var tw = w;
32455                         w = ow * (h/oh);
32456                         w = Math.min(Math.max(mw, w), mxw);
32457                         h = oh * (w/ow);
32458                         x += (tw - w) / 2;
32459                         break;
32460                     case "southwest":
32461                         h = oh * (w/ow);
32462                         h = Math.min(Math.max(mh, h), mxh);
32463                         var tw = w;
32464                         w = ow * (h/oh);
32465                         x += tw - w;
32466                         break;
32467                     case "west":
32468                         var th = h;
32469                         h = oh * (w/ow);
32470                         h = Math.min(Math.max(mh, h), mxh);
32471                         y += (th - h) / 2;
32472                         var tw = w;
32473                         w = ow * (h/oh);
32474                         x += tw - w;
32475                        break;
32476                     case "northwest":
32477                         var tw = w;
32478                         var th = h;
32479                         h = oh * (w/ow);
32480                         h = Math.min(Math.max(mh, h), mxh);
32481                         w = ow * (h/oh);
32482                         y += th - h;
32483                         x += tw - w;
32484                        break;
32485
32486                 }
32487             }
32488             if (pos == 'hdrag') {
32489                 w = ow;
32490             }
32491             this.proxy.setBounds(x, y, w, h);
32492             if(this.dynamic){
32493                 this.resizeElement();
32494             }
32495             }catch(e){}
32496         }
32497         this.fireEvent("resizing", this, x, y, w, h, e);
32498     },
32499
32500     // private
32501     handleOver : function(){
32502         if(this.enabled){
32503             this.el.addClass("x-resizable-over");
32504         }
32505     },
32506
32507     // private
32508     handleOut : function(){
32509         if(!this.resizing){
32510             this.el.removeClass("x-resizable-over");
32511         }
32512     },
32513
32514     /**
32515      * Returns the element this component is bound to.
32516      * @return {Roo.Element}
32517      */
32518     getEl : function(){
32519         return this.el;
32520     },
32521
32522     /**
32523      * Returns the resizeChild element (or null).
32524      * @return {Roo.Element}
32525      */
32526     getResizeChild : function(){
32527         return this.resizeChild;
32528     },
32529     groupHandler : function()
32530     {
32531         
32532     },
32533     /**
32534      * Destroys this resizable. If the element was wrapped and
32535      * removeEl is not true then the element remains.
32536      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
32537      */
32538     destroy : function(removeEl){
32539         this.proxy.remove();
32540         if(this.overlay){
32541             this.overlay.removeAllListeners();
32542             this.overlay.remove();
32543         }
32544         var ps = Roo.Resizable.positions;
32545         for(var k in ps){
32546             if(typeof ps[k] != "function" && this[ps[k]]){
32547                 var h = this[ps[k]];
32548                 h.el.removeAllListeners();
32549                 h.el.remove();
32550             }
32551         }
32552         if(removeEl){
32553             this.el.update("");
32554             this.el.remove();
32555         }
32556     }
32557 });
32558
32559 // private
32560 // hash to map config positions to true positions
32561 Roo.Resizable.positions = {
32562     n: "north", s: "south", e: "east", w: "west", se: "southeast", sw: "southwest", nw: "northwest", ne: "northeast", 
32563     hd: "hdrag"
32564 };
32565
32566 // private
32567 Roo.Resizable.Handle = function(rz, pos, disableTrackOver, transparent){
32568     if(!this.tpl){
32569         // only initialize the template if resizable is used
32570         var tpl = Roo.DomHelper.createTemplate(
32571             {tag: "div", cls: "x-resizable-handle x-resizable-handle-{0}"}
32572         );
32573         tpl.compile();
32574         Roo.Resizable.Handle.prototype.tpl = tpl;
32575     }
32576     this.position = pos;
32577     this.rz = rz;
32578     // show north drag fro topdra
32579     var handlepos = pos == 'hdrag' ? 'north' : pos;
32580     
32581     this.el = this.tpl.append(rz.el.dom, [handlepos], true);
32582     if (pos == 'hdrag') {
32583         this.el.setStyle('cursor', 'pointer');
32584     }
32585     this.el.unselectable();
32586     if(transparent){
32587         this.el.setOpacity(0);
32588     }
32589     this.el.on("mousedown", this.onMouseDown, this);
32590     if(!disableTrackOver){
32591         this.el.on("mouseover", this.onMouseOver, this);
32592         this.el.on("mouseout", this.onMouseOut, this);
32593     }
32594 };
32595
32596 // private
32597 Roo.Resizable.Handle.prototype = {
32598     afterResize : function(rz){
32599         Roo.log('after?');
32600         // do nothing
32601     },
32602     // private
32603     onMouseDown : function(e){
32604         this.rz.onMouseDown(this, e);
32605     },
32606     // private
32607     onMouseOver : function(e){
32608         this.rz.handleOver(this, e);
32609     },
32610     // private
32611     onMouseOut : function(e){
32612         this.rz.handleOut(this, e);
32613     }
32614 };/*
32615  * Based on:
32616  * Ext JS Library 1.1.1
32617  * Copyright(c) 2006-2007, Ext JS, LLC.
32618  *
32619  * Originally Released Under LGPL - original licence link has changed is not relivant.
32620  *
32621  * Fork - LGPL
32622  * <script type="text/javascript">
32623  */
32624
32625 /**
32626  * @class Roo.Editor
32627  * @extends Roo.Component
32628  * A base editor field that handles displaying/hiding on demand and has some built-in sizing and event handling logic.
32629  * @constructor
32630  * Create a new Editor
32631  * @param {Roo.form.Field} field The Field object (or descendant)
32632  * @param {Object} config The config object
32633  */
32634 Roo.Editor = function(field, config){
32635     Roo.Editor.superclass.constructor.call(this, config);
32636     this.field = field;
32637     this.addEvents({
32638         /**
32639              * @event beforestartedit
32640              * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
32641              * false from the handler of this event.
32642              * @param {Editor} this
32643              * @param {Roo.Element} boundEl The underlying element bound to this editor
32644              * @param {Mixed} value The field value being set
32645              */
32646         "beforestartedit" : true,
32647         /**
32648              * @event startedit
32649              * Fires when this editor is displayed
32650              * @param {Roo.Element} boundEl The underlying element bound to this editor
32651              * @param {Mixed} value The starting field value
32652              */
32653         "startedit" : true,
32654         /**
32655              * @event beforecomplete
32656              * Fires after a change has been made to the field, but before the change is reflected in the underlying
32657              * field.  Saving the change to the field can be canceled by returning false from the handler of this event.
32658              * Note that if the value has not changed and ignoreNoChange = true, the editing will still end but this
32659              * event will not fire since no edit actually occurred.
32660              * @param {Editor} this
32661              * @param {Mixed} value The current field value
32662              * @param {Mixed} startValue The original field value
32663              */
32664         "beforecomplete" : true,
32665         /**
32666              * @event complete
32667              * Fires after editing is complete and any changed value has been written to the underlying field.
32668              * @param {Editor} this
32669              * @param {Mixed} value The current field value
32670              * @param {Mixed} startValue The original field value
32671              */
32672         "complete" : true,
32673         /**
32674          * @event specialkey
32675          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
32676          * {@link Roo.EventObject#getKey} to determine which key was pressed.
32677          * @param {Roo.form.Field} this
32678          * @param {Roo.EventObject} e The event object
32679          */
32680         "specialkey" : true
32681     });
32682 };
32683
32684 Roo.extend(Roo.Editor, Roo.Component, {
32685     /**
32686      * @cfg {Boolean/String} autosize
32687      * True for the editor to automatically adopt the size of the underlying field, "width" to adopt the width only,
32688      * or "height" to adopt the height only (defaults to false)
32689      */
32690     /**
32691      * @cfg {Boolean} revertInvalid
32692      * True to automatically revert the field value and cancel the edit when the user completes an edit and the field
32693      * validation fails (defaults to true)
32694      */
32695     /**
32696      * @cfg {Boolean} ignoreNoChange
32697      * True to skip the the edit completion process (no save, no events fired) if the user completes an edit and
32698      * the value has not changed (defaults to false).  Applies only to string values - edits for other data types
32699      * will never be ignored.
32700      */
32701     /**
32702      * @cfg {Boolean} hideEl
32703      * False to keep the bound element visible while the editor is displayed (defaults to true)
32704      */
32705     /**
32706      * @cfg {Mixed} value
32707      * The data value of the underlying field (defaults to "")
32708      */
32709     value : "",
32710     /**
32711      * @cfg {String} alignment
32712      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "c-c?").
32713      */
32714     alignment: "c-c?",
32715     /**
32716      * @cfg {Boolean/String} shadow "sides" for sides/bottom only, "frame" for 4-way shadow, and "drop"
32717      * for bottom-right shadow (defaults to "frame")
32718      */
32719     shadow : "frame",
32720     /**
32721      * @cfg {Boolean} constrain True to constrain the editor to the viewport
32722      */
32723     constrain : false,
32724     /**
32725      * @cfg {Boolean} completeOnEnter True to complete the edit when the enter key is pressed (defaults to false)
32726      */
32727     completeOnEnter : false,
32728     /**
32729      * @cfg {Boolean} cancelOnEsc True to cancel the edit when the escape key is pressed (defaults to false)
32730      */
32731     cancelOnEsc : false,
32732     /**
32733      * @cfg {Boolean} updateEl True to update the innerHTML of the bound element when the update completes (defaults to false)
32734      */
32735     updateEl : false,
32736
32737     // private
32738     onRender : function(ct, position){
32739         this.el = new Roo.Layer({
32740             shadow: this.shadow,
32741             cls: "x-editor",
32742             parentEl : ct,
32743             shim : this.shim,
32744             shadowOffset:4,
32745             id: this.id,
32746             constrain: this.constrain
32747         });
32748         this.el.setStyle("overflow", Roo.isGecko ? "auto" : "hidden");
32749         if(this.field.msgTarget != 'title'){
32750             this.field.msgTarget = 'qtip';
32751         }
32752         this.field.render(this.el);
32753         if(Roo.isGecko){
32754             this.field.el.dom.setAttribute('autocomplete', 'off');
32755         }
32756         this.field.on("specialkey", this.onSpecialKey, this);
32757         if(this.swallowKeys){
32758             this.field.el.swallowEvent(['keydown','keypress']);
32759         }
32760         this.field.show();
32761         this.field.on("blur", this.onBlur, this);
32762         if(this.field.grow){
32763             this.field.on("autosize", this.el.sync,  this.el, {delay:1});
32764         }
32765     },
32766
32767     onSpecialKey : function(field, e)
32768     {
32769         //Roo.log('editor onSpecialKey');
32770         if(this.completeOnEnter && e.getKey() == e.ENTER){
32771             e.stopEvent();
32772             this.completeEdit();
32773             return;
32774         }
32775         // do not fire special key otherwise it might hide close the editor...
32776         if(e.getKey() == e.ENTER){    
32777             return;
32778         }
32779         if(this.cancelOnEsc && e.getKey() == e.ESC){
32780             this.cancelEdit();
32781             return;
32782         } 
32783         this.fireEvent('specialkey', field, e);
32784     
32785     },
32786
32787     /**
32788      * Starts the editing process and shows the editor.
32789      * @param {String/HTMLElement/Element} el The element to edit
32790      * @param {String} value (optional) A value to initialize the editor with. If a value is not provided, it defaults
32791       * to the innerHTML of el.
32792      */
32793     startEdit : function(el, value){
32794         if(this.editing){
32795             this.completeEdit();
32796         }
32797         this.boundEl = Roo.get(el);
32798         var v = value !== undefined ? value : this.boundEl.dom.innerHTML;
32799         if(!this.rendered){
32800             this.render(this.parentEl || document.body);
32801         }
32802         if(this.fireEvent("beforestartedit", this, this.boundEl, v) === false){
32803             return;
32804         }
32805         this.startValue = v;
32806         this.field.setValue(v);
32807         if(this.autoSize){
32808             var sz = this.boundEl.getSize();
32809             switch(this.autoSize){
32810                 case "width":
32811                 this.setSize(sz.width,  "");
32812                 break;
32813                 case "height":
32814                 this.setSize("",  sz.height);
32815                 break;
32816                 default:
32817                 this.setSize(sz.width,  sz.height);
32818             }
32819         }
32820         this.el.alignTo(this.boundEl, this.alignment);
32821         this.editing = true;
32822         if(Roo.QuickTips){
32823             Roo.QuickTips.disable();
32824         }
32825         this.show();
32826     },
32827
32828     /**
32829      * Sets the height and width of this editor.
32830      * @param {Number} width The new width
32831      * @param {Number} height The new height
32832      */
32833     setSize : function(w, h){
32834         this.field.setSize(w, h);
32835         if(this.el){
32836             this.el.sync();
32837         }
32838     },
32839
32840     /**
32841      * Realigns the editor to the bound field based on the current alignment config value.
32842      */
32843     realign : function(){
32844         this.el.alignTo(this.boundEl, this.alignment);
32845     },
32846
32847     /**
32848      * Ends the editing process, persists the changed value to the underlying field, and hides the editor.
32849      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after edit (defaults to false)
32850      */
32851     completeEdit : function(remainVisible){
32852         if(!this.editing){
32853             return;
32854         }
32855         var v = this.getValue();
32856         if(this.revertInvalid !== false && !this.field.isValid()){
32857             v = this.startValue;
32858             this.cancelEdit(true);
32859         }
32860         if(String(v) === String(this.startValue) && this.ignoreNoChange){
32861             this.editing = false;
32862             this.hide();
32863             return;
32864         }
32865         if(this.fireEvent("beforecomplete", this, v, this.startValue) !== false){
32866             this.editing = false;
32867             if(this.updateEl && this.boundEl){
32868                 this.boundEl.update(v);
32869             }
32870             if(remainVisible !== true){
32871                 this.hide();
32872             }
32873             this.fireEvent("complete", this, v, this.startValue);
32874         }
32875     },
32876
32877     // private
32878     onShow : function(){
32879         this.el.show();
32880         if(this.hideEl !== false){
32881             this.boundEl.hide();
32882         }
32883         this.field.show();
32884         if(Roo.isIE && !this.fixIEFocus){ // IE has problems with focusing the first time
32885             this.fixIEFocus = true;
32886             this.deferredFocus.defer(50, this);
32887         }else{
32888             this.field.focus();
32889         }
32890         this.fireEvent("startedit", this.boundEl, this.startValue);
32891     },
32892
32893     deferredFocus : function(){
32894         if(this.editing){
32895             this.field.focus();
32896         }
32897     },
32898
32899     /**
32900      * Cancels the editing process and hides the editor without persisting any changes.  The field value will be
32901      * reverted to the original starting value.
32902      * @param {Boolean} remainVisible Override the default behavior and keep the editor visible after
32903      * cancel (defaults to false)
32904      */
32905     cancelEdit : function(remainVisible){
32906         if(this.editing){
32907             this.setValue(this.startValue);
32908             if(remainVisible !== true){
32909                 this.hide();
32910             }
32911         }
32912     },
32913
32914     // private
32915     onBlur : function(){
32916         if(this.allowBlur !== true && this.editing){
32917             this.completeEdit();
32918         }
32919     },
32920
32921     // private
32922     onHide : function(){
32923         if(this.editing){
32924             this.completeEdit();
32925             return;
32926         }
32927         this.field.blur();
32928         if(this.field.collapse){
32929             this.field.collapse();
32930         }
32931         this.el.hide();
32932         if(this.hideEl !== false){
32933             this.boundEl.show();
32934         }
32935         if(Roo.QuickTips){
32936             Roo.QuickTips.enable();
32937         }
32938     },
32939
32940     /**
32941      * Sets the data value of the editor
32942      * @param {Mixed} value Any valid value supported by the underlying field
32943      */
32944     setValue : function(v){
32945         this.field.setValue(v);
32946     },
32947
32948     /**
32949      * Gets the data value of the editor
32950      * @return {Mixed} The data value
32951      */
32952     getValue : function(){
32953         return this.field.getValue();
32954     }
32955 });/*
32956  * Based on:
32957  * Ext JS Library 1.1.1
32958  * Copyright(c) 2006-2007, Ext JS, LLC.
32959  *
32960  * Originally Released Under LGPL - original licence link has changed is not relivant.
32961  *
32962  * Fork - LGPL
32963  * <script type="text/javascript">
32964  */
32965  
32966 /**
32967  * @class Roo.BasicDialog
32968  * @extends Roo.util.Observable
32969  * Lightweight Dialog Class.  The code below shows the creation of a typical dialog using existing HTML markup:
32970  * <pre><code>
32971 var dlg = new Roo.BasicDialog("my-dlg", {
32972     height: 200,
32973     width: 300,
32974     minHeight: 100,
32975     minWidth: 150,
32976     modal: true,
32977     proxyDrag: true,
32978     shadow: true
32979 });
32980 dlg.addKeyListener(27, dlg.hide, dlg); // ESC can also close the dialog
32981 dlg.addButton('OK', dlg.hide, dlg);    // Could call a save function instead of hiding
32982 dlg.addButton('Cancel', dlg.hide, dlg);
32983 dlg.show();
32984 </code></pre>
32985   <b>A Dialog should always be a direct child of the body element.</b>
32986  * @cfg {Boolean/DomHelper} autoCreate True to auto create from scratch, or using a DomHelper Object (defaults to false)
32987  * @cfg {String} title Default text to display in the title bar (defaults to null)
32988  * @cfg {Number} width Width of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32989  * @cfg {Number} height Height of the dialog in pixels (can also be set via CSS).  Determined by browser if unspecified.
32990  * @cfg {Number} x The default left page coordinate of the dialog (defaults to center screen)
32991  * @cfg {Number} y The default top page coordinate of the dialog (defaults to center screen)
32992  * @cfg {String/Element} animateTarget Id or element from which the dialog should animate while opening
32993  * (defaults to null with no animation)
32994  * @cfg {Boolean} resizable False to disable manual dialog resizing (defaults to true)
32995  * @cfg {String} resizeHandles Which resize handles to display - see the {@link Roo.Resizable} handles config
32996  * property for valid values (defaults to 'all')
32997  * @cfg {Number} minHeight The minimum allowable height for a resizable dialog (defaults to 80)
32998  * @cfg {Number} minWidth The minimum allowable width for a resizable dialog (defaults to 200)
32999  * @cfg {Boolean} modal True to show the dialog modally, preventing user interaction with the rest of the page (defaults to false)
33000  * @cfg {Boolean} autoScroll True to allow the dialog body contents to overflow and display scrollbars (defaults to false)
33001  * @cfg {Boolean} closable False to remove the built-in top-right corner close button (defaults to true)
33002  * @cfg {Boolean} collapsible False to remove the built-in top-right corner collapse button (defaults to true)
33003  * @cfg {Boolean} constraintoviewport True to keep the dialog constrained within the visible viewport boundaries (defaults to true)
33004  * @cfg {Boolean} syncHeightBeforeShow True to cause the dimensions to be recalculated before the dialog is shown (defaults to false)
33005  * @cfg {Boolean} draggable False to disable dragging of the dialog within the viewport (defaults to true)
33006  * @cfg {Boolean} autoTabs If true, all elements with class 'x-dlg-tab' will get automatically converted to tabs (defaults to false)
33007  * @cfg {String} tabTag The tag name of tab elements, used when autoTabs = true (defaults to 'div')
33008  * @cfg {Boolean} proxyDrag True to drag a lightweight proxy element rather than the dialog itself, used when
33009  * draggable = true (defaults to false)
33010  * @cfg {Boolean} fixedcenter True to ensure that anytime the dialog is shown or resized it gets centered (defaults to false)
33011  * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
33012  * shadow (defaults to false)
33013  * @cfg {Number} shadowOffset The number of pixels to offset the shadow if displayed (defaults to 5)
33014  * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "right")
33015  * @cfg {Number} minButtonWidth Minimum width of all dialog buttons (defaults to 75)
33016  * @cfg {Array} buttons Array of buttons
33017  * @cfg {Boolean} shim True to create an iframe shim that prevents selects from showing through (defaults to false)
33018  * @constructor
33019  * Create a new BasicDialog.
33020  * @param {String/HTMLElement/Roo.Element} el The container element or DOM node, or its id
33021  * @param {Object} config Configuration options
33022  */
33023 Roo.BasicDialog = function(el, config){
33024     this.el = Roo.get(el);
33025     var dh = Roo.DomHelper;
33026     if(!this.el && config && config.autoCreate){
33027         if(typeof config.autoCreate == "object"){
33028             if(!config.autoCreate.id){
33029                 config.autoCreate.id = el;
33030             }
33031             this.el = dh.append(document.body,
33032                         config.autoCreate, true);
33033         }else{
33034             this.el = dh.append(document.body,
33035                         {tag: "div", id: el, style:'visibility:hidden;'}, true);
33036         }
33037     }
33038     el = this.el;
33039     el.setDisplayed(true);
33040     el.hide = this.hideAction;
33041     this.id = el.id;
33042     el.addClass("x-dlg");
33043
33044     Roo.apply(this, config);
33045
33046     this.proxy = el.createProxy("x-dlg-proxy");
33047     this.proxy.hide = this.hideAction;
33048     this.proxy.setOpacity(.5);
33049     this.proxy.hide();
33050
33051     if(config.width){
33052         el.setWidth(config.width);
33053     }
33054     if(config.height){
33055         el.setHeight(config.height);
33056     }
33057     this.size = el.getSize();
33058     if(typeof config.x != "undefined" && typeof config.y != "undefined"){
33059         this.xy = [config.x,config.y];
33060     }else{
33061         this.xy = el.getCenterXY(true);
33062     }
33063     /** The header element @type Roo.Element */
33064     this.header = el.child("> .x-dlg-hd");
33065     /** The body element @type Roo.Element */
33066     this.body = el.child("> .x-dlg-bd");
33067     /** The footer element @type Roo.Element */
33068     this.footer = el.child("> .x-dlg-ft");
33069
33070     if(!this.header){
33071         this.header = el.createChild({tag: "div", cls:"x-dlg-hd", html: "&#160;"}, this.body ? this.body.dom : null);
33072     }
33073     if(!this.body){
33074         this.body = el.createChild({tag: "div", cls:"x-dlg-bd"});
33075     }
33076
33077     this.header.unselectable();
33078     if(this.title){
33079         this.header.update(this.title);
33080     }
33081     // this element allows the dialog to be focused for keyboard event
33082     this.focusEl = el.createChild({tag: "a", href:"#", cls:"x-dlg-focus", tabIndex:"-1"});
33083     this.focusEl.swallowEvent("click", true);
33084
33085     this.header.wrap({cls:"x-dlg-hd-right"}).wrap({cls:"x-dlg-hd-left"}, true);
33086
33087     // wrap the body and footer for special rendering
33088     this.bwrap = this.body.wrap({tag: "div", cls:"x-dlg-dlg-body"});
33089     if(this.footer){
33090         this.bwrap.dom.appendChild(this.footer.dom);
33091     }
33092
33093     this.bg = this.el.createChild({
33094         tag: "div", cls:"x-dlg-bg",
33095         html: '<div class="x-dlg-bg-left"><div class="x-dlg-bg-right"><div class="x-dlg-bg-center">&#160;</div></div></div>'
33096     });
33097     this.centerBg = this.bg.child("div.x-dlg-bg-center");
33098
33099
33100     if(this.autoScroll !== false && !this.autoTabs){
33101         this.body.setStyle("overflow", "auto");
33102     }
33103
33104     this.toolbox = this.el.createChild({cls: "x-dlg-toolbox"});
33105
33106     if(this.closable !== false){
33107         this.el.addClass("x-dlg-closable");
33108         this.close = this.toolbox.createChild({cls:"x-dlg-close"});
33109         this.close.on("click", this.closeClick, this);
33110         this.close.addClassOnOver("x-dlg-close-over");
33111     }
33112     if(this.collapsible !== false){
33113         this.collapseBtn = this.toolbox.createChild({cls:"x-dlg-collapse"});
33114         this.collapseBtn.on("click", this.collapseClick, this);
33115         this.collapseBtn.addClassOnOver("x-dlg-collapse-over");
33116         this.header.on("dblclick", this.collapseClick, this);
33117     }
33118     if(this.resizable !== false){
33119         this.el.addClass("x-dlg-resizable");
33120         this.resizer = new Roo.Resizable(el, {
33121             minWidth: this.minWidth || 80,
33122             minHeight:this.minHeight || 80,
33123             handles: this.resizeHandles || "all",
33124             pinned: true
33125         });
33126         this.resizer.on("beforeresize", this.beforeResize, this);
33127         this.resizer.on("resize", this.onResize, this);
33128     }
33129     if(this.draggable !== false){
33130         el.addClass("x-dlg-draggable");
33131         if (!this.proxyDrag) {
33132             var dd = new Roo.dd.DD(el.dom.id, "WindowDrag");
33133         }
33134         else {
33135             var dd = new Roo.dd.DDProxy(el.dom.id, "WindowDrag", {dragElId: this.proxy.id});
33136         }
33137         dd.setHandleElId(this.header.id);
33138         dd.endDrag = this.endMove.createDelegate(this);
33139         dd.startDrag = this.startMove.createDelegate(this);
33140         dd.onDrag = this.onDrag.createDelegate(this);
33141         dd.scroll = false;
33142         this.dd = dd;
33143     }
33144     if(this.modal){
33145         this.mask = dh.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
33146         this.mask.enableDisplayMode("block");
33147         this.mask.hide();
33148         this.el.addClass("x-dlg-modal");
33149     }
33150     if(this.shadow){
33151         this.shadow = new Roo.Shadow({
33152             mode : typeof this.shadow == "string" ? this.shadow : "sides",
33153             offset : this.shadowOffset
33154         });
33155     }else{
33156         this.shadowOffset = 0;
33157     }
33158     if(Roo.useShims && this.shim !== false){
33159         this.shim = this.el.createShim();
33160         this.shim.hide = this.hideAction;
33161         this.shim.hide();
33162     }else{
33163         this.shim = false;
33164     }
33165     if(this.autoTabs){
33166         this.initTabs();
33167     }
33168     if (this.buttons) { 
33169         var bts= this.buttons;
33170         this.buttons = [];
33171         Roo.each(bts, function(b) {
33172             this.addButton(b);
33173         }, this);
33174     }
33175     
33176     
33177     this.addEvents({
33178         /**
33179          * @event keydown
33180          * Fires when a key is pressed
33181          * @param {Roo.BasicDialog} this
33182          * @param {Roo.EventObject} e
33183          */
33184         "keydown" : true,
33185         /**
33186          * @event move
33187          * Fires when this dialog is moved by the user.
33188          * @param {Roo.BasicDialog} this
33189          * @param {Number} x The new page X
33190          * @param {Number} y The new page Y
33191          */
33192         "move" : true,
33193         /**
33194          * @event resize
33195          * Fires when this dialog is resized by the user.
33196          * @param {Roo.BasicDialog} this
33197          * @param {Number} width The new width
33198          * @param {Number} height The new height
33199          */
33200         "resize" : true,
33201         /**
33202          * @event beforehide
33203          * Fires before this dialog is hidden.
33204          * @param {Roo.BasicDialog} this
33205          */
33206         "beforehide" : true,
33207         /**
33208          * @event hide
33209          * Fires when this dialog is hidden.
33210          * @param {Roo.BasicDialog} this
33211          */
33212         "hide" : true,
33213         /**
33214          * @event beforeshow
33215          * Fires before this dialog is shown.
33216          * @param {Roo.BasicDialog} this
33217          */
33218         "beforeshow" : true,
33219         /**
33220          * @event show
33221          * Fires when this dialog is shown.
33222          * @param {Roo.BasicDialog} this
33223          */
33224         "show" : true
33225     });
33226     el.on("keydown", this.onKeyDown, this);
33227     el.on("mousedown", this.toFront, this);
33228     Roo.EventManager.onWindowResize(this.adjustViewport, this, true);
33229     this.el.hide();
33230     Roo.DialogManager.register(this);
33231     Roo.BasicDialog.superclass.constructor.call(this);
33232 };
33233
33234 Roo.extend(Roo.BasicDialog, Roo.util.Observable, {
33235     shadowOffset: Roo.isIE ? 6 : 5,
33236     minHeight: 80,
33237     minWidth: 200,
33238     minButtonWidth: 75,
33239     defaultButton: null,
33240     buttonAlign: "right",
33241     tabTag: 'div',
33242     firstShow: true,
33243
33244     /**
33245      * Sets the dialog title text
33246      * @param {String} text The title text to display
33247      * @return {Roo.BasicDialog} this
33248      */
33249     setTitle : function(text){
33250         this.header.update(text);
33251         return this;
33252     },
33253
33254     // private
33255     closeClick : function(){
33256         this.hide();
33257     },
33258
33259     // private
33260     collapseClick : function(){
33261         this[this.collapsed ? "expand" : "collapse"]();
33262     },
33263
33264     /**
33265      * Collapses the dialog to its minimized state (only the title bar is visible).
33266      * Equivalent to the user clicking the collapse dialog button.
33267      */
33268     collapse : function(){
33269         if(!this.collapsed){
33270             this.collapsed = true;
33271             this.el.addClass("x-dlg-collapsed");
33272             this.restoreHeight = this.el.getHeight();
33273             this.resizeTo(this.el.getWidth(), this.header.getHeight());
33274         }
33275     },
33276
33277     /**
33278      * Expands a collapsed dialog back to its normal state.  Equivalent to the user
33279      * clicking the expand dialog button.
33280      */
33281     expand : function(){
33282         if(this.collapsed){
33283             this.collapsed = false;
33284             this.el.removeClass("x-dlg-collapsed");
33285             this.resizeTo(this.el.getWidth(), this.restoreHeight);
33286         }
33287     },
33288
33289     /**
33290      * Reinitializes the tabs component, clearing out old tabs and finding new ones.
33291      * @return {Roo.TabPanel} The tabs component
33292      */
33293     initTabs : function(){
33294         var tabs = this.getTabs();
33295         while(tabs.getTab(0)){
33296             tabs.removeTab(0);
33297         }
33298         this.el.select(this.tabTag+'.x-dlg-tab').each(function(el){
33299             var dom = el.dom;
33300             tabs.addTab(Roo.id(dom), dom.title);
33301             dom.title = "";
33302         });
33303         tabs.activate(0);
33304         return tabs;
33305     },
33306
33307     // private
33308     beforeResize : function(){
33309         this.resizer.minHeight = Math.max(this.minHeight, this.getHeaderFooterHeight(true)+40);
33310     },
33311
33312     // private
33313     onResize : function(){
33314         this.refreshSize();
33315         this.syncBodyHeight();
33316         this.adjustAssets();
33317         this.focus();
33318         this.fireEvent("resize", this, this.size.width, this.size.height);
33319     },
33320
33321     // private
33322     onKeyDown : function(e){
33323         if(this.isVisible()){
33324             this.fireEvent("keydown", this, e);
33325         }
33326     },
33327
33328     /**
33329      * Resizes the dialog.
33330      * @param {Number} width
33331      * @param {Number} height
33332      * @return {Roo.BasicDialog} this
33333      */
33334     resizeTo : function(width, height){
33335         this.el.setSize(width, height);
33336         this.size = {width: width, height: height};
33337         this.syncBodyHeight();
33338         if(this.fixedcenter){
33339             this.center();
33340         }
33341         if(this.isVisible()){
33342             this.constrainXY();
33343             this.adjustAssets();
33344         }
33345         this.fireEvent("resize", this, width, height);
33346         return this;
33347     },
33348
33349
33350     /**
33351      * Resizes the dialog to fit the specified content size.
33352      * @param {Number} width
33353      * @param {Number} height
33354      * @return {Roo.BasicDialog} this
33355      */
33356     setContentSize : function(w, h){
33357         h += this.getHeaderFooterHeight() + this.body.getMargins("tb");
33358         w += this.body.getMargins("lr") + this.bwrap.getMargins("lr") + this.centerBg.getPadding("lr");
33359         //if(!this.el.isBorderBox()){
33360             h +=  this.body.getPadding("tb") + this.bwrap.getBorderWidth("tb") + this.body.getBorderWidth("tb") + this.el.getBorderWidth("tb");
33361             w += this.body.getPadding("lr") + this.bwrap.getBorderWidth("lr") + this.body.getBorderWidth("lr") + this.bwrap.getPadding("lr") + this.el.getBorderWidth("lr");
33362         //}
33363         if(this.tabs){
33364             h += this.tabs.stripWrap.getHeight() + this.tabs.bodyEl.getMargins("tb") + this.tabs.bodyEl.getPadding("tb");
33365             w += this.tabs.bodyEl.getMargins("lr") + this.tabs.bodyEl.getPadding("lr");
33366         }
33367         this.resizeTo(w, h);
33368         return this;
33369     },
33370
33371     /**
33372      * Adds a key listener for when this dialog is displayed.  This allows you to hook in a function that will be
33373      * executed in response to a particular key being pressed while the dialog is active.
33374      * @param {Number/Array/Object} key Either the numeric key code, array of key codes or an object with the following options:
33375      *                                  {key: (number or array), shift: (true/false), ctrl: (true/false), alt: (true/false)}
33376      * @param {Function} fn The function to call
33377      * @param {Object} scope (optional) The scope of the function
33378      * @return {Roo.BasicDialog} this
33379      */
33380     addKeyListener : function(key, fn, scope){
33381         var keyCode, shift, ctrl, alt;
33382         if(typeof key == "object" && !(key instanceof Array)){
33383             keyCode = key["key"];
33384             shift = key["shift"];
33385             ctrl = key["ctrl"];
33386             alt = key["alt"];
33387         }else{
33388             keyCode = key;
33389         }
33390         var handler = function(dlg, e){
33391             if((!shift || e.shiftKey) && (!ctrl || e.ctrlKey) &&  (!alt || e.altKey)){
33392                 var k = e.getKey();
33393                 if(keyCode instanceof Array){
33394                     for(var i = 0, len = keyCode.length; i < len; i++){
33395                         if(keyCode[i] == k){
33396                           fn.call(scope || window, dlg, k, e);
33397                           return;
33398                         }
33399                     }
33400                 }else{
33401                     if(k == keyCode){
33402                         fn.call(scope || window, dlg, k, e);
33403                     }
33404                 }
33405             }
33406         };
33407         this.on("keydown", handler);
33408         return this;
33409     },
33410
33411     /**
33412      * Returns the TabPanel component (creates it if it doesn't exist).
33413      * Note: If you wish to simply check for the existence of tabs without creating them,
33414      * check for a null 'tabs' property.
33415      * @return {Roo.TabPanel} The tabs component
33416      */
33417     getTabs : function(){
33418         if(!this.tabs){
33419             this.el.addClass("x-dlg-auto-tabs");
33420             this.body.addClass(this.tabPosition == "bottom" ? "x-tabs-bottom" : "x-tabs-top");
33421             this.tabs = new Roo.TabPanel(this.body.dom, this.tabPosition == "bottom");
33422         }
33423         return this.tabs;
33424     },
33425
33426     /**
33427      * Adds a button to the footer section of the dialog.
33428      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
33429      * object or a valid Roo.DomHelper element config
33430      * @param {Function} handler The function called when the button is clicked
33431      * @param {Object} scope (optional) The scope of the handler function (accepts position as a property)
33432      * @return {Roo.Button} The new button
33433      */
33434     addButton : function(config, handler, scope){
33435         var dh = Roo.DomHelper;
33436         if(!this.footer){
33437             this.footer = dh.append(this.bwrap, {tag: "div", cls:"x-dlg-ft"}, true);
33438         }
33439         if(!this.btnContainer){
33440             var tb = this.footer.createChild({
33441
33442                 cls:"x-dlg-btns x-dlg-btns-"+this.buttonAlign,
33443                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
33444             }, null, true);
33445             this.btnContainer = tb.firstChild.firstChild.firstChild;
33446         }
33447         var bconfig = {
33448             handler: handler,
33449             scope: scope,
33450             minWidth: this.minButtonWidth,
33451             hideParent:true
33452         };
33453         if(typeof config == "string"){
33454             bconfig.text = config;
33455         }else{
33456             if(config.tag){
33457                 bconfig.dhconfig = config;
33458             }else{
33459                 Roo.apply(bconfig, config);
33460             }
33461         }
33462         var fc = false;
33463         if ((typeof(bconfig.position) != 'undefined') && bconfig.position < this.btnContainer.childNodes.length-1) {
33464             bconfig.position = Math.max(0, bconfig.position);
33465             fc = this.btnContainer.childNodes[bconfig.position];
33466         }
33467          
33468         var btn = new Roo.Button(
33469             fc ? 
33470                 this.btnContainer.insertBefore(document.createElement("td"),fc)
33471                 : this.btnContainer.appendChild(document.createElement("td")),
33472             //Roo.get(this.btnContainer).createChild( { tag: 'td'},  fc ),
33473             bconfig
33474         );
33475         this.syncBodyHeight();
33476         if(!this.buttons){
33477             /**
33478              * Array of all the buttons that have been added to this dialog via addButton
33479              * @type Array
33480              */
33481             this.buttons = [];
33482         }
33483         this.buttons.push(btn);
33484         return btn;
33485     },
33486
33487     /**
33488      * Sets the default button to be focused when the dialog is displayed.
33489      * @param {Roo.BasicDialog.Button} btn The button object returned by {@link #addButton}
33490      * @return {Roo.BasicDialog} this
33491      */
33492     setDefaultButton : function(btn){
33493         this.defaultButton = btn;
33494         return this;
33495     },
33496
33497     // private
33498     getHeaderFooterHeight : function(safe){
33499         var height = 0;
33500         if(this.header){
33501            height += this.header.getHeight();
33502         }
33503         if(this.footer){
33504            var fm = this.footer.getMargins();
33505             height += (this.footer.getHeight()+fm.top+fm.bottom);
33506         }
33507         height += this.bwrap.getPadding("tb")+this.bwrap.getBorderWidth("tb");
33508         height += this.centerBg.getPadding("tb");
33509         return height;
33510     },
33511
33512     // private
33513     syncBodyHeight : function()
33514     {
33515         var bd = this.body, // the text
33516             cb = this.centerBg, // wrapper around bottom.. but does not seem to be used..
33517             bw = this.bwrap;
33518         var height = this.size.height - this.getHeaderFooterHeight(false);
33519         bd.setHeight(height-bd.getMargins("tb"));
33520         var hh = this.header.getHeight();
33521         var h = this.size.height-hh;
33522         cb.setHeight(h);
33523         
33524         bw.setLeftTop(cb.getPadding("l"), hh+cb.getPadding("t"));
33525         bw.setHeight(h-cb.getPadding("tb"));
33526         
33527         bw.setWidth(this.el.getWidth(true)-cb.getPadding("lr"));
33528         bd.setWidth(bw.getWidth(true));
33529         if(this.tabs){
33530             this.tabs.syncHeight();
33531             if(Roo.isIE){
33532                 this.tabs.el.repaint();
33533             }
33534         }
33535     },
33536
33537     /**
33538      * Restores the previous state of the dialog if Roo.state is configured.
33539      * @return {Roo.BasicDialog} this
33540      */
33541     restoreState : function(){
33542         var box = Roo.state.Manager.get(this.stateId || (this.el.id + "-state"));
33543         if(box && box.width){
33544             this.xy = [box.x, box.y];
33545             this.resizeTo(box.width, box.height);
33546         }
33547         return this;
33548     },
33549
33550     // private
33551     beforeShow : function(){
33552         this.expand();
33553         if(this.fixedcenter){
33554             this.xy = this.el.getCenterXY(true);
33555         }
33556         if(this.modal){
33557             Roo.get(document.body).addClass("x-body-masked");
33558             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33559             this.mask.show();
33560         }
33561         this.constrainXY();
33562     },
33563
33564     // private
33565     animShow : function(){
33566         var b = Roo.get(this.animateTarget).getBox();
33567         this.proxy.setSize(b.width, b.height);
33568         this.proxy.setLocation(b.x, b.y);
33569         this.proxy.show();
33570         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height,
33571                     true, .35, this.showEl.createDelegate(this));
33572     },
33573
33574     /**
33575      * Shows the dialog.
33576      * @param {String/HTMLElement/Roo.Element} animateTarget (optional) Reset the animation target
33577      * @return {Roo.BasicDialog} this
33578      */
33579     show : function(animateTarget){
33580         if (this.fireEvent("beforeshow", this) === false){
33581             return;
33582         }
33583         if(this.syncHeightBeforeShow){
33584             this.syncBodyHeight();
33585         }else if(this.firstShow){
33586             this.firstShow = false;
33587             this.syncBodyHeight(); // sync the height on the first show instead of in the constructor
33588         }
33589         this.animateTarget = animateTarget || this.animateTarget;
33590         if(!this.el.isVisible()){
33591             this.beforeShow();
33592             if(this.animateTarget && Roo.get(this.animateTarget)){
33593                 this.animShow();
33594             }else{
33595                 this.showEl();
33596             }
33597         }
33598         return this;
33599     },
33600
33601     // private
33602     showEl : function(){
33603         this.proxy.hide();
33604         this.el.setXY(this.xy);
33605         this.el.show();
33606         this.adjustAssets(true);
33607         this.toFront();
33608         this.focus();
33609         // IE peekaboo bug - fix found by Dave Fenwick
33610         if(Roo.isIE){
33611             this.el.repaint();
33612         }
33613         this.fireEvent("show", this);
33614     },
33615
33616     /**
33617      * Focuses the dialog.  If a defaultButton is set, it will receive focus, otherwise the
33618      * dialog itself will receive focus.
33619      */
33620     focus : function(){
33621         if(this.defaultButton){
33622             this.defaultButton.focus();
33623         }else{
33624             this.focusEl.focus();
33625         }
33626     },
33627
33628     // private
33629     constrainXY : function(){
33630         if(this.constraintoviewport !== false){
33631             if(!this.viewSize){
33632                 if(this.container){
33633                     var s = this.container.getSize();
33634                     this.viewSize = [s.width, s.height];
33635                 }else{
33636                     this.viewSize = [Roo.lib.Dom.getViewWidth(),Roo.lib.Dom.getViewHeight()];
33637                 }
33638             }
33639             var s = Roo.get(this.container||document).getScroll();
33640
33641             var x = this.xy[0], y = this.xy[1];
33642             var w = this.size.width, h = this.size.height;
33643             var vw = this.viewSize[0], vh = this.viewSize[1];
33644             // only move it if it needs it
33645             var moved = false;
33646             // first validate right/bottom
33647             if(x + w > vw+s.left){
33648                 x = vw - w;
33649                 moved = true;
33650             }
33651             if(y + h > vh+s.top){
33652                 y = vh - h;
33653                 moved = true;
33654             }
33655             // then make sure top/left isn't negative
33656             if(x < s.left){
33657                 x = s.left;
33658                 moved = true;
33659             }
33660             if(y < s.top){
33661                 y = s.top;
33662                 moved = true;
33663             }
33664             if(moved){
33665                 // cache xy
33666                 this.xy = [x, y];
33667                 if(this.isVisible()){
33668                     this.el.setLocation(x, y);
33669                     this.adjustAssets();
33670                 }
33671             }
33672         }
33673     },
33674
33675     // private
33676     onDrag : function(){
33677         if(!this.proxyDrag){
33678             this.xy = this.el.getXY();
33679             this.adjustAssets();
33680         }
33681     },
33682
33683     // private
33684     adjustAssets : function(doShow){
33685         var x = this.xy[0], y = this.xy[1];
33686         var w = this.size.width, h = this.size.height;
33687         if(doShow === true){
33688             if(this.shadow){
33689                 this.shadow.show(this.el);
33690             }
33691             if(this.shim){
33692                 this.shim.show();
33693             }
33694         }
33695         if(this.shadow && this.shadow.isVisible()){
33696             this.shadow.show(this.el);
33697         }
33698         if(this.shim && this.shim.isVisible()){
33699             this.shim.setBounds(x, y, w, h);
33700         }
33701     },
33702
33703     // private
33704     adjustViewport : function(w, h){
33705         if(!w || !h){
33706             w = Roo.lib.Dom.getViewWidth();
33707             h = Roo.lib.Dom.getViewHeight();
33708         }
33709         // cache the size
33710         this.viewSize = [w, h];
33711         if(this.modal && this.mask.isVisible()){
33712             this.mask.setSize(w, h); // first make sure the mask isn't causing overflow
33713             this.mask.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
33714         }
33715         if(this.isVisible()){
33716             this.constrainXY();
33717         }
33718     },
33719
33720     /**
33721      * Destroys this dialog and all its supporting elements (including any tabs, shim,
33722      * shadow, proxy, mask, etc.)  Also removes all event listeners.
33723      * @param {Boolean} removeEl (optional) true to remove the element from the DOM
33724      */
33725     destroy : function(removeEl){
33726         if(this.isVisible()){
33727             this.animateTarget = null;
33728             this.hide();
33729         }
33730         Roo.EventManager.removeResizeListener(this.adjustViewport, this);
33731         if(this.tabs){
33732             this.tabs.destroy(removeEl);
33733         }
33734         Roo.destroy(
33735              this.shim,
33736              this.proxy,
33737              this.resizer,
33738              this.close,
33739              this.mask
33740         );
33741         if(this.dd){
33742             this.dd.unreg();
33743         }
33744         if(this.buttons){
33745            for(var i = 0, len = this.buttons.length; i < len; i++){
33746                this.buttons[i].destroy();
33747            }
33748         }
33749         this.el.removeAllListeners();
33750         if(removeEl === true){
33751             this.el.update("");
33752             this.el.remove();
33753         }
33754         Roo.DialogManager.unregister(this);
33755     },
33756
33757     // private
33758     startMove : function(){
33759         if(this.proxyDrag){
33760             this.proxy.show();
33761         }
33762         if(this.constraintoviewport !== false){
33763             this.dd.constrainTo(document.body, {right: this.shadowOffset, bottom: this.shadowOffset});
33764         }
33765     },
33766
33767     // private
33768     endMove : function(){
33769         if(!this.proxyDrag){
33770             Roo.dd.DD.prototype.endDrag.apply(this.dd, arguments);
33771         }else{
33772             Roo.dd.DDProxy.prototype.endDrag.apply(this.dd, arguments);
33773             this.proxy.hide();
33774         }
33775         this.refreshSize();
33776         this.adjustAssets();
33777         this.focus();
33778         this.fireEvent("move", this, this.xy[0], this.xy[1]);
33779     },
33780
33781     /**
33782      * Brings this dialog to the front of any other visible dialogs
33783      * @return {Roo.BasicDialog} this
33784      */
33785     toFront : function(){
33786         Roo.DialogManager.bringToFront(this);
33787         return this;
33788     },
33789
33790     /**
33791      * Sends this dialog to the back (under) of any other visible dialogs
33792      * @return {Roo.BasicDialog} this
33793      */
33794     toBack : function(){
33795         Roo.DialogManager.sendToBack(this);
33796         return this;
33797     },
33798
33799     /**
33800      * Centers this dialog in the viewport
33801      * @return {Roo.BasicDialog} this
33802      */
33803     center : function(){
33804         var xy = this.el.getCenterXY(true);
33805         this.moveTo(xy[0], xy[1]);
33806         return this;
33807     },
33808
33809     /**
33810      * Moves the dialog's top-left corner to the specified point
33811      * @param {Number} x
33812      * @param {Number} y
33813      * @return {Roo.BasicDialog} this
33814      */
33815     moveTo : function(x, y){
33816         this.xy = [x,y];
33817         if(this.isVisible()){
33818             this.el.setXY(this.xy);
33819             this.adjustAssets();
33820         }
33821         return this;
33822     },
33823
33824     /**
33825      * Aligns the dialog to the specified element
33826      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33827      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details).
33828      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33829      * @return {Roo.BasicDialog} this
33830      */
33831     alignTo : function(element, position, offsets){
33832         this.xy = this.el.getAlignToXY(element, position, offsets);
33833         if(this.isVisible()){
33834             this.el.setXY(this.xy);
33835             this.adjustAssets();
33836         }
33837         return this;
33838     },
33839
33840     /**
33841      * Anchors an element to another element and realigns it when the window is resized.
33842      * @param {String/HTMLElement/Roo.Element} element The element to align to.
33843      * @param {String} position The position to align to (see {@link Roo.Element#alignTo} for more details)
33844      * @param {Array} offsets (optional) Offset the positioning by [x, y]
33845      * @param {Boolean/Number} monitorScroll (optional) true to monitor body scroll and reposition. If this parameter
33846      * is a number, it is used as the buffer delay (defaults to 50ms).
33847      * @return {Roo.BasicDialog} this
33848      */
33849     anchorTo : function(el, alignment, offsets, monitorScroll){
33850         var action = function(){
33851             this.alignTo(el, alignment, offsets);
33852         };
33853         Roo.EventManager.onWindowResize(action, this);
33854         var tm = typeof monitorScroll;
33855         if(tm != 'undefined'){
33856             Roo.EventManager.on(window, 'scroll', action, this,
33857                 {buffer: tm == 'number' ? monitorScroll : 50});
33858         }
33859         action.call(this);
33860         return this;
33861     },
33862
33863     /**
33864      * Returns true if the dialog is visible
33865      * @return {Boolean}
33866      */
33867     isVisible : function(){
33868         return this.el.isVisible();
33869     },
33870
33871     // private
33872     animHide : function(callback){
33873         var b = Roo.get(this.animateTarget).getBox();
33874         this.proxy.show();
33875         this.proxy.setBounds(this.xy[0], this.xy[1], this.size.width, this.size.height);
33876         this.el.hide();
33877         this.proxy.setBounds(b.x, b.y, b.width, b.height, true, .35,
33878                     this.hideEl.createDelegate(this, [callback]));
33879     },
33880
33881     /**
33882      * Hides the dialog.
33883      * @param {Function} callback (optional) Function to call when the dialog is hidden
33884      * @return {Roo.BasicDialog} this
33885      */
33886     hide : function(callback){
33887         if (this.fireEvent("beforehide", this) === false){
33888             return;
33889         }
33890         if(this.shadow){
33891             this.shadow.hide();
33892         }
33893         if(this.shim) {
33894           this.shim.hide();
33895         }
33896         // sometimes animateTarget seems to get set.. causing problems...
33897         // this just double checks..
33898         if(this.animateTarget && Roo.get(this.animateTarget)) {
33899            this.animHide(callback);
33900         }else{
33901             this.el.hide();
33902             this.hideEl(callback);
33903         }
33904         return this;
33905     },
33906
33907     // private
33908     hideEl : function(callback){
33909         this.proxy.hide();
33910         if(this.modal){
33911             this.mask.hide();
33912             Roo.get(document.body).removeClass("x-body-masked");
33913         }
33914         this.fireEvent("hide", this);
33915         if(typeof callback == "function"){
33916             callback();
33917         }
33918     },
33919
33920     // private
33921     hideAction : function(){
33922         this.setLeft("-10000px");
33923         this.setTop("-10000px");
33924         this.setStyle("visibility", "hidden");
33925     },
33926
33927     // private
33928     refreshSize : function(){
33929         this.size = this.el.getSize();
33930         this.xy = this.el.getXY();
33931         Roo.state.Manager.set(this.stateId || this.el.id + "-state", this.el.getBox());
33932     },
33933
33934     // private
33935     // z-index is managed by the DialogManager and may be overwritten at any time
33936     setZIndex : function(index){
33937         if(this.modal){
33938             this.mask.setStyle("z-index", index);
33939         }
33940         if(this.shim){
33941             this.shim.setStyle("z-index", ++index);
33942         }
33943         if(this.shadow){
33944             this.shadow.setZIndex(++index);
33945         }
33946         this.el.setStyle("z-index", ++index);
33947         if(this.proxy){
33948             this.proxy.setStyle("z-index", ++index);
33949         }
33950         if(this.resizer){
33951             this.resizer.proxy.setStyle("z-index", ++index);
33952         }
33953
33954         this.lastZIndex = index;
33955     },
33956
33957     /**
33958      * Returns the element for this dialog
33959      * @return {Roo.Element} The underlying dialog Element
33960      */
33961     getEl : function(){
33962         return this.el;
33963     }
33964 });
33965
33966 /**
33967  * @class Roo.DialogManager
33968  * Provides global access to BasicDialogs that have been created and
33969  * support for z-indexing (layering) multiple open dialogs.
33970  */
33971 Roo.DialogManager = function(){
33972     var list = {};
33973     var accessList = [];
33974     var front = null;
33975
33976     // private
33977     var sortDialogs = function(d1, d2){
33978         return (!d1._lastAccess || d1._lastAccess < d2._lastAccess) ? -1 : 1;
33979     };
33980
33981     // private
33982     var orderDialogs = function(){
33983         accessList.sort(sortDialogs);
33984         var seed = Roo.DialogManager.zseed;
33985         for(var i = 0, len = accessList.length; i < len; i++){
33986             var dlg = accessList[i];
33987             if(dlg){
33988                 dlg.setZIndex(seed + (i*10));
33989             }
33990         }
33991     };
33992
33993     return {
33994         /**
33995          * The starting z-index for BasicDialogs (defaults to 9000)
33996          * @type Number The z-index value
33997          */
33998         zseed : 9000,
33999
34000         // private
34001         register : function(dlg){
34002             list[dlg.id] = dlg;
34003             accessList.push(dlg);
34004         },
34005
34006         // private
34007         unregister : function(dlg){
34008             delete list[dlg.id];
34009             var i=0;
34010             var len=0;
34011             if(!accessList.indexOf){
34012                 for(  i = 0, len = accessList.length; i < len; i++){
34013                     if(accessList[i] == dlg){
34014                         accessList.splice(i, 1);
34015                         return;
34016                     }
34017                 }
34018             }else{
34019                  i = accessList.indexOf(dlg);
34020                 if(i != -1){
34021                     accessList.splice(i, 1);
34022                 }
34023             }
34024         },
34025
34026         /**
34027          * Gets a registered dialog by id
34028          * @param {String/Object} id The id of the dialog or a dialog
34029          * @return {Roo.BasicDialog} this
34030          */
34031         get : function(id){
34032             return typeof id == "object" ? id : list[id];
34033         },
34034
34035         /**
34036          * Brings the specified dialog to the front
34037          * @param {String/Object} dlg The id of the dialog or a dialog
34038          * @return {Roo.BasicDialog} this
34039          */
34040         bringToFront : function(dlg){
34041             dlg = this.get(dlg);
34042             if(dlg != front){
34043                 front = dlg;
34044                 dlg._lastAccess = new Date().getTime();
34045                 orderDialogs();
34046             }
34047             return dlg;
34048         },
34049
34050         /**
34051          * Sends the specified dialog to the back
34052          * @param {String/Object} dlg The id of the dialog or a dialog
34053          * @return {Roo.BasicDialog} this
34054          */
34055         sendToBack : function(dlg){
34056             dlg = this.get(dlg);
34057             dlg._lastAccess = -(new Date().getTime());
34058             orderDialogs();
34059             return dlg;
34060         },
34061
34062         /**
34063          * Hides all dialogs
34064          */
34065         hideAll : function(){
34066             for(var id in list){
34067                 if(list[id] && typeof list[id] != "function" && list[id].isVisible()){
34068                     list[id].hide();
34069                 }
34070             }
34071         }
34072     };
34073 }();
34074
34075 /**
34076  * @class Roo.LayoutDialog
34077  * @extends Roo.BasicDialog
34078  * @children Roo.ContentPanel
34079  * @parent builder none
34080  * Dialog which provides adjustments for working with a layout in a Dialog.
34081  * Add your necessary layout config options to the dialog's config.<br>
34082  * Example usage (including a nested layout):
34083  * <pre><code>
34084 if(!dialog){
34085     dialog = new Roo.LayoutDialog("download-dlg", {
34086         modal: true,
34087         width:600,
34088         height:450,
34089         shadow:true,
34090         minWidth:500,
34091         minHeight:350,
34092         autoTabs:true,
34093         proxyDrag:true,
34094         // layout config merges with the dialog config
34095         center:{
34096             tabPosition: "top",
34097             alwaysShowTabs: true
34098         }
34099     });
34100     dialog.addKeyListener(27, dialog.hide, dialog);
34101     dialog.setDefaultButton(dialog.addButton("Close", dialog.hide, dialog));
34102     dialog.addButton("Build It!", this.getDownload, this);
34103
34104     // we can even add nested layouts
34105     var innerLayout = new Roo.BorderLayout("dl-inner", {
34106         east: {
34107             initialSize: 200,
34108             autoScroll:true,
34109             split:true
34110         },
34111         center: {
34112             autoScroll:true
34113         }
34114     });
34115     innerLayout.beginUpdate();
34116     innerLayout.add("east", new Roo.ContentPanel("dl-details"));
34117     innerLayout.add("center", new Roo.ContentPanel("selection-panel"));
34118     innerLayout.endUpdate(true);
34119
34120     var layout = dialog.getLayout();
34121     layout.beginUpdate();
34122     layout.add("center", new Roo.ContentPanel("standard-panel",
34123                         {title: "Download the Source", fitToFrame:true}));
34124     layout.add("center", new Roo.NestedLayoutPanel(innerLayout,
34125                {title: "Build your own roo.js"}));
34126     layout.getRegion("center").showPanel(sp);
34127     layout.endUpdate();
34128 }
34129 </code></pre>
34130     * @constructor
34131     * @param {String/HTMLElement/Roo.Element} el The id of or container element, or config
34132     * @param {Object} config configuration options
34133   */
34134 Roo.LayoutDialog = function(el, cfg){
34135     
34136     var config=  cfg;
34137     if (typeof(cfg) == 'undefined') {
34138         config = Roo.apply({}, el);
34139         // not sure why we use documentElement here.. - it should always be body.
34140         // IE7 borks horribly if we use documentElement.
34141         // webkit also does not like documentElement - it creates a body element...
34142         el = Roo.get( document.body || document.documentElement ).createChild();
34143         //config.autoCreate = true;
34144     }
34145     
34146     
34147     config.autoTabs = false;
34148     Roo.LayoutDialog.superclass.constructor.call(this, el, config);
34149     this.body.setStyle({overflow:"hidden", position:"relative"});
34150     this.layout = new Roo.BorderLayout(this.body.dom, config);
34151     this.layout.monitorWindowResize = false;
34152     this.el.addClass("x-dlg-auto-layout");
34153     // fix case when center region overwrites center function
34154     this.center = Roo.BasicDialog.prototype.center;
34155     this.on("show", this.layout.layout, this.layout, true);
34156     if (config.items) {
34157         var xitems = config.items;
34158         delete config.items;
34159         Roo.each(xitems, this.addxtype, this);
34160     }
34161     
34162     
34163 };
34164 Roo.extend(Roo.LayoutDialog, Roo.BasicDialog, {
34165     
34166     
34167     /**
34168      * @cfg {Roo.LayoutRegion} east  
34169      */
34170     /**
34171      * @cfg {Roo.LayoutRegion} west
34172      */
34173     /**
34174      * @cfg {Roo.LayoutRegion} south
34175      */
34176     /**
34177      * @cfg {Roo.LayoutRegion} north
34178      */
34179     /**
34180      * @cfg {Roo.LayoutRegion} center
34181      */
34182     /**
34183      * @cfg {Roo.Button} buttons[]  Bottom buttons..
34184      */
34185     
34186     
34187     /**
34188      * Ends update of the layout <strike>and resets display to none</strike>. Use standard beginUpdate/endUpdate on the layout.
34189      * @deprecated
34190      */
34191     endUpdate : function(){
34192         this.layout.endUpdate();
34193     },
34194
34195     /**
34196      * Begins an update of the layout <strike>and sets display to block and visibility to hidden</strike>. Use standard beginUpdate/endUpdate on the layout.
34197      *  @deprecated
34198      */
34199     beginUpdate : function(){
34200         this.layout.beginUpdate();
34201     },
34202
34203     /**
34204      * Get the BorderLayout for this dialog
34205      * @return {Roo.BorderLayout}
34206      */
34207     getLayout : function(){
34208         return this.layout;
34209     },
34210
34211     showEl : function(){
34212         Roo.LayoutDialog.superclass.showEl.apply(this, arguments);
34213         if(Roo.isIE7){
34214             this.layout.layout();
34215         }
34216     },
34217
34218     // private
34219     // Use the syncHeightBeforeShow config option to control this automatically
34220     syncBodyHeight : function(){
34221         Roo.LayoutDialog.superclass.syncBodyHeight.call(this);
34222         if(this.layout){this.layout.layout();}
34223     },
34224     
34225       /**
34226      * Add an xtype element (actually adds to the layout.)
34227      * @return {Object} xdata xtype object data.
34228      */
34229     
34230     addxtype : function(c) {
34231         return this.layout.addxtype(c);
34232     }
34233 });/*
34234  * Based on:
34235  * Ext JS Library 1.1.1
34236  * Copyright(c) 2006-2007, Ext JS, LLC.
34237  *
34238  * Originally Released Under LGPL - original licence link has changed is not relivant.
34239  *
34240  * Fork - LGPL
34241  * <script type="text/javascript">
34242  */
34243  
34244 /**
34245  * @class Roo.MessageBox
34246  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
34247  * Example usage:
34248  *<pre><code>
34249 // Basic alert:
34250 Roo.Msg.alert('Status', 'Changes saved successfully.');
34251
34252 // Prompt for user data:
34253 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
34254     if (btn == 'ok'){
34255         // process text value...
34256     }
34257 });
34258
34259 // Show a dialog using config options:
34260 Roo.Msg.show({
34261    title:'Save Changes?',
34262    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
34263    buttons: Roo.Msg.YESNOCANCEL,
34264    fn: processResult,
34265    animEl: 'elId'
34266 });
34267 </code></pre>
34268  * @static
34269  */
34270 Roo.MessageBox = function(){
34271     var dlg, opt, mask, waitTimer;
34272     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
34273     var buttons, activeTextEl, bwidth;
34274
34275     // private
34276     var handleButton = function(button){
34277         dlg.hide();
34278         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
34279     };
34280
34281     // private
34282     var handleHide = function(){
34283         if(opt && opt.cls){
34284             dlg.el.removeClass(opt.cls);
34285         }
34286         if(waitTimer){
34287             Roo.TaskMgr.stop(waitTimer);
34288             waitTimer = null;
34289         }
34290     };
34291
34292     // private
34293     var updateButtons = function(b){
34294         var width = 0;
34295         if(!b){
34296             buttons["ok"].hide();
34297             buttons["cancel"].hide();
34298             buttons["yes"].hide();
34299             buttons["no"].hide();
34300             dlg.footer.dom.style.display = 'none';
34301             return width;
34302         }
34303         dlg.footer.dom.style.display = '';
34304         for(var k in buttons){
34305             if(typeof buttons[k] != "function"){
34306                 if(b[k]){
34307                     buttons[k].show();
34308                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.MessageBox.buttonText[k]);
34309                     width += buttons[k].el.getWidth()+15;
34310                 }else{
34311                     buttons[k].hide();
34312                 }
34313             }
34314         }
34315         return width;
34316     };
34317
34318     // private
34319     var handleEsc = function(d, k, e){
34320         if(opt && opt.closable !== false){
34321             dlg.hide();
34322         }
34323         if(e){
34324             e.stopEvent();
34325         }
34326     };
34327
34328     return {
34329         /**
34330          * Returns a reference to the underlying {@link Roo.BasicDialog} element
34331          * @return {Roo.BasicDialog} The BasicDialog element
34332          */
34333         getDialog : function(){
34334            if(!dlg){
34335                 dlg = new Roo.BasicDialog("x-msg-box", {
34336                     autoCreate : true,
34337                     shadow: true,
34338                     draggable: true,
34339                     resizable:false,
34340                     constraintoviewport:false,
34341                     fixedcenter:true,
34342                     collapsible : false,
34343                     shim:true,
34344                     modal: true,
34345                     width:400, height:100,
34346                     buttonAlign:"center",
34347                     closeClick : function(){
34348                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
34349                             handleButton("no");
34350                         }else{
34351                             handleButton("cancel");
34352                         }
34353                     }
34354                 });
34355                 dlg.on("hide", handleHide);
34356                 mask = dlg.mask;
34357                 dlg.addKeyListener(27, handleEsc);
34358                 buttons = {};
34359                 var bt = this.buttonText;
34360                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
34361                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
34362                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
34363                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
34364                 bodyEl = dlg.body.createChild({
34365
34366                     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>'
34367                 });
34368                 msgEl = bodyEl.dom.firstChild;
34369                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
34370                 textboxEl.enableDisplayMode();
34371                 textboxEl.addKeyListener([10,13], function(){
34372                     if(dlg.isVisible() && opt && opt.buttons){
34373                         if(opt.buttons.ok){
34374                             handleButton("ok");
34375                         }else if(opt.buttons.yes){
34376                             handleButton("yes");
34377                         }
34378                     }
34379                 });
34380                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
34381                 textareaEl.enableDisplayMode();
34382                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
34383                 progressEl.enableDisplayMode();
34384                 var pf = progressEl.dom.firstChild;
34385                 if (pf) {
34386                     pp = Roo.get(pf.firstChild);
34387                     pp.setHeight(pf.offsetHeight);
34388                 }
34389                 
34390             }
34391             return dlg;
34392         },
34393
34394         /**
34395          * Updates the message box body text
34396          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
34397          * the XHTML-compliant non-breaking space character '&amp;#160;')
34398          * @return {Roo.MessageBox} This message box
34399          */
34400         updateText : function(text){
34401             if(!dlg.isVisible() && !opt.width){
34402                 dlg.resizeTo(this.maxWidth, 100); // resize first so content is never clipped from previous shows
34403             }
34404             msgEl.innerHTML = text || '&#160;';
34405       
34406             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
34407             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
34408             var w = Math.max(
34409                     Math.min(opt.width || cw , this.maxWidth), 
34410                     Math.max(opt.minWidth || this.minWidth, bwidth)
34411             );
34412             if(opt.prompt){
34413                 activeTextEl.setWidth(w);
34414             }
34415             if(dlg.isVisible()){
34416                 dlg.fixedcenter = false;
34417             }
34418             // to big, make it scroll. = But as usual stupid IE does not support
34419             // !important..
34420             
34421             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
34422                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
34423                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
34424             } else {
34425                 bodyEl.dom.style.height = '';
34426                 bodyEl.dom.style.overflowY = '';
34427             }
34428             if (cw > w) {
34429                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
34430             } else {
34431                 bodyEl.dom.style.overflowX = '';
34432             }
34433             
34434             dlg.setContentSize(w, bodyEl.getHeight());
34435             if(dlg.isVisible()){
34436                 dlg.fixedcenter = true;
34437             }
34438             return this;
34439         },
34440
34441         /**
34442          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
34443          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
34444          * @param {Number} value Any number between 0 and 1 (e.g., .5)
34445          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
34446          * @return {Roo.MessageBox} This message box
34447          */
34448         updateProgress : function(value, text){
34449             if(text){
34450                 this.updateText(text);
34451             }
34452             if (pp) { // weird bug on my firefox - for some reason this is not defined
34453                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
34454             }
34455             return this;
34456         },        
34457
34458         /**
34459          * Returns true if the message box is currently displayed
34460          * @return {Boolean} True if the message box is visible, else false
34461          */
34462         isVisible : function(){
34463             return dlg && dlg.isVisible();  
34464         },
34465
34466         /**
34467          * Hides the message box if it is displayed
34468          */
34469         hide : function(){
34470             if(this.isVisible()){
34471                 dlg.hide();
34472             }  
34473         },
34474
34475         /**
34476          * Displays a new message box, or reinitializes an existing message box, based on the config options
34477          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
34478          * The following config object properties are supported:
34479          * <pre>
34480 Property    Type             Description
34481 ----------  ---------------  ------------------------------------------------------------------------------------
34482 animEl            String/Element   An id or Element from which the message box should animate as it opens and
34483                                    closes (defaults to undefined)
34484 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
34485                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
34486 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
34487                                    progress and wait dialogs will ignore this property and always hide the
34488                                    close button as they can only be closed programmatically.
34489 cls               String           A custom CSS class to apply to the message box element
34490 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
34491                                    displayed (defaults to 75)
34492 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
34493                                    function will be btn (the name of the button that was clicked, if applicable,
34494                                    e.g. "ok"), and text (the value of the active text field, if applicable).
34495                                    Progress and wait dialogs will ignore this option since they do not respond to
34496                                    user actions and can only be closed programmatically, so any required function
34497                                    should be called by the same code after it closes the dialog.
34498 icon              String           A CSS class that provides a background image to be used as an icon for
34499                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
34500 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
34501 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
34502 modal             Boolean          False to allow user interaction with the page while the message box is
34503                                    displayed (defaults to true)
34504 msg               String           A string that will replace the existing message box body text (defaults
34505                                    to the XHTML-compliant non-breaking space character '&#160;')
34506 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
34507 progress          Boolean          True to display a progress bar (defaults to false)
34508 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
34509 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
34510 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
34511 title             String           The title text
34512 value             String           The string value to set into the active textbox element if displayed
34513 wait              Boolean          True to display a progress bar (defaults to false)
34514 width             Number           The width of the dialog in pixels
34515 </pre>
34516          *
34517          * Example usage:
34518          * <pre><code>
34519 Roo.Msg.show({
34520    title: 'Address',
34521    msg: 'Please enter your address:',
34522    width: 300,
34523    buttons: Roo.MessageBox.OKCANCEL,
34524    multiline: true,
34525    fn: saveAddress,
34526    animEl: 'addAddressBtn'
34527 });
34528 </code></pre>
34529          * @param {Object} config Configuration options
34530          * @return {Roo.MessageBox} This message box
34531          */
34532         show : function(options)
34533         {
34534             
34535             // this causes nightmares if you show one dialog after another
34536             // especially on callbacks..
34537              
34538             if(this.isVisible()){
34539                 
34540                 this.hide();
34541                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
34542                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
34543                 Roo.log("New Dialog Message:" +  options.msg )
34544                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
34545                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
34546                 
34547             }
34548             var d = this.getDialog();
34549             opt = options;
34550             d.setTitle(opt.title || "&#160;");
34551             d.close.setDisplayed(opt.closable !== false);
34552             activeTextEl = textboxEl;
34553             opt.prompt = opt.prompt || (opt.multiline ? true : false);
34554             if(opt.prompt){
34555                 if(opt.multiline){
34556                     textboxEl.hide();
34557                     textareaEl.show();
34558                     textareaEl.setHeight(typeof opt.multiline == "number" ?
34559                         opt.multiline : this.defaultTextHeight);
34560                     activeTextEl = textareaEl;
34561                 }else{
34562                     textboxEl.show();
34563                     textareaEl.hide();
34564                 }
34565             }else{
34566                 textboxEl.hide();
34567                 textareaEl.hide();
34568             }
34569             progressEl.setDisplayed(opt.progress === true);
34570             this.updateProgress(0);
34571             activeTextEl.dom.value = opt.value || "";
34572             if(opt.prompt){
34573                 dlg.setDefaultButton(activeTextEl);
34574             }else{
34575                 var bs = opt.buttons;
34576                 var db = null;
34577                 if(bs && bs.ok){
34578                     db = buttons["ok"];
34579                 }else if(bs && bs.yes){
34580                     db = buttons["yes"];
34581                 }
34582                 dlg.setDefaultButton(db);
34583             }
34584             bwidth = updateButtons(opt.buttons);
34585             this.updateText(opt.msg);
34586             if(opt.cls){
34587                 d.el.addClass(opt.cls);
34588             }
34589             d.proxyDrag = opt.proxyDrag === true;
34590             d.modal = opt.modal !== false;
34591             d.mask = opt.modal !== false ? mask : false;
34592             if(!d.isVisible()){
34593                 // force it to the end of the z-index stack so it gets a cursor in FF
34594                 document.body.appendChild(dlg.el.dom);
34595                 d.animateTarget = null;
34596                 d.show(options.animEl);
34597             }
34598             return this;
34599         },
34600
34601         /**
34602          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
34603          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
34604          * and closing the message box when the process is complete.
34605          * @param {String} title The title bar text
34606          * @param {String} msg The message box body text
34607          * @return {Roo.MessageBox} This message box
34608          */
34609         progress : function(title, msg){
34610             this.show({
34611                 title : title,
34612                 msg : msg,
34613                 buttons: false,
34614                 progress:true,
34615                 closable:false,
34616                 minWidth: this.minProgressWidth,
34617                 modal : true
34618             });
34619             return this;
34620         },
34621
34622         /**
34623          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
34624          * If a callback function is passed it will be called after the user clicks the button, and the
34625          * id of the button that was clicked will be passed as the only parameter to the callback
34626          * (could also be the top-right close button).
34627          * @param {String} title The title bar text
34628          * @param {String} msg The message box body text
34629          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34630          * @param {Object} scope (optional) The scope of the callback function
34631          * @return {Roo.MessageBox} This message box
34632          */
34633         alert : function(title, msg, fn, scope){
34634             this.show({
34635                 title : title,
34636                 msg : msg,
34637                 buttons: this.OK,
34638                 fn: fn,
34639                 scope : scope,
34640                 modal : true
34641             });
34642             return this;
34643         },
34644
34645         /**
34646          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
34647          * interaction while waiting for a long-running process to complete that does not have defined intervals.
34648          * You are responsible for closing the message box when the process is complete.
34649          * @param {String} msg The message box body text
34650          * @param {String} title (optional) The title bar text
34651          * @return {Roo.MessageBox} This message box
34652          */
34653         wait : function(msg, title){
34654             this.show({
34655                 title : title,
34656                 msg : msg,
34657                 buttons: false,
34658                 closable:false,
34659                 progress:true,
34660                 modal:true,
34661                 width:300,
34662                 wait:true
34663             });
34664             waitTimer = Roo.TaskMgr.start({
34665                 run: function(i){
34666                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
34667                 },
34668                 interval: 1000
34669             });
34670             return this;
34671         },
34672
34673         /**
34674          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
34675          * If a callback function is passed it will be called after the user clicks either button, and the id of the
34676          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
34677          * @param {String} title The title bar text
34678          * @param {String} msg The message box body text
34679          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34680          * @param {Object} scope (optional) The scope of the callback function
34681          * @return {Roo.MessageBox} This message box
34682          */
34683         confirm : function(title, msg, fn, scope){
34684             this.show({
34685                 title : title,
34686                 msg : msg,
34687                 buttons: this.YESNO,
34688                 fn: fn,
34689                 scope : scope,
34690                 modal : true
34691             });
34692             return this;
34693         },
34694
34695         /**
34696          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
34697          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
34698          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
34699          * (could also be the top-right close button) and the text that was entered will be passed as the two
34700          * parameters to the callback.
34701          * @param {String} title The title bar text
34702          * @param {String} msg The message box body text
34703          * @param {Function} fn (optional) The callback function invoked after the message box is closed
34704          * @param {Object} scope (optional) The scope of the callback function
34705          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
34706          * property, or the height in pixels to create the textbox (defaults to false / single-line)
34707          * @return {Roo.MessageBox} This message box
34708          */
34709         prompt : function(title, msg, fn, scope, multiline){
34710             this.show({
34711                 title : title,
34712                 msg : msg,
34713                 buttons: this.OKCANCEL,
34714                 fn: fn,
34715                 minWidth:250,
34716                 scope : scope,
34717                 prompt:true,
34718                 multiline: multiline,
34719                 modal : true
34720             });
34721             return this;
34722         },
34723
34724         /**
34725          * Button config that displays a single OK button
34726          * @type Object
34727          */
34728         OK : {ok:true},
34729         /**
34730          * Button config that displays Yes and No buttons
34731          * @type Object
34732          */
34733         YESNO : {yes:true, no:true},
34734         /**
34735          * Button config that displays OK and Cancel buttons
34736          * @type Object
34737          */
34738         OKCANCEL : {ok:true, cancel:true},
34739         /**
34740          * Button config that displays Yes, No and Cancel buttons
34741          * @type Object
34742          */
34743         YESNOCANCEL : {yes:true, no:true, cancel:true},
34744
34745         /**
34746          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
34747          * @type Number
34748          */
34749         defaultTextHeight : 75,
34750         /**
34751          * The maximum width in pixels of the message box (defaults to 600)
34752          * @type Number
34753          */
34754         maxWidth : 600,
34755         /**
34756          * The minimum width in pixels of the message box (defaults to 100)
34757          * @type Number
34758          */
34759         minWidth : 100,
34760         /**
34761          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
34762          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
34763          * @type Number
34764          */
34765         minProgressWidth : 250,
34766         /**
34767          * An object containing the default button text strings that can be overriden for localized language support.
34768          * Supported properties are: ok, cancel, yes and no.
34769          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
34770          * @type Object
34771          */
34772         buttonText : {
34773             ok : "OK",
34774             cancel : "Cancel",
34775             yes : "Yes",
34776             no : "No"
34777         }
34778     };
34779 }();
34780
34781 /**
34782  * Shorthand for {@link Roo.MessageBox}
34783  */
34784 Roo.Msg = Roo.MessageBox;/*
34785  * Based on:
34786  * Ext JS Library 1.1.1
34787  * Copyright(c) 2006-2007, Ext JS, LLC.
34788  *
34789  * Originally Released Under LGPL - original licence link has changed is not relivant.
34790  *
34791  * Fork - LGPL
34792  * <script type="text/javascript">
34793  */
34794 /**
34795  * @class Roo.QuickTips
34796  * Provides attractive and customizable tooltips for any element.
34797  * @static
34798  */
34799 Roo.QuickTips = function(){
34800     var el, tipBody, tipBodyText, tipTitle, tm, cfg, close, tagEls = {}, esc, removeCls = null, bdLeft, bdRight;
34801     var ce, bd, xy, dd;
34802     var visible = false, disabled = true, inited = false;
34803     var showProc = 1, hideProc = 1, dismissProc = 1, locks = [];
34804     
34805     var onOver = function(e){
34806         if(disabled){
34807             return;
34808         }
34809         var t = e.getTarget();
34810         if(!t || t.nodeType !== 1 || t == document || t == document.body){
34811             return;
34812         }
34813         if(ce && t == ce.el){
34814             clearTimeout(hideProc);
34815             return;
34816         }
34817         if(t && tagEls[t.id]){
34818             tagEls[t.id].el = t;
34819             showProc = show.defer(tm.showDelay, tm, [tagEls[t.id]]);
34820             return;
34821         }
34822         var ttp, et = Roo.fly(t);
34823         var ns = cfg.namespace;
34824         if(tm.interceptTitles && t.title){
34825             ttp = t.title;
34826             t.qtip = ttp;
34827             t.removeAttribute("title");
34828             e.preventDefault();
34829         }else{
34830             ttp = t.qtip || et.getAttributeNS(ns, cfg.attribute) || et.getAttributeNS(cfg.alt_namespace, cfg.attribute) ;
34831         }
34832         if(ttp){
34833             showProc = show.defer(tm.showDelay, tm, [{
34834                 el: t, 
34835                 text: ttp.replace(/\\n/g,'<br/>'),
34836                 width: et.getAttributeNS(ns, cfg.width),
34837                 autoHide: et.getAttributeNS(ns, cfg.hide) != "user",
34838                 title: et.getAttributeNS(ns, cfg.title),
34839                     cls: et.getAttributeNS(ns, cfg.cls)
34840             }]);
34841         }
34842     };
34843     
34844     var onOut = function(e){
34845         clearTimeout(showProc);
34846         var t = e.getTarget();
34847         if(t && ce && ce.el == t && (tm.autoHide && ce.autoHide !== false)){
34848             hideProc = setTimeout(hide, tm.hideDelay);
34849         }
34850     };
34851     
34852     var onMove = function(e){
34853         if(disabled){
34854             return;
34855         }
34856         xy = e.getXY();
34857         xy[1] += 18;
34858         if(tm.trackMouse && ce){
34859             el.setXY(xy);
34860         }
34861     };
34862     
34863     var onDown = function(e){
34864         clearTimeout(showProc);
34865         clearTimeout(hideProc);
34866         if(!e.within(el)){
34867             if(tm.hideOnClick){
34868                 hide();
34869                 tm.disable();
34870                 tm.enable.defer(100, tm);
34871             }
34872         }
34873     };
34874     
34875     var getPad = function(){
34876         return 2;//bdLeft.getPadding('l')+bdRight.getPadding('r');
34877     };
34878
34879     var show = function(o){
34880         if(disabled){
34881             return;
34882         }
34883         clearTimeout(dismissProc);
34884         ce = o;
34885         if(removeCls){ // in case manually hidden
34886             el.removeClass(removeCls);
34887             removeCls = null;
34888         }
34889         if(ce.cls){
34890             el.addClass(ce.cls);
34891             removeCls = ce.cls;
34892         }
34893         if(ce.title){
34894             tipTitle.update(ce.title);
34895             tipTitle.show();
34896         }else{
34897             tipTitle.update('');
34898             tipTitle.hide();
34899         }
34900         el.dom.style.width  = tm.maxWidth+'px';
34901         //tipBody.dom.style.width = '';
34902         tipBodyText.update(o.text);
34903         var p = getPad(), w = ce.width;
34904         if(!w){
34905             var td = tipBodyText.dom;
34906             var aw = Math.max(td.offsetWidth, td.clientWidth, td.scrollWidth);
34907             if(aw > tm.maxWidth){
34908                 w = tm.maxWidth;
34909             }else if(aw < tm.minWidth){
34910                 w = tm.minWidth;
34911             }else{
34912                 w = aw;
34913             }
34914         }
34915         //tipBody.setWidth(w);
34916         el.setWidth(parseInt(w, 10) + p);
34917         if(ce.autoHide === false){
34918             close.setDisplayed(true);
34919             if(dd){
34920                 dd.unlock();
34921             }
34922         }else{
34923             close.setDisplayed(false);
34924             if(dd){
34925                 dd.lock();
34926             }
34927         }
34928         if(xy){
34929             el.avoidY = xy[1]-18;
34930             el.setXY(xy);
34931         }
34932         if(tm.animate){
34933             el.setOpacity(.1);
34934             el.setStyle("visibility", "visible");
34935             el.fadeIn({callback: afterShow});
34936         }else{
34937             afterShow();
34938         }
34939     };
34940     
34941     var afterShow = function(){
34942         if(ce){
34943             el.show();
34944             esc.enable();
34945             if(tm.autoDismiss && ce.autoHide !== false){
34946                 dismissProc = setTimeout(hide, tm.autoDismissDelay);
34947             }
34948         }
34949     };
34950     
34951     var hide = function(noanim){
34952         clearTimeout(dismissProc);
34953         clearTimeout(hideProc);
34954         ce = null;
34955         if(el.isVisible()){
34956             esc.disable();
34957             if(noanim !== true && tm.animate){
34958                 el.fadeOut({callback: afterHide});
34959             }else{
34960                 afterHide();
34961             } 
34962         }
34963     };
34964     
34965     var afterHide = function(){
34966         el.hide();
34967         if(removeCls){
34968             el.removeClass(removeCls);
34969             removeCls = null;
34970         }
34971     };
34972     
34973     return {
34974         /**
34975         * @cfg {Number} minWidth
34976         * The minimum width of the quick tip (defaults to 40)
34977         */
34978        minWidth : 40,
34979         /**
34980         * @cfg {Number} maxWidth
34981         * The maximum width of the quick tip (defaults to 300)
34982         */
34983        maxWidth : 300,
34984         /**
34985         * @cfg {Boolean} interceptTitles
34986         * True to automatically use the element's DOM title value if available (defaults to false)
34987         */
34988        interceptTitles : false,
34989         /**
34990         * @cfg {Boolean} trackMouse
34991         * True to have the quick tip follow the mouse as it moves over the target element (defaults to false)
34992         */
34993        trackMouse : false,
34994         /**
34995         * @cfg {Boolean} hideOnClick
34996         * True to hide the quick tip if the user clicks anywhere in the document (defaults to true)
34997         */
34998        hideOnClick : true,
34999         /**
35000         * @cfg {Number} showDelay
35001         * Delay in milliseconds before the quick tip displays after the mouse enters the target element (defaults to 500)
35002         */
35003        showDelay : 500,
35004         /**
35005         * @cfg {Number} hideDelay
35006         * Delay in milliseconds before the quick tip hides when autoHide = true (defaults to 200)
35007         */
35008        hideDelay : 200,
35009         /**
35010         * @cfg {Boolean} autoHide
35011         * True to automatically hide the quick tip after the mouse exits the target element (defaults to true).
35012         * Used in conjunction with hideDelay.
35013         */
35014        autoHide : true,
35015         /**
35016         * @cfg {Boolean}
35017         * True to automatically hide the quick tip after a set period of time, regardless of the user's actions
35018         * (defaults to true).  Used in conjunction with autoDismissDelay.
35019         */
35020        autoDismiss : true,
35021         /**
35022         * @cfg {Number}
35023         * Delay in milliseconds before the quick tip hides when autoDismiss = true (defaults to 5000)
35024         */
35025        autoDismissDelay : 5000,
35026        /**
35027         * @cfg {Boolean} animate
35028         * True to turn on fade animation. Defaults to false (ClearType/scrollbar flicker issues in IE7).
35029         */
35030        animate : false,
35031
35032        /**
35033         * @cfg {String} title
35034         * Title text to display (defaults to '').  This can be any valid HTML markup.
35035         */
35036         title: '',
35037        /**
35038         * @cfg {String} text
35039         * Body text to display (defaults to '').  This can be any valid HTML markup.
35040         */
35041         text : '',
35042        /**
35043         * @cfg {String} cls
35044         * A CSS class to apply to the base quick tip element (defaults to '').
35045         */
35046         cls : '',
35047        /**
35048         * @cfg {Number} width
35049         * Width in pixels of the quick tip (defaults to auto).  Width will be ignored if it exceeds the bounds of
35050         * minWidth or maxWidth.
35051         */
35052         width : null,
35053
35054     /**
35055      * Initialize and enable QuickTips for first use.  This should be called once before the first attempt to access
35056      * or display QuickTips in a page.
35057      */
35058        init : function(){
35059           tm = Roo.QuickTips;
35060           cfg = tm.tagConfig;
35061           if(!inited){
35062               if(!Roo.isReady){ // allow calling of init() before onReady
35063                   Roo.onReady(Roo.QuickTips.init, Roo.QuickTips);
35064                   return;
35065               }
35066               el = new Roo.Layer({cls:"x-tip", shadow:"drop", shim: true, constrain:true, shadowOffset:4});
35067               el.fxDefaults = {stopFx: true};
35068               // maximum custom styling
35069               //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>');
35070               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>');              
35071               tipTitle = el.child('h3');
35072               tipTitle.enableDisplayMode("block");
35073               tipBody = el.child('div.x-tip-bd');
35074               tipBodyText = el.child('div.x-tip-bd-inner');
35075               //bdLeft = el.child('div.x-tip-bd-left');
35076               //bdRight = el.child('div.x-tip-bd-right');
35077               close = el.child('div.x-tip-close');
35078               close.enableDisplayMode("block");
35079               close.on("click", hide);
35080               var d = Roo.get(document);
35081               d.on("mousedown", onDown);
35082               d.on("mouseover", onOver);
35083               d.on("mouseout", onOut);
35084               d.on("mousemove", onMove);
35085               esc = d.addKeyListener(27, hide);
35086               esc.disable();
35087               if(Roo.dd.DD){
35088                   dd = el.initDD("default", null, {
35089                       onDrag : function(){
35090                           el.sync();  
35091                       }
35092                   });
35093                   dd.setHandleElId(tipTitle.id);
35094                   dd.lock();
35095               }
35096               inited = true;
35097           }
35098           this.enable(); 
35099        },
35100
35101     /**
35102      * Configures a new quick tip instance and assigns it to a target element.  The following config options
35103      * are supported:
35104      * <pre>
35105 Property    Type                   Description
35106 ----------  ---------------------  ------------------------------------------------------------------------
35107 target      Element/String/Array   An Element, id or array of ids that this quick tip should be tied to
35108      * </ul>
35109      * @param {Object} config The config object
35110      */
35111        register : function(config){
35112            var cs = config instanceof Array ? config : arguments;
35113            for(var i = 0, len = cs.length; i < len; i++) {
35114                var c = cs[i];
35115                var target = c.target;
35116                if(target){
35117                    if(target instanceof Array){
35118                        for(var j = 0, jlen = target.length; j < jlen; j++){
35119                            tagEls[target[j]] = c;
35120                        }
35121                    }else{
35122                        tagEls[typeof target == 'string' ? target : Roo.id(target)] = c;
35123                    }
35124                }
35125            }
35126        },
35127
35128     /**
35129      * Removes this quick tip from its element and destroys it.
35130      * @param {String/HTMLElement/Element} el The element from which the quick tip is to be removed.
35131      */
35132        unregister : function(el){
35133            delete tagEls[Roo.id(el)];
35134        },
35135
35136     /**
35137      * Enable this quick tip.
35138      */
35139        enable : function(){
35140            if(inited && disabled){
35141                locks.pop();
35142                if(locks.length < 1){
35143                    disabled = false;
35144                }
35145            }
35146        },
35147
35148     /**
35149      * Disable this quick tip.
35150      */
35151        disable : function(){
35152           disabled = true;
35153           clearTimeout(showProc);
35154           clearTimeout(hideProc);
35155           clearTimeout(dismissProc);
35156           if(ce){
35157               hide(true);
35158           }
35159           locks.push(1);
35160        },
35161
35162     /**
35163      * Returns true if the quick tip is enabled, else false.
35164      */
35165        isEnabled : function(){
35166             return !disabled;
35167        },
35168
35169         // private
35170        tagConfig : {
35171            namespace : "roo", // was ext?? this may break..
35172            alt_namespace : "ext",
35173            attribute : "qtip",
35174            width : "width",
35175            target : "target",
35176            title : "qtitle",
35177            hide : "hide",
35178            cls : "qclass"
35179        }
35180    };
35181 }();
35182
35183 // backwards compat
35184 Roo.QuickTips.tips = Roo.QuickTips.register;/*
35185  * Based on:
35186  * Ext JS Library 1.1.1
35187  * Copyright(c) 2006-2007, Ext JS, LLC.
35188  *
35189  * Originally Released Under LGPL - original licence link has changed is not relivant.
35190  *
35191  * Fork - LGPL
35192  * <script type="text/javascript">
35193  */
35194  
35195
35196 /**
35197  * @class Roo.tree.TreePanel
35198  * @extends Roo.data.Tree
35199  * @cfg {Roo.tree.TreeNode} root The root node
35200  * @cfg {Boolean} rootVisible false to hide the root node (defaults to true)
35201  * @cfg {Boolean} lines false to disable tree lines (defaults to true)
35202  * @cfg {Boolean} enableDD true to enable drag and drop
35203  * @cfg {Boolean} enableDrag true to enable just drag
35204  * @cfg {Boolean} enableDrop true to enable just drop
35205  * @cfg {Object} dragConfig Custom config to pass to the {@link Roo.tree.TreeDragZone} instance
35206  * @cfg {Object} dropConfig Custom config to pass to the {@link Roo.tree.TreeDropZone} instance
35207  * @cfg {String} ddGroup The DD group this TreePanel belongs to
35208  * @cfg {String} ddAppendOnly True if the tree should only allow append drops (use for trees which are sorted)
35209  * @cfg {Boolean} ddScroll true to enable YUI body scrolling
35210  * @cfg {Boolean} containerScroll true to register this container with ScrollManager
35211  * @cfg {Boolean} hlDrop false to disable node highlight on drop (defaults to the value of Roo.enableFx)
35212  * @cfg {String} hlColor The color of the node highlight (defaults to C3DAF9)
35213  * @cfg {Boolean} animate true to enable animated expand/collapse (defaults to the value of Roo.enableFx)
35214  * @cfg {Boolean} singleExpand true if only 1 node per branch may be expanded
35215  * @cfg {Boolean} selModel A tree selection model to use with this TreePanel (defaults to a {@link Roo.tree.DefaultSelectionModel})
35216  * @cfg {Roo.tree.TreeLoader} loader A TreeLoader for use with this TreePanel
35217  * @cfg {Roo.tree.TreeEditor} editor The TreeEditor to display when clicked.
35218  * @cfg {String} pathSeparator The token used to separate sub-paths in path strings (defaults to '/')
35219  * @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>
35220  * @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>
35221  * 
35222  * @constructor
35223  * @param {String/HTMLElement/Element} el The container element
35224  * @param {Object} config
35225  */
35226 Roo.tree.TreePanel = function(el, config){
35227     var root = false;
35228     var loader = false;
35229     if (config.root) {
35230         root = config.root;
35231         delete config.root;
35232     }
35233     if (config.loader) {
35234         loader = config.loader;
35235         delete config.loader;
35236     }
35237     
35238     Roo.apply(this, config);
35239     Roo.tree.TreePanel.superclass.constructor.call(this);
35240     this.el = Roo.get(el);
35241     this.el.addClass('x-tree');
35242     //console.log(root);
35243     if (root) {
35244         this.setRootNode( Roo.factory(root, Roo.tree));
35245     }
35246     if (loader) {
35247         this.loader = Roo.factory(loader, Roo.tree);
35248     }
35249    /**
35250     * Read-only. The id of the container element becomes this TreePanel's id.
35251     */
35252     this.id = this.el.id;
35253     this.addEvents({
35254         /**
35255         * @event beforeload
35256         * Fires before a node is loaded, return false to cancel
35257         * @param {Node} node The node being loaded
35258         */
35259         "beforeload" : true,
35260         /**
35261         * @event load
35262         * Fires when a node is loaded
35263         * @param {Node} node The node that was loaded
35264         */
35265         "load" : true,
35266         /**
35267         * @event textchange
35268         * Fires when the text for a node is changed
35269         * @param {Node} node The node
35270         * @param {String} text The new text
35271         * @param {String} oldText The old text
35272         */
35273         "textchange" : true,
35274         /**
35275         * @event beforeexpand
35276         * Fires before a node is expanded, return false to cancel.
35277         * @param {Node} node The node
35278         * @param {Boolean} deep
35279         * @param {Boolean} anim
35280         */
35281         "beforeexpand" : true,
35282         /**
35283         * @event beforecollapse
35284         * Fires before a node is collapsed, return false to cancel.
35285         * @param {Node} node The node
35286         * @param {Boolean} deep
35287         * @param {Boolean} anim
35288         */
35289         "beforecollapse" : true,
35290         /**
35291         * @event expand
35292         * Fires when a node is expanded
35293         * @param {Node} node The node
35294         */
35295         "expand" : true,
35296         /**
35297         * @event disabledchange
35298         * Fires when the disabled status of a node changes
35299         * @param {Node} node The node
35300         * @param {Boolean} disabled
35301         */
35302         "disabledchange" : true,
35303         /**
35304         * @event collapse
35305         * Fires when a node is collapsed
35306         * @param {Node} node The node
35307         */
35308         "collapse" : true,
35309         /**
35310         * @event beforeclick
35311         * Fires before click processing on a node. Return false to cancel the default action.
35312         * @param {Node} node The node
35313         * @param {Roo.EventObject} e The event object
35314         */
35315         "beforeclick":true,
35316         /**
35317         * @event checkchange
35318         * Fires when a node with a checkbox's checked property changes
35319         * @param {Node} this This node
35320         * @param {Boolean} checked
35321         */
35322         "checkchange":true,
35323         /**
35324         * @event click
35325         * Fires when a node is clicked
35326         * @param {Node} node The node
35327         * @param {Roo.EventObject} e The event object
35328         */
35329         "click":true,
35330         /**
35331         * @event dblclick
35332         * Fires when a node is double clicked
35333         * @param {Node} node The node
35334         * @param {Roo.EventObject} e The event object
35335         */
35336         "dblclick":true,
35337         /**
35338         * @event contextmenu
35339         * Fires when a node is right clicked
35340         * @param {Node} node The node
35341         * @param {Roo.EventObject} e The event object
35342         */
35343         "contextmenu":true,
35344         /**
35345         * @event beforechildrenrendered
35346         * Fires right before the child nodes for a node are rendered
35347         * @param {Node} node The node
35348         */
35349         "beforechildrenrendered":true,
35350         /**
35351         * @event startdrag
35352         * Fires when a node starts being dragged
35353         * @param {Roo.tree.TreePanel} this
35354         * @param {Roo.tree.TreeNode} node
35355         * @param {event} e The raw browser event
35356         */ 
35357        "startdrag" : true,
35358        /**
35359         * @event enddrag
35360         * Fires when a drag operation is complete
35361         * @param {Roo.tree.TreePanel} this
35362         * @param {Roo.tree.TreeNode} node
35363         * @param {event} e The raw browser event
35364         */
35365        "enddrag" : true,
35366        /**
35367         * @event dragdrop
35368         * Fires when a dragged node is dropped on a valid DD target
35369         * @param {Roo.tree.TreePanel} this
35370         * @param {Roo.tree.TreeNode} node
35371         * @param {DD} dd The dd it was dropped on
35372         * @param {event} e The raw browser event
35373         */
35374        "dragdrop" : true,
35375        /**
35376         * @event beforenodedrop
35377         * Fires when a DD object is dropped on a node in this tree for preprocessing. Return false to cancel the drop. The dropEvent
35378         * passed to handlers has the following properties:<br />
35379         * <ul style="padding:5px;padding-left:16px;">
35380         * <li>tree - The TreePanel</li>
35381         * <li>target - The node being targeted for the drop</li>
35382         * <li>data - The drag data from the drag source</li>
35383         * <li>point - The point of the drop - append, above or below</li>
35384         * <li>source - The drag source</li>
35385         * <li>rawEvent - Raw mouse event</li>
35386         * <li>dropNode - Drop node(s) provided by the source <b>OR</b> you can supply node(s)
35387         * to be inserted by setting them on this object.</li>
35388         * <li>cancel - Set this to true to cancel the drop.</li>
35389         * </ul>
35390         * @param {Object} dropEvent
35391         */
35392        "beforenodedrop" : true,
35393        /**
35394         * @event nodedrop
35395         * Fires after a DD object is dropped on a node in this tree. The dropEvent
35396         * passed to handlers has the following properties:<br />
35397         * <ul style="padding:5px;padding-left:16px;">
35398         * <li>tree - The TreePanel</li>
35399         * <li>target - The node being targeted for the drop</li>
35400         * <li>data - The drag data from the drag source</li>
35401         * <li>point - The point of the drop - append, above or below</li>
35402         * <li>source - The drag source</li>
35403         * <li>rawEvent - Raw mouse event</li>
35404         * <li>dropNode - Dropped node(s).</li>
35405         * </ul>
35406         * @param {Object} dropEvent
35407         */
35408        "nodedrop" : true,
35409         /**
35410         * @event nodedragover
35411         * Fires when a tree node is being targeted for a drag drop, return false to signal drop not allowed. The dragOverEvent
35412         * passed to handlers has the following properties:<br />
35413         * <ul style="padding:5px;padding-left:16px;">
35414         * <li>tree - The TreePanel</li>
35415         * <li>target - The node being targeted for the drop</li>
35416         * <li>data - The drag data from the drag source</li>
35417         * <li>point - The point of the drop - append, above or below</li>
35418         * <li>source - The drag source</li>
35419         * <li>rawEvent - Raw mouse event</li>
35420         * <li>dropNode - Drop node(s) provided by the source.</li>
35421         * <li>cancel - Set this to true to signal drop not allowed.</li>
35422         * </ul>
35423         * @param {Object} dragOverEvent
35424         */
35425        "nodedragover" : true,
35426        /**
35427         * @event appendnode
35428         * Fires when append node to the tree
35429         * @param {Roo.tree.TreePanel} this
35430         * @param {Roo.tree.TreeNode} node
35431         * @param {Number} index The index of the newly appended node
35432         */
35433        "appendnode" : true
35434         
35435     });
35436     if(this.singleExpand){
35437        this.on("beforeexpand", this.restrictExpand, this);
35438     }
35439     if (this.editor) {
35440         this.editor.tree = this;
35441         this.editor = Roo.factory(this.editor, Roo.tree);
35442     }
35443     
35444     if (this.selModel) {
35445         this.selModel = Roo.factory(this.selModel, Roo.tree);
35446     }
35447    
35448 };
35449 Roo.extend(Roo.tree.TreePanel, Roo.data.Tree, {
35450     rootVisible : true,
35451     animate: Roo.enableFx,
35452     lines : true,
35453     enableDD : false,
35454     hlDrop : Roo.enableFx,
35455   
35456     renderer: false,
35457     
35458     rendererTip: false,
35459     // private
35460     restrictExpand : function(node){
35461         var p = node.parentNode;
35462         if(p){
35463             if(p.expandedChild && p.expandedChild.parentNode == p){
35464                 p.expandedChild.collapse();
35465             }
35466             p.expandedChild = node;
35467         }
35468     },
35469
35470     // private override
35471     setRootNode : function(node){
35472         Roo.tree.TreePanel.superclass.setRootNode.call(this, node);
35473         if(!this.rootVisible){
35474             node.ui = new Roo.tree.RootTreeNodeUI(node);
35475         }
35476         return node;
35477     },
35478
35479     /**
35480      * Returns the container element for this TreePanel
35481      */
35482     getEl : function(){
35483         return this.el;
35484     },
35485
35486     /**
35487      * Returns the default TreeLoader for this TreePanel
35488      */
35489     getLoader : function(){
35490         return this.loader;
35491     },
35492
35493     /**
35494      * Expand all nodes
35495      */
35496     expandAll : function(){
35497         this.root.expand(true);
35498     },
35499
35500     /**
35501      * Collapse all nodes
35502      */
35503     collapseAll : function(){
35504         this.root.collapse(true);
35505     },
35506
35507     /**
35508      * Returns the selection model used by this TreePanel
35509      */
35510     getSelectionModel : function(){
35511         if(!this.selModel){
35512             this.selModel = new Roo.tree.DefaultSelectionModel();
35513         }
35514         return this.selModel;
35515     },
35516
35517     /**
35518      * Retrieve an array of checked nodes, or an array of a specific attribute of checked nodes (e.g. "id")
35519      * @param {String} attribute (optional) Defaults to null (return the actual nodes)
35520      * @param {TreeNode} startNode (optional) The node to start from, defaults to the root
35521      * @return {Array}
35522      */
35523     getChecked : function(a, startNode){
35524         startNode = startNode || this.root;
35525         var r = [];
35526         var f = function(){
35527             if(this.attributes.checked){
35528                 r.push(!a ? this : (a == 'id' ? this.id : this.attributes[a]));
35529             }
35530         }
35531         startNode.cascade(f);
35532         return r;
35533     },
35534
35535     /**
35536      * Expands a specified path in this TreePanel. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35537      * @param {String} path
35538      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35539      * @param {Function} callback (optional) The callback to call when the expand is complete. The callback will be called with
35540      * (bSuccess, oLastNode) where bSuccess is if the expand was successful and oLastNode is the last node that was expanded.
35541      */
35542     expandPath : function(path, attr, callback){
35543         attr = attr || "id";
35544         var keys = path.split(this.pathSeparator);
35545         var curNode = this.root;
35546         if(curNode.attributes[attr] != keys[1]){ // invalid root
35547             if(callback){
35548                 callback(false, null);
35549             }
35550             return;
35551         }
35552         var index = 1;
35553         var f = function(){
35554             if(++index == keys.length){
35555                 if(callback){
35556                     callback(true, curNode);
35557                 }
35558                 return;
35559             }
35560             var c = curNode.findChild(attr, keys[index]);
35561             if(!c){
35562                 if(callback){
35563                     callback(false, curNode);
35564                 }
35565                 return;
35566             }
35567             curNode = c;
35568             c.expand(false, false, f);
35569         };
35570         curNode.expand(false, false, f);
35571     },
35572
35573     /**
35574      * Selects the node in this tree at the specified path. A path can be retrieved from a node with {@link Roo.data.Node#getPath}
35575      * @param {String} path
35576      * @param {String} attr (optional) The attribute used in the path (see {@link Roo.data.Node#getPath} for more info)
35577      * @param {Function} callback (optional) The callback to call when the selection is complete. The callback will be called with
35578      * (bSuccess, oSelNode) where bSuccess is if the selection was successful and oSelNode is the selected node.
35579      */
35580     selectPath : function(path, attr, callback){
35581         attr = attr || "id";
35582         var keys = path.split(this.pathSeparator);
35583         var v = keys.pop();
35584         if(keys.length > 0){
35585             var f = function(success, node){
35586                 if(success && node){
35587                     var n = node.findChild(attr, v);
35588                     if(n){
35589                         n.select();
35590                         if(callback){
35591                             callback(true, n);
35592                         }
35593                     }else if(callback){
35594                         callback(false, n);
35595                     }
35596                 }else{
35597                     if(callback){
35598                         callback(false, n);
35599                     }
35600                 }
35601             };
35602             this.expandPath(keys.join(this.pathSeparator), attr, f);
35603         }else{
35604             this.root.select();
35605             if(callback){
35606                 callback(true, this.root);
35607             }
35608         }
35609     },
35610
35611     getTreeEl : function(){
35612         return this.el;
35613     },
35614
35615     /**
35616      * Trigger rendering of this TreePanel
35617      */
35618     render : function(){
35619         if (this.innerCt) {
35620             return this; // stop it rendering more than once!!
35621         }
35622         
35623         this.innerCt = this.el.createChild({tag:"ul",
35624                cls:"x-tree-root-ct " +
35625                (this.lines ? "x-tree-lines" : "x-tree-no-lines")});
35626
35627         if(this.containerScroll){
35628             Roo.dd.ScrollManager.register(this.el);
35629         }
35630         if((this.enableDD || this.enableDrop) && !this.dropZone){
35631            /**
35632             * The dropZone used by this tree if drop is enabled
35633             * @type Roo.tree.TreeDropZone
35634             */
35635              this.dropZone = new Roo.tree.TreeDropZone(this, this.dropConfig || {
35636                ddGroup: this.ddGroup || "TreeDD", appendOnly: this.ddAppendOnly === true
35637            });
35638         }
35639         if((this.enableDD || this.enableDrag) && !this.dragZone){
35640            /**
35641             * The dragZone used by this tree if drag is enabled
35642             * @type Roo.tree.TreeDragZone
35643             */
35644             this.dragZone = new Roo.tree.TreeDragZone(this, this.dragConfig || {
35645                ddGroup: this.ddGroup || "TreeDD",
35646                scroll: this.ddScroll
35647            });
35648         }
35649         this.getSelectionModel().init(this);
35650         if (!this.root) {
35651             Roo.log("ROOT not set in tree");
35652             return this;
35653         }
35654         this.root.render();
35655         if(!this.rootVisible){
35656             this.root.renderChildren();
35657         }
35658         return this;
35659     }
35660 });/*
35661  * Based on:
35662  * Ext JS Library 1.1.1
35663  * Copyright(c) 2006-2007, Ext JS, LLC.
35664  *
35665  * Originally Released Under LGPL - original licence link has changed is not relivant.
35666  *
35667  * Fork - LGPL
35668  * <script type="text/javascript">
35669  */
35670  
35671
35672 /**
35673  * @class Roo.tree.DefaultSelectionModel
35674  * @extends Roo.util.Observable
35675  * The default single selection for a TreePanel.
35676  * @param {Object} cfg Configuration
35677  */
35678 Roo.tree.DefaultSelectionModel = function(cfg){
35679    this.selNode = null;
35680    
35681    
35682    
35683    this.addEvents({
35684        /**
35685         * @event selectionchange
35686         * Fires when the selected node changes
35687         * @param {DefaultSelectionModel} this
35688         * @param {TreeNode} node the new selection
35689         */
35690        "selectionchange" : true,
35691
35692        /**
35693         * @event beforeselect
35694         * Fires before the selected node changes, return false to cancel the change
35695         * @param {DefaultSelectionModel} this
35696         * @param {TreeNode} node the new selection
35697         * @param {TreeNode} node the old selection
35698         */
35699        "beforeselect" : true
35700    });
35701    
35702     Roo.tree.DefaultSelectionModel.superclass.constructor.call(this,cfg);
35703 };
35704
35705 Roo.extend(Roo.tree.DefaultSelectionModel, Roo.util.Observable, {
35706     init : function(tree){
35707         this.tree = tree;
35708         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35709         tree.on("click", this.onNodeClick, this);
35710     },
35711     
35712     onNodeClick : function(node, e){
35713         if (e.ctrlKey && this.selNode == node)  {
35714             this.unselect(node);
35715             return;
35716         }
35717         this.select(node);
35718     },
35719     
35720     /**
35721      * Select a node.
35722      * @param {TreeNode} node The node to select
35723      * @return {TreeNode} The selected node
35724      */
35725     select : function(node){
35726         var last = this.selNode;
35727         if(last != node && this.fireEvent('beforeselect', this, node, last) !== false){
35728             if(last){
35729                 last.ui.onSelectedChange(false);
35730             }
35731             this.selNode = node;
35732             node.ui.onSelectedChange(true);
35733             this.fireEvent("selectionchange", this, node, last);
35734         }
35735         return node;
35736     },
35737     
35738     /**
35739      * Deselect a node.
35740      * @param {TreeNode} node The node to unselect
35741      */
35742     unselect : function(node){
35743         if(this.selNode == node){
35744             this.clearSelections();
35745         }    
35746     },
35747     
35748     /**
35749      * Clear all selections
35750      */
35751     clearSelections : function(){
35752         var n = this.selNode;
35753         if(n){
35754             n.ui.onSelectedChange(false);
35755             this.selNode = null;
35756             this.fireEvent("selectionchange", this, null);
35757         }
35758         return n;
35759     },
35760     
35761     /**
35762      * Get the selected node
35763      * @return {TreeNode} The selected node
35764      */
35765     getSelectedNode : function(){
35766         return this.selNode;    
35767     },
35768     
35769     /**
35770      * Returns true if the node is selected
35771      * @param {TreeNode} node The node to check
35772      * @return {Boolean}
35773      */
35774     isSelected : function(node){
35775         return this.selNode == node;  
35776     },
35777
35778     /**
35779      * Selects the node above the selected node in the tree, intelligently walking the nodes
35780      * @return TreeNode The new selection
35781      */
35782     selectPrevious : function(){
35783         var s = this.selNode || this.lastSelNode;
35784         if(!s){
35785             return null;
35786         }
35787         var ps = s.previousSibling;
35788         if(ps){
35789             if(!ps.isExpanded() || ps.childNodes.length < 1){
35790                 return this.select(ps);
35791             } else{
35792                 var lc = ps.lastChild;
35793                 while(lc && lc.isExpanded() && lc.childNodes.length > 0){
35794                     lc = lc.lastChild;
35795                 }
35796                 return this.select(lc);
35797             }
35798         } else if(s.parentNode && (this.tree.rootVisible || !s.parentNode.isRoot)){
35799             return this.select(s.parentNode);
35800         }
35801         return null;
35802     },
35803
35804     /**
35805      * Selects the node above the selected node in the tree, intelligently walking the nodes
35806      * @return TreeNode The new selection
35807      */
35808     selectNext : function(){
35809         var s = this.selNode || this.lastSelNode;
35810         if(!s){
35811             return null;
35812         }
35813         if(s.firstChild && s.isExpanded()){
35814              return this.select(s.firstChild);
35815          }else if(s.nextSibling){
35816              return this.select(s.nextSibling);
35817          }else if(s.parentNode){
35818             var newS = null;
35819             s.parentNode.bubble(function(){
35820                 if(this.nextSibling){
35821                     newS = this.getOwnerTree().selModel.select(this.nextSibling);
35822                     return false;
35823                 }
35824             });
35825             return newS;
35826          }
35827         return null;
35828     },
35829
35830     onKeyDown : function(e){
35831         var s = this.selNode || this.lastSelNode;
35832         // undesirable, but required
35833         var sm = this;
35834         if(!s){
35835             return;
35836         }
35837         var k = e.getKey();
35838         switch(k){
35839              case e.DOWN:
35840                  e.stopEvent();
35841                  this.selectNext();
35842              break;
35843              case e.UP:
35844                  e.stopEvent();
35845                  this.selectPrevious();
35846              break;
35847              case e.RIGHT:
35848                  e.preventDefault();
35849                  if(s.hasChildNodes()){
35850                      if(!s.isExpanded()){
35851                          s.expand();
35852                      }else if(s.firstChild){
35853                          this.select(s.firstChild, e);
35854                      }
35855                  }
35856              break;
35857              case e.LEFT:
35858                  e.preventDefault();
35859                  if(s.hasChildNodes() && s.isExpanded()){
35860                      s.collapse();
35861                  }else if(s.parentNode && (this.tree.rootVisible || s.parentNode != this.tree.getRootNode())){
35862                      this.select(s.parentNode, e);
35863                  }
35864              break;
35865         };
35866     }
35867 });
35868
35869 /**
35870  * @class Roo.tree.MultiSelectionModel
35871  * @extends Roo.util.Observable
35872  * Multi selection for a TreePanel.
35873  * @param {Object} cfg Configuration
35874  */
35875 Roo.tree.MultiSelectionModel = function(){
35876    this.selNodes = [];
35877    this.selMap = {};
35878    this.addEvents({
35879        /**
35880         * @event selectionchange
35881         * Fires when the selected nodes change
35882         * @param {MultiSelectionModel} this
35883         * @param {Array} nodes Array of the selected nodes
35884         */
35885        "selectionchange" : true
35886    });
35887    Roo.tree.MultiSelectionModel.superclass.constructor.call(this,cfg);
35888    
35889 };
35890
35891 Roo.extend(Roo.tree.MultiSelectionModel, Roo.util.Observable, {
35892     init : function(tree){
35893         this.tree = tree;
35894         tree.getTreeEl().on("keydown", this.onKeyDown, this);
35895         tree.on("click", this.onNodeClick, this);
35896     },
35897     
35898     onNodeClick : function(node, e){
35899         this.select(node, e, e.ctrlKey);
35900     },
35901     
35902     /**
35903      * Select a node.
35904      * @param {TreeNode} node The node to select
35905      * @param {EventObject} e (optional) An event associated with the selection
35906      * @param {Boolean} keepExisting True to retain existing selections
35907      * @return {TreeNode} The selected node
35908      */
35909     select : function(node, e, keepExisting){
35910         if(keepExisting !== true){
35911             this.clearSelections(true);
35912         }
35913         if(this.isSelected(node)){
35914             this.lastSelNode = node;
35915             return node;
35916         }
35917         this.selNodes.push(node);
35918         this.selMap[node.id] = node;
35919         this.lastSelNode = node;
35920         node.ui.onSelectedChange(true);
35921         this.fireEvent("selectionchange", this, this.selNodes);
35922         return node;
35923     },
35924     
35925     /**
35926      * Deselect a node.
35927      * @param {TreeNode} node The node to unselect
35928      */
35929     unselect : function(node){
35930         if(this.selMap[node.id]){
35931             node.ui.onSelectedChange(false);
35932             var sn = this.selNodes;
35933             var index = -1;
35934             if(sn.indexOf){
35935                 index = sn.indexOf(node);
35936             }else{
35937                 for(var i = 0, len = sn.length; i < len; i++){
35938                     if(sn[i] == node){
35939                         index = i;
35940                         break;
35941                     }
35942                 }
35943             }
35944             if(index != -1){
35945                 this.selNodes.splice(index, 1);
35946             }
35947             delete this.selMap[node.id];
35948             this.fireEvent("selectionchange", this, this.selNodes);
35949         }
35950     },
35951     
35952     /**
35953      * Clear all selections
35954      */
35955     clearSelections : function(suppressEvent){
35956         var sn = this.selNodes;
35957         if(sn.length > 0){
35958             for(var i = 0, len = sn.length; i < len; i++){
35959                 sn[i].ui.onSelectedChange(false);
35960             }
35961             this.selNodes = [];
35962             this.selMap = {};
35963             if(suppressEvent !== true){
35964                 this.fireEvent("selectionchange", this, this.selNodes);
35965             }
35966         }
35967     },
35968     
35969     /**
35970      * Returns true if the node is selected
35971      * @param {TreeNode} node The node to check
35972      * @return {Boolean}
35973      */
35974     isSelected : function(node){
35975         return this.selMap[node.id] ? true : false;  
35976     },
35977     
35978     /**
35979      * Returns an array of the selected nodes
35980      * @return {Array}
35981      */
35982     getSelectedNodes : function(){
35983         return this.selNodes;    
35984     },
35985
35986     onKeyDown : Roo.tree.DefaultSelectionModel.prototype.onKeyDown,
35987
35988     selectNext : Roo.tree.DefaultSelectionModel.prototype.selectNext,
35989
35990     selectPrevious : Roo.tree.DefaultSelectionModel.prototype.selectPrevious
35991 });/*
35992  * Based on:
35993  * Ext JS Library 1.1.1
35994  * Copyright(c) 2006-2007, Ext JS, LLC.
35995  *
35996  * Originally Released Under LGPL - original licence link has changed is not relivant.
35997  *
35998  * Fork - LGPL
35999  * <script type="text/javascript">
36000  */
36001  
36002 /**
36003  * @class Roo.tree.TreeNode
36004  * @extends Roo.data.Node
36005  * @cfg {String} text The text for this node
36006  * @cfg {Boolean} expanded true to start the node expanded
36007  * @cfg {Boolean} allowDrag false to make this node undraggable if DD is on (defaults to true)
36008  * @cfg {Boolean} allowDrop false if this node cannot be drop on
36009  * @cfg {Boolean} disabled true to start the node disabled
36010  * @cfg {String} icon The path to an icon for the node. The preferred way to do this
36011  *    is to use the cls or iconCls attributes and add the icon via a CSS background image.
36012  * @cfg {String} cls A css class to be added to the node
36013  * @cfg {String} iconCls A css class to be added to the nodes icon element for applying css background images
36014  * @cfg {String} href URL of the link used for the node (defaults to #)
36015  * @cfg {String} hrefTarget target frame for the link
36016  * @cfg {String} qtip An Ext QuickTip for the node
36017  * @cfg {String} qtipCfg An Ext QuickTip config for the node (used instead of qtip)
36018  * @cfg {Boolean} singleClickExpand True for single click expand on this node
36019  * @cfg {Function} uiProvider A UI <b>class</b> to use for this node (defaults to Roo.tree.TreeNodeUI)
36020  * @cfg {Boolean} checked True to render a checked checkbox for this node, false to render an unchecked checkbox
36021  * (defaults to undefined with no checkbox rendered)
36022  * @constructor
36023  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node
36024  */
36025 Roo.tree.TreeNode = function(attributes){
36026     attributes = attributes || {};
36027     if(typeof attributes == "string"){
36028         attributes = {text: attributes};
36029     }
36030     this.childrenRendered = false;
36031     this.rendered = false;
36032     Roo.tree.TreeNode.superclass.constructor.call(this, attributes);
36033     this.expanded = attributes.expanded === true;
36034     this.isTarget = attributes.isTarget !== false;
36035     this.draggable = attributes.draggable !== false && attributes.allowDrag !== false;
36036     this.allowChildren = attributes.allowChildren !== false && attributes.allowDrop !== false;
36037
36038     /**
36039      * Read-only. The text for this node. To change it use setText().
36040      * @type String
36041      */
36042     this.text = attributes.text;
36043     /**
36044      * True if this node is disabled.
36045      * @type Boolean
36046      */
36047     this.disabled = attributes.disabled === true;
36048
36049     this.addEvents({
36050         /**
36051         * @event textchange
36052         * Fires when the text for this node is changed
36053         * @param {Node} this This node
36054         * @param {String} text The new text
36055         * @param {String} oldText The old text
36056         */
36057         "textchange" : true,
36058         /**
36059         * @event beforeexpand
36060         * Fires before this node is expanded, return false to cancel.
36061         * @param {Node} this This node
36062         * @param {Boolean} deep
36063         * @param {Boolean} anim
36064         */
36065         "beforeexpand" : true,
36066         /**
36067         * @event beforecollapse
36068         * Fires before this node is collapsed, return false to cancel.
36069         * @param {Node} this This node
36070         * @param {Boolean} deep
36071         * @param {Boolean} anim
36072         */
36073         "beforecollapse" : true,
36074         /**
36075         * @event expand
36076         * Fires when this node is expanded
36077         * @param {Node} this This node
36078         */
36079         "expand" : true,
36080         /**
36081         * @event disabledchange
36082         * Fires when the disabled status of this node changes
36083         * @param {Node} this This node
36084         * @param {Boolean} disabled
36085         */
36086         "disabledchange" : true,
36087         /**
36088         * @event collapse
36089         * Fires when this node is collapsed
36090         * @param {Node} this This node
36091         */
36092         "collapse" : true,
36093         /**
36094         * @event beforeclick
36095         * Fires before click processing. Return false to cancel the default action.
36096         * @param {Node} this This node
36097         * @param {Roo.EventObject} e The event object
36098         */
36099         "beforeclick":true,
36100         /**
36101         * @event checkchange
36102         * Fires when a node with a checkbox's checked property changes
36103         * @param {Node} this This node
36104         * @param {Boolean} checked
36105         */
36106         "checkchange":true,
36107         /**
36108         * @event click
36109         * Fires when this node is clicked
36110         * @param {Node} this This node
36111         * @param {Roo.EventObject} e The event object
36112         */
36113         "click":true,
36114         /**
36115         * @event dblclick
36116         * Fires when this node is double clicked
36117         * @param {Node} this This node
36118         * @param {Roo.EventObject} e The event object
36119         */
36120         "dblclick":true,
36121         /**
36122         * @event contextmenu
36123         * Fires when this node is right clicked
36124         * @param {Node} this This node
36125         * @param {Roo.EventObject} e The event object
36126         */
36127         "contextmenu":true,
36128         /**
36129         * @event beforechildrenrendered
36130         * Fires right before the child nodes for this node are rendered
36131         * @param {Node} this This node
36132         */
36133         "beforechildrenrendered":true
36134     });
36135
36136     var uiClass = this.attributes.uiProvider || Roo.tree.TreeNodeUI;
36137
36138     /**
36139      * Read-only. The UI for this node
36140      * @type TreeNodeUI
36141      */
36142     this.ui = new uiClass(this);
36143     
36144     // finally support items[]
36145     if (typeof(this.attributes.items) == 'undefined' || !this.attributes.items) {
36146         return;
36147     }
36148     
36149     
36150     Roo.each(this.attributes.items, function(c) {
36151         this.appendChild(Roo.factory(c,Roo.Tree));
36152     }, this);
36153     delete this.attributes.items;
36154     
36155     
36156     
36157 };
36158 Roo.extend(Roo.tree.TreeNode, Roo.data.Node, {
36159     preventHScroll: true,
36160     /**
36161      * Returns true if this node is expanded
36162      * @return {Boolean}
36163      */
36164     isExpanded : function(){
36165         return this.expanded;
36166     },
36167
36168     /**
36169      * Returns the UI object for this node
36170      * @return {TreeNodeUI}
36171      */
36172     getUI : function(){
36173         return this.ui;
36174     },
36175
36176     // private override
36177     setFirstChild : function(node){
36178         var of = this.firstChild;
36179         Roo.tree.TreeNode.superclass.setFirstChild.call(this, node);
36180         if(this.childrenRendered && of && node != of){
36181             of.renderIndent(true, true);
36182         }
36183         if(this.rendered){
36184             this.renderIndent(true, true);
36185         }
36186     },
36187
36188     // private override
36189     setLastChild : function(node){
36190         var ol = this.lastChild;
36191         Roo.tree.TreeNode.superclass.setLastChild.call(this, node);
36192         if(this.childrenRendered && ol && node != ol){
36193             ol.renderIndent(true, true);
36194         }
36195         if(this.rendered){
36196             this.renderIndent(true, true);
36197         }
36198     },
36199
36200     // these methods are overridden to provide lazy rendering support
36201     // private override
36202     appendChild : function()
36203     {
36204         var node = Roo.tree.TreeNode.superclass.appendChild.apply(this, arguments);
36205         if(node && this.childrenRendered){
36206             node.render();
36207         }
36208         this.ui.updateExpandIcon();
36209         return node;
36210     },
36211
36212     // private override
36213     removeChild : function(node){
36214         this.ownerTree.getSelectionModel().unselect(node);
36215         Roo.tree.TreeNode.superclass.removeChild.apply(this, arguments);
36216         // if it's been rendered remove dom node
36217         if(this.childrenRendered){
36218             node.ui.remove();
36219         }
36220         if(this.childNodes.length < 1){
36221             this.collapse(false, false);
36222         }else{
36223             this.ui.updateExpandIcon();
36224         }
36225         if(!this.firstChild) {
36226             this.childrenRendered = false;
36227         }
36228         return node;
36229     },
36230
36231     // private override
36232     insertBefore : function(node, refNode){
36233         var newNode = Roo.tree.TreeNode.superclass.insertBefore.apply(this, arguments);
36234         if(newNode && refNode && this.childrenRendered){
36235             node.render();
36236         }
36237         this.ui.updateExpandIcon();
36238         return newNode;
36239     },
36240
36241     /**
36242      * Sets the text for this node
36243      * @param {String} text
36244      */
36245     setText : function(text){
36246         var oldText = this.text;
36247         this.text = text;
36248         this.attributes.text = text;
36249         if(this.rendered){ // event without subscribing
36250             this.ui.onTextChange(this, text, oldText);
36251         }
36252         this.fireEvent("textchange", this, text, oldText);
36253     },
36254
36255     /**
36256      * Triggers selection of this node
36257      */
36258     select : function(){
36259         this.getOwnerTree().getSelectionModel().select(this);
36260     },
36261
36262     /**
36263      * Triggers deselection of this node
36264      */
36265     unselect : function(){
36266         this.getOwnerTree().getSelectionModel().unselect(this);
36267     },
36268
36269     /**
36270      * Returns true if this node is selected
36271      * @return {Boolean}
36272      */
36273     isSelected : function(){
36274         return this.getOwnerTree().getSelectionModel().isSelected(this);
36275     },
36276
36277     /**
36278      * Expand this node.
36279      * @param {Boolean} deep (optional) True to expand all children as well
36280      * @param {Boolean} anim (optional) false to cancel the default animation
36281      * @param {Function} callback (optional) A callback to be called when
36282      * expanding this node completes (does not wait for deep expand to complete).
36283      * Called with 1 parameter, this node.
36284      */
36285     expand : function(deep, anim, callback){
36286         if(!this.expanded){
36287             if(this.fireEvent("beforeexpand", this, deep, anim) === false){
36288                 return;
36289             }
36290             if(!this.childrenRendered){
36291                 this.renderChildren();
36292             }
36293             this.expanded = true;
36294             
36295             if(!this.isHiddenRoot() && (this.getOwnerTree() && this.getOwnerTree().animate && anim !== false) || anim){
36296                 this.ui.animExpand(function(){
36297                     this.fireEvent("expand", this);
36298                     if(typeof callback == "function"){
36299                         callback(this);
36300                     }
36301                     if(deep === true){
36302                         this.expandChildNodes(true);
36303                     }
36304                 }.createDelegate(this));
36305                 return;
36306             }else{
36307                 this.ui.expand();
36308                 this.fireEvent("expand", this);
36309                 if(typeof callback == "function"){
36310                     callback(this);
36311                 }
36312             }
36313         }else{
36314            if(typeof callback == "function"){
36315                callback(this);
36316            }
36317         }
36318         if(deep === true){
36319             this.expandChildNodes(true);
36320         }
36321     },
36322
36323     isHiddenRoot : function(){
36324         return this.isRoot && !this.getOwnerTree().rootVisible;
36325     },
36326
36327     /**
36328      * Collapse this node.
36329      * @param {Boolean} deep (optional) True to collapse all children as well
36330      * @param {Boolean} anim (optional) false to cancel the default animation
36331      */
36332     collapse : function(deep, anim){
36333         if(this.expanded && !this.isHiddenRoot()){
36334             if(this.fireEvent("beforecollapse", this, deep, anim) === false){
36335                 return;
36336             }
36337             this.expanded = false;
36338             if((this.getOwnerTree().animate && anim !== false) || anim){
36339                 this.ui.animCollapse(function(){
36340                     this.fireEvent("collapse", this);
36341                     if(deep === true){
36342                         this.collapseChildNodes(true);
36343                     }
36344                 }.createDelegate(this));
36345                 return;
36346             }else{
36347                 this.ui.collapse();
36348                 this.fireEvent("collapse", this);
36349             }
36350         }
36351         if(deep === true){
36352             var cs = this.childNodes;
36353             for(var i = 0, len = cs.length; i < len; i++) {
36354                 cs[i].collapse(true, false);
36355             }
36356         }
36357     },
36358
36359     // private
36360     delayedExpand : function(delay){
36361         if(!this.expandProcId){
36362             this.expandProcId = this.expand.defer(delay, this);
36363         }
36364     },
36365
36366     // private
36367     cancelExpand : function(){
36368         if(this.expandProcId){
36369             clearTimeout(this.expandProcId);
36370         }
36371         this.expandProcId = false;
36372     },
36373
36374     /**
36375      * Toggles expanded/collapsed state of the node
36376      */
36377     toggle : function(){
36378         if(this.expanded){
36379             this.collapse();
36380         }else{
36381             this.expand();
36382         }
36383     },
36384
36385     /**
36386      * Ensures all parent nodes are expanded
36387      */
36388     ensureVisible : function(callback){
36389         var tree = this.getOwnerTree();
36390         tree.expandPath(this.parentNode.getPath(), false, function(){
36391             tree.getTreeEl().scrollChildIntoView(this.ui.anchor);
36392             Roo.callback(callback);
36393         }.createDelegate(this));
36394     },
36395
36396     /**
36397      * Expand all child nodes
36398      * @param {Boolean} deep (optional) true if the child nodes should also expand their child nodes
36399      */
36400     expandChildNodes : function(deep){
36401         var cs = this.childNodes;
36402         for(var i = 0, len = cs.length; i < len; i++) {
36403                 cs[i].expand(deep);
36404         }
36405     },
36406
36407     /**
36408      * Collapse all child nodes
36409      * @param {Boolean} deep (optional) true if the child nodes should also collapse their child nodes
36410      */
36411     collapseChildNodes : function(deep){
36412         var cs = this.childNodes;
36413         for(var i = 0, len = cs.length; i < len; i++) {
36414                 cs[i].collapse(deep);
36415         }
36416     },
36417
36418     /**
36419      * Disables this node
36420      */
36421     disable : function(){
36422         this.disabled = true;
36423         this.unselect();
36424         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36425             this.ui.onDisableChange(this, true);
36426         }
36427         this.fireEvent("disabledchange", this, true);
36428     },
36429
36430     /**
36431      * Enables this node
36432      */
36433     enable : function(){
36434         this.disabled = false;
36435         if(this.rendered && this.ui.onDisableChange){ // event without subscribing
36436             this.ui.onDisableChange(this, false);
36437         }
36438         this.fireEvent("disabledchange", this, false);
36439     },
36440
36441     // private
36442     renderChildren : function(suppressEvent){
36443         if(suppressEvent !== false){
36444             this.fireEvent("beforechildrenrendered", this);
36445         }
36446         var cs = this.childNodes;
36447         for(var i = 0, len = cs.length; i < len; i++){
36448             cs[i].render(true);
36449         }
36450         this.childrenRendered = true;
36451     },
36452
36453     // private
36454     sort : function(fn, scope){
36455         Roo.tree.TreeNode.superclass.sort.apply(this, arguments);
36456         if(this.childrenRendered){
36457             var cs = this.childNodes;
36458             for(var i = 0, len = cs.length; i < len; i++){
36459                 cs[i].render(true);
36460             }
36461         }
36462     },
36463
36464     // private
36465     render : function(bulkRender){
36466         this.ui.render(bulkRender);
36467         if(!this.rendered){
36468             this.rendered = true;
36469             if(this.expanded){
36470                 this.expanded = false;
36471                 this.expand(false, false);
36472             }
36473         }
36474     },
36475
36476     // private
36477     renderIndent : function(deep, refresh){
36478         if(refresh){
36479             this.ui.childIndent = null;
36480         }
36481         this.ui.renderIndent();
36482         if(deep === true && this.childrenRendered){
36483             var cs = this.childNodes;
36484             for(var i = 0, len = cs.length; i < len; i++){
36485                 cs[i].renderIndent(true, refresh);
36486             }
36487         }
36488     }
36489 });/*
36490  * Based on:
36491  * Ext JS Library 1.1.1
36492  * Copyright(c) 2006-2007, Ext JS, LLC.
36493  *
36494  * Originally Released Under LGPL - original licence link has changed is not relivant.
36495  *
36496  * Fork - LGPL
36497  * <script type="text/javascript">
36498  */
36499  
36500 /**
36501  * @class Roo.tree.AsyncTreeNode
36502  * @extends Roo.tree.TreeNode
36503  * @cfg {TreeLoader} loader A TreeLoader to be used by this node (defaults to the loader defined on the tree)
36504  * @constructor
36505  * @param {Object/String} attributes The attributes/config for the node or just a string with the text for the node 
36506  */
36507  Roo.tree.AsyncTreeNode = function(config){
36508     this.loaded = false;
36509     this.loading = false;
36510     Roo.tree.AsyncTreeNode.superclass.constructor.apply(this, arguments);
36511     /**
36512     * @event beforeload
36513     * Fires before this node is loaded, return false to cancel
36514     * @param {Node} this This node
36515     */
36516     this.addEvents({'beforeload':true, 'load': true});
36517     /**
36518     * @event load
36519     * Fires when this node is loaded
36520     * @param {Node} this This node
36521     */
36522     /**
36523      * The loader used by this node (defaults to using the tree's defined loader)
36524      * @type TreeLoader
36525      * @property loader
36526      */
36527 };
36528 Roo.extend(Roo.tree.AsyncTreeNode, Roo.tree.TreeNode, {
36529     expand : function(deep, anim, callback){
36530         if(this.loading){ // if an async load is already running, waiting til it's done
36531             var timer;
36532             var f = function(){
36533                 if(!this.loading){ // done loading
36534                     clearInterval(timer);
36535                     this.expand(deep, anim, callback);
36536                 }
36537             }.createDelegate(this);
36538             timer = setInterval(f, 200);
36539             return;
36540         }
36541         if(!this.loaded){
36542             if(this.fireEvent("beforeload", this) === false){
36543                 return;
36544             }
36545             this.loading = true;
36546             this.ui.beforeLoad(this);
36547             var loader = this.loader || this.attributes.loader || this.getOwnerTree().getLoader();
36548             if(loader){
36549                 loader.load(this, this.loadComplete.createDelegate(this, [deep, anim, callback]));
36550                 return;
36551             }
36552         }
36553         Roo.tree.AsyncTreeNode.superclass.expand.call(this, deep, anim, callback);
36554     },
36555     
36556     /**
36557      * Returns true if this node is currently loading
36558      * @return {Boolean}
36559      */
36560     isLoading : function(){
36561         return this.loading;  
36562     },
36563     
36564     loadComplete : function(deep, anim, callback){
36565         this.loading = false;
36566         this.loaded = true;
36567         this.ui.afterLoad(this);
36568         this.fireEvent("load", this);
36569         this.expand(deep, anim, callback);
36570     },
36571     
36572     /**
36573      * Returns true if this node has been loaded
36574      * @return {Boolean}
36575      */
36576     isLoaded : function(){
36577         return this.loaded;
36578     },
36579     
36580     hasChildNodes : function(){
36581         if(!this.isLeaf() && !this.loaded){
36582             return true;
36583         }else{
36584             return Roo.tree.AsyncTreeNode.superclass.hasChildNodes.call(this);
36585         }
36586     },
36587
36588     /**
36589      * Trigger a reload for this node
36590      * @param {Function} callback
36591      */
36592     reload : function(callback){
36593         this.collapse(false, false);
36594         while(this.firstChild){
36595             this.removeChild(this.firstChild);
36596         }
36597         this.childrenRendered = false;
36598         this.loaded = false;
36599         if(this.isHiddenRoot()){
36600             this.expanded = false;
36601         }
36602         this.expand(false, false, callback);
36603     }
36604 });/*
36605  * Based on:
36606  * Ext JS Library 1.1.1
36607  * Copyright(c) 2006-2007, Ext JS, LLC.
36608  *
36609  * Originally Released Under LGPL - original licence link has changed is not relivant.
36610  *
36611  * Fork - LGPL
36612  * <script type="text/javascript">
36613  */
36614  
36615 /**
36616  * @class Roo.tree.TreeNodeUI
36617  * @constructor
36618  * @param {Object} node The node to render
36619  * The TreeNode UI implementation is separate from the
36620  * tree implementation. Unless you are customizing the tree UI,
36621  * you should never have to use this directly.
36622  */
36623 Roo.tree.TreeNodeUI = function(node){
36624     this.node = node;
36625     this.rendered = false;
36626     this.animating = false;
36627     this.emptyIcon = Roo.BLANK_IMAGE_URL;
36628 };
36629
36630 Roo.tree.TreeNodeUI.prototype = {
36631     removeChild : function(node){
36632         if(this.rendered){
36633             this.ctNode.removeChild(node.ui.getEl());
36634         }
36635     },
36636
36637     beforeLoad : function(){
36638          this.addClass("x-tree-node-loading");
36639     },
36640
36641     afterLoad : function(){
36642          this.removeClass("x-tree-node-loading");
36643     },
36644
36645     onTextChange : function(node, text, oldText){
36646         if(this.rendered){
36647             this.textNode.innerHTML = text;
36648         }
36649     },
36650
36651     onDisableChange : function(node, state){
36652         this.disabled = state;
36653         if(state){
36654             this.addClass("x-tree-node-disabled");
36655         }else{
36656             this.removeClass("x-tree-node-disabled");
36657         }
36658     },
36659
36660     onSelectedChange : function(state){
36661         if(state){
36662             this.focus();
36663             this.addClass("x-tree-selected");
36664         }else{
36665             //this.blur();
36666             this.removeClass("x-tree-selected");
36667         }
36668     },
36669
36670     onMove : function(tree, node, oldParent, newParent, index, refNode){
36671         this.childIndent = null;
36672         if(this.rendered){
36673             var targetNode = newParent.ui.getContainer();
36674             if(!targetNode){//target not rendered
36675                 this.holder = document.createElement("div");
36676                 this.holder.appendChild(this.wrap);
36677                 return;
36678             }
36679             var insertBefore = refNode ? refNode.ui.getEl() : null;
36680             if(insertBefore){
36681                 targetNode.insertBefore(this.wrap, insertBefore);
36682             }else{
36683                 targetNode.appendChild(this.wrap);
36684             }
36685             this.node.renderIndent(true);
36686         }
36687     },
36688
36689     addClass : function(cls){
36690         if(this.elNode){
36691             Roo.fly(this.elNode).addClass(cls);
36692         }
36693     },
36694
36695     removeClass : function(cls){
36696         if(this.elNode){
36697             Roo.fly(this.elNode).removeClass(cls);
36698         }
36699     },
36700
36701     remove : function(){
36702         if(this.rendered){
36703             this.holder = document.createElement("div");
36704             this.holder.appendChild(this.wrap);
36705         }
36706     },
36707
36708     fireEvent : function(){
36709         return this.node.fireEvent.apply(this.node, arguments);
36710     },
36711
36712     initEvents : function(){
36713         this.node.on("move", this.onMove, this);
36714         var E = Roo.EventManager;
36715         var a = this.anchor;
36716
36717         var el = Roo.fly(a, '_treeui');
36718
36719         if(Roo.isOpera){ // opera render bug ignores the CSS
36720             el.setStyle("text-decoration", "none");
36721         }
36722
36723         el.on("click", this.onClick, this);
36724         el.on("dblclick", this.onDblClick, this);
36725
36726         if(this.checkbox){
36727             Roo.EventManager.on(this.checkbox,
36728                     Roo.isIE ? 'click' : 'change', this.onCheckChange, this);
36729         }
36730
36731         el.on("contextmenu", this.onContextMenu, this);
36732
36733         var icon = Roo.fly(this.iconNode);
36734         icon.on("click", this.onClick, this);
36735         icon.on("dblclick", this.onDblClick, this);
36736         icon.on("contextmenu", this.onContextMenu, this);
36737         E.on(this.ecNode, "click", this.ecClick, this, true);
36738
36739         if(this.node.disabled){
36740             this.addClass("x-tree-node-disabled");
36741         }
36742         if(this.node.hidden){
36743             this.addClass("x-tree-node-disabled");
36744         }
36745         var ot = this.node.getOwnerTree();
36746         var dd = ot ? (ot.enableDD || ot.enableDrag || ot.enableDrop) : false;
36747         if(dd && (!this.node.isRoot || ot.rootVisible)){
36748             Roo.dd.Registry.register(this.elNode, {
36749                 node: this.node,
36750                 handles: this.getDDHandles(),
36751                 isHandle: false
36752             });
36753         }
36754     },
36755
36756     getDDHandles : function(){
36757         return [this.iconNode, this.textNode];
36758     },
36759
36760     hide : function(){
36761         if(this.rendered){
36762             this.wrap.style.display = "none";
36763         }
36764     },
36765
36766     show : function(){
36767         if(this.rendered){
36768             this.wrap.style.display = "";
36769         }
36770     },
36771
36772     onContextMenu : function(e){
36773         if (this.node.hasListener("contextmenu") || this.node.getOwnerTree().hasListener("contextmenu")) {
36774             e.preventDefault();
36775             this.focus();
36776             this.fireEvent("contextmenu", this.node, e);
36777         }
36778     },
36779
36780     onClick : function(e){
36781         if(this.dropping){
36782             e.stopEvent();
36783             return;
36784         }
36785         if(this.fireEvent("beforeclick", this.node, e) !== false){
36786             if(!this.disabled && this.node.attributes.href){
36787                 this.fireEvent("click", this.node, e);
36788                 return;
36789             }
36790             e.preventDefault();
36791             if(this.disabled){
36792                 return;
36793             }
36794
36795             if(this.node.attributes.singleClickExpand && !this.animating && this.node.hasChildNodes()){
36796                 this.node.toggle();
36797             }
36798
36799             this.fireEvent("click", this.node, e);
36800         }else{
36801             e.stopEvent();
36802         }
36803     },
36804
36805     onDblClick : function(e){
36806         e.preventDefault();
36807         if(this.disabled){
36808             return;
36809         }
36810         if(this.checkbox){
36811             this.toggleCheck();
36812         }
36813         if(!this.animating && this.node.hasChildNodes()){
36814             this.node.toggle();
36815         }
36816         this.fireEvent("dblclick", this.node, e);
36817     },
36818
36819     onCheckChange : function(){
36820         var checked = this.checkbox.checked;
36821         this.node.attributes.checked = checked;
36822         this.fireEvent('checkchange', this.node, checked);
36823     },
36824
36825     ecClick : function(e){
36826         if(!this.animating && this.node.hasChildNodes()){
36827             this.node.toggle();
36828         }
36829     },
36830
36831     startDrop : function(){
36832         this.dropping = true;
36833     },
36834
36835     // delayed drop so the click event doesn't get fired on a drop
36836     endDrop : function(){
36837        setTimeout(function(){
36838            this.dropping = false;
36839        }.createDelegate(this), 50);
36840     },
36841
36842     expand : function(){
36843         this.updateExpandIcon();
36844         this.ctNode.style.display = "";
36845     },
36846
36847     focus : function(){
36848         if(!this.node.preventHScroll){
36849             try{this.anchor.focus();
36850             }catch(e){}
36851         }else if(!Roo.isIE){
36852             try{
36853                 var noscroll = this.node.getOwnerTree().getTreeEl().dom;
36854                 var l = noscroll.scrollLeft;
36855                 this.anchor.focus();
36856                 noscroll.scrollLeft = l;
36857             }catch(e){}
36858         }
36859     },
36860
36861     toggleCheck : function(value){
36862         var cb = this.checkbox;
36863         if(cb){
36864             cb.checked = (value === undefined ? !cb.checked : value);
36865         }
36866     },
36867
36868     blur : function(){
36869         try{
36870             this.anchor.blur();
36871         }catch(e){}
36872     },
36873
36874     animExpand : function(callback){
36875         var ct = Roo.get(this.ctNode);
36876         ct.stopFx();
36877         if(!this.node.hasChildNodes()){
36878             this.updateExpandIcon();
36879             this.ctNode.style.display = "";
36880             Roo.callback(callback);
36881             return;
36882         }
36883         this.animating = true;
36884         this.updateExpandIcon();
36885
36886         ct.slideIn('t', {
36887            callback : function(){
36888                this.animating = false;
36889                Roo.callback(callback);
36890             },
36891             scope: this,
36892             duration: this.node.ownerTree.duration || .25
36893         });
36894     },
36895
36896     highlight : function(){
36897         var tree = this.node.getOwnerTree();
36898         Roo.fly(this.wrap).highlight(
36899             tree.hlColor || "C3DAF9",
36900             {endColor: tree.hlBaseColor}
36901         );
36902     },
36903
36904     collapse : function(){
36905         this.updateExpandIcon();
36906         this.ctNode.style.display = "none";
36907     },
36908
36909     animCollapse : function(callback){
36910         var ct = Roo.get(this.ctNode);
36911         ct.enableDisplayMode('block');
36912         ct.stopFx();
36913
36914         this.animating = true;
36915         this.updateExpandIcon();
36916
36917         ct.slideOut('t', {
36918             callback : function(){
36919                this.animating = false;
36920                Roo.callback(callback);
36921             },
36922             scope: this,
36923             duration: this.node.ownerTree.duration || .25
36924         });
36925     },
36926
36927     getContainer : function(){
36928         return this.ctNode;
36929     },
36930
36931     getEl : function(){
36932         return this.wrap;
36933     },
36934
36935     appendDDGhost : function(ghostNode){
36936         ghostNode.appendChild(this.elNode.cloneNode(true));
36937     },
36938
36939     getDDRepairXY : function(){
36940         return Roo.lib.Dom.getXY(this.iconNode);
36941     },
36942
36943     onRender : function(){
36944         this.render();
36945     },
36946
36947     render : function(bulkRender){
36948         var n = this.node, a = n.attributes;
36949         var targetNode = n.parentNode ?
36950               n.parentNode.ui.getContainer() : n.ownerTree.innerCt.dom;
36951
36952         if(!this.rendered){
36953             this.rendered = true;
36954
36955             this.renderElements(n, a, targetNode, bulkRender);
36956
36957             if(a.qtip){
36958                if(this.textNode.setAttributeNS){
36959                    this.textNode.setAttributeNS("ext", "qtip", a.qtip);
36960                    if(a.qtipTitle){
36961                        this.textNode.setAttributeNS("ext", "qtitle", a.qtipTitle);
36962                    }
36963                }else{
36964                    this.textNode.setAttribute("ext:qtip", a.qtip);
36965                    if(a.qtipTitle){
36966                        this.textNode.setAttribute("ext:qtitle", a.qtipTitle);
36967                    }
36968                }
36969             }else if(a.qtipCfg){
36970                 a.qtipCfg.target = Roo.id(this.textNode);
36971                 Roo.QuickTips.register(a.qtipCfg);
36972             }
36973             this.initEvents();
36974             if(!this.node.expanded){
36975                 this.updateExpandIcon();
36976             }
36977         }else{
36978             if(bulkRender === true) {
36979                 targetNode.appendChild(this.wrap);
36980             }
36981         }
36982     },
36983
36984     renderElements : function(n, a, targetNode, bulkRender)
36985     {
36986         // add some indent caching, this helps performance when rendering a large tree
36987         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
36988         var t = n.getOwnerTree();
36989         var txt = t && t.renderer ? t.renderer(n.attributes) : Roo.util.Format.htmlEncode(n.text);
36990         if (typeof(n.attributes.html) != 'undefined') {
36991             txt = n.attributes.html;
36992         }
36993         var tip = t && t.rendererTip ? t.rendererTip(n.attributes) : txt;
36994         var cb = typeof a.checked == 'boolean';
36995         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
36996         var buf = ['<li class="x-tree-node"><div class="x-tree-node-el ', a.cls,'">',
36997             '<span class="x-tree-node-indent">',this.indentMarkup,"</span>",
36998             '<img src="', this.emptyIcon, '" class="x-tree-ec-icon" />',
36999             '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',(a.icon ? " x-tree-node-inline-icon" : ""),(a.iconCls ? " "+a.iconCls : ""),'" unselectable="on" />',
37000             cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + (a.checked ? 'checked="checked" />' : ' />')) : '',
37001             '<a hidefocus="on" href="',href,'" tabIndex="1" ',
37002              a.hrefTarget ? ' target="'+a.hrefTarget+'"' : "", 
37003                 '><span unselectable="on" qtip="' , tip ,'">',txt,"</span></a></div>",
37004             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
37005             "</li>"];
37006
37007         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
37008             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
37009                                 n.nextSibling.ui.getEl(), buf.join(""));
37010         }else{
37011             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
37012         }
37013
37014         this.elNode = this.wrap.childNodes[0];
37015         this.ctNode = this.wrap.childNodes[1];
37016         var cs = this.elNode.childNodes;
37017         this.indentNode = cs[0];
37018         this.ecNode = cs[1];
37019         this.iconNode = cs[2];
37020         var index = 3;
37021         if(cb){
37022             this.checkbox = cs[3];
37023             index++;
37024         }
37025         this.anchor = cs[index];
37026         this.textNode = cs[index].firstChild;
37027     },
37028
37029     getAnchor : function(){
37030         return this.anchor;
37031     },
37032
37033     getTextEl : function(){
37034         return this.textNode;
37035     },
37036
37037     getIconEl : function(){
37038         return this.iconNode;
37039     },
37040
37041     isChecked : function(){
37042         return this.checkbox ? this.checkbox.checked : false;
37043     },
37044
37045     updateExpandIcon : function(){
37046         if(this.rendered){
37047             var n = this.node, c1, c2;
37048             var cls = n.isLast() ? "x-tree-elbow-end" : "x-tree-elbow";
37049             var hasChild = n.hasChildNodes();
37050             if(hasChild){
37051                 if(n.expanded){
37052                     cls += "-minus";
37053                     c1 = "x-tree-node-collapsed";
37054                     c2 = "x-tree-node-expanded";
37055                 }else{
37056                     cls += "-plus";
37057                     c1 = "x-tree-node-expanded";
37058                     c2 = "x-tree-node-collapsed";
37059                 }
37060                 if(this.wasLeaf){
37061                     this.removeClass("x-tree-node-leaf");
37062                     this.wasLeaf = false;
37063                 }
37064                 if(this.c1 != c1 || this.c2 != c2){
37065                     Roo.fly(this.elNode).replaceClass(c1, c2);
37066                     this.c1 = c1; this.c2 = c2;
37067                 }
37068             }else{
37069                 // this changes non-leafs into leafs if they have no children.
37070                 // it's not very rational behaviour..
37071                 
37072                 if(!this.wasLeaf && this.node.leaf){
37073                     Roo.fly(this.elNode).replaceClass("x-tree-node-expanded", "x-tree-node-leaf");
37074                     delete this.c1;
37075                     delete this.c2;
37076                     this.wasLeaf = true;
37077                 }
37078             }
37079             var ecc = "x-tree-ec-icon "+cls;
37080             if(this.ecc != ecc){
37081                 this.ecNode.className = ecc;
37082                 this.ecc = ecc;
37083             }
37084         }
37085     },
37086
37087     getChildIndent : function(){
37088         if(!this.childIndent){
37089             var buf = [];
37090             var p = this.node;
37091             while(p){
37092                 if(!p.isRoot || (p.isRoot && p.ownerTree.rootVisible)){
37093                     if(!p.isLast()) {
37094                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-elbow-line" />');
37095                     } else {
37096                         buf.unshift('<img src="'+this.emptyIcon+'" class="x-tree-icon" />');
37097                     }
37098                 }
37099                 p = p.parentNode;
37100             }
37101             this.childIndent = buf.join("");
37102         }
37103         return this.childIndent;
37104     },
37105
37106     renderIndent : function(){
37107         if(this.rendered){
37108             var indent = "";
37109             var p = this.node.parentNode;
37110             if(p){
37111                 indent = p.ui.getChildIndent();
37112             }
37113             if(this.indentMarkup != indent){ // don't rerender if not required
37114                 this.indentNode.innerHTML = indent;
37115                 this.indentMarkup = indent;
37116             }
37117             this.updateExpandIcon();
37118         }
37119     }
37120 };
37121
37122 Roo.tree.RootTreeNodeUI = function(){
37123     Roo.tree.RootTreeNodeUI.superclass.constructor.apply(this, arguments);
37124 };
37125 Roo.extend(Roo.tree.RootTreeNodeUI, Roo.tree.TreeNodeUI, {
37126     render : function(){
37127         if(!this.rendered){
37128             var targetNode = this.node.ownerTree.innerCt.dom;
37129             this.node.expanded = true;
37130             targetNode.innerHTML = '<div class="x-tree-root-node"></div>';
37131             this.wrap = this.ctNode = targetNode.firstChild;
37132         }
37133     },
37134     collapse : function(){
37135     },
37136     expand : function(){
37137     }
37138 });/*
37139  * Based on:
37140  * Ext JS Library 1.1.1
37141  * Copyright(c) 2006-2007, Ext JS, LLC.
37142  *
37143  * Originally Released Under LGPL - original licence link has changed is not relivant.
37144  *
37145  * Fork - LGPL
37146  * <script type="text/javascript">
37147  */
37148 /**
37149  * @class Roo.tree.TreeLoader
37150  * @extends Roo.util.Observable
37151  * A TreeLoader provides for lazy loading of an {@link Roo.tree.TreeNode}'s child
37152  * nodes from a specified URL. The response must be a javascript Array definition
37153  * who's elements are node definition objects. eg:
37154  * <pre><code>
37155 {  success : true,
37156    data :      [
37157    
37158     { 'id': 1, 'text': 'A folder Node', 'leaf': false },
37159     { 'id': 2, 'text': 'A leaf Node', 'leaf': true }
37160     ]
37161 }
37162
37163
37164 </code></pre>
37165  * <br><br>
37166  * The old style respose with just an array is still supported, but not recommended.
37167  * <br><br>
37168  *
37169  * A server request is sent, and child nodes are loaded only when a node is expanded.
37170  * The loading node's id is passed to the server under the parameter name "node" to
37171  * enable the server to produce the correct child nodes.
37172  * <br><br>
37173  * To pass extra parameters, an event handler may be attached to the "beforeload"
37174  * event, and the parameters specified in the TreeLoader's baseParams property:
37175  * <pre><code>
37176     myTreeLoader.on("beforeload", function(treeLoader, node) {
37177         this.baseParams.category = node.attributes.category;
37178     }, this);
37179     
37180 </code></pre>
37181  *
37182  * This would pass an HTTP parameter called "category" to the server containing
37183  * the value of the Node's "category" attribute.
37184  * @constructor
37185  * Creates a new Treeloader.
37186  * @param {Object} config A config object containing config properties.
37187  */
37188 Roo.tree.TreeLoader = function(config){
37189     this.baseParams = {};
37190     this.requestMethod = "POST";
37191     Roo.apply(this, config);
37192
37193     this.addEvents({
37194     
37195         /**
37196          * @event beforeload
37197          * Fires before a network request is made to retrieve the Json text which specifies a node's children.
37198          * @param {Object} This TreeLoader object.
37199          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37200          * @param {Object} callback The callback function specified in the {@link #load} call.
37201          */
37202         beforeload : true,
37203         /**
37204          * @event load
37205          * Fires when the node has been successfuly loaded.
37206          * @param {Object} This TreeLoader object.
37207          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37208          * @param {Object} response The response object containing the data from the server.
37209          */
37210         load : true,
37211         /**
37212          * @event loadexception
37213          * Fires if the network request failed.
37214          * @param {Object} This TreeLoader object.
37215          * @param {Object} node The {@link Roo.tree.TreeNode} object being loaded.
37216          * @param {Object} response The response object containing the data from the server.
37217          */
37218         loadexception : true,
37219         /**
37220          * @event create
37221          * Fires before a node is created, enabling you to return custom Node types 
37222          * @param {Object} This TreeLoader object.
37223          * @param {Object} attr - the data returned from the AJAX call (modify it to suit)
37224          */
37225         create : true
37226     });
37227
37228     Roo.tree.TreeLoader.superclass.constructor.call(this);
37229 };
37230
37231 Roo.extend(Roo.tree.TreeLoader, Roo.util.Observable, {
37232     /**
37233     * @cfg {String} dataUrl The URL from which to request a Json string which
37234     * specifies an array of node definition object representing the child nodes
37235     * to be loaded.
37236     */
37237     /**
37238     * @cfg {String} requestMethod either GET or POST
37239     * defaults to POST (due to BC)
37240     * to be loaded.
37241     */
37242     /**
37243     * @cfg {Object} baseParams (optional) An object containing properties which
37244     * specify HTTP parameters to be passed to each request for child nodes.
37245     */
37246     /**
37247     * @cfg {Object} baseAttrs (optional) An object containing attributes to be added to all nodes
37248     * created by this loader. If the attributes sent by the server have an attribute in this object,
37249     * they take priority.
37250     */
37251     /**
37252     * @cfg {Object} uiProviders (optional) An object containing properties which
37253     * 
37254     * DEPRECATED - use 'create' event handler to modify attributes - which affect creation.
37255     * specify custom {@link Roo.tree.TreeNodeUI} implementations. If the optional
37256     * <i>uiProvider</i> attribute of a returned child node is a string rather
37257     * than a reference to a TreeNodeUI implementation, this that string value
37258     * is used as a property name in the uiProviders object. You can define the provider named
37259     * 'default' , and this will be used for all nodes (if no uiProvider is delivered by the node data)
37260     */
37261     uiProviders : {},
37262
37263     /**
37264     * @cfg {Boolean} clearOnLoad (optional) Default to true. Remove previously existing
37265     * child nodes before loading.
37266     */
37267     clearOnLoad : true,
37268
37269     /**
37270     * @cfg {String} root (optional) Default to false. Use this to read data from an object 
37271     * property on loading, rather than expecting an array. (eg. more compatible to a standard
37272     * Grid query { data : [ .....] }
37273     */
37274     
37275     root : false,
37276      /**
37277     * @cfg {String} queryParam (optional) 
37278     * Name of the query as it will be passed on the querystring (defaults to 'node')
37279     * eg. the request will be ?node=[id]
37280     */
37281     
37282     
37283     queryParam: false,
37284     
37285     /**
37286      * Load an {@link Roo.tree.TreeNode} from the URL specified in the constructor.
37287      * This is called automatically when a node is expanded, but may be used to reload
37288      * a node (or append new children if the {@link #clearOnLoad} option is false.)
37289      * @param {Roo.tree.TreeNode} node
37290      * @param {Function} callback
37291      */
37292     load : function(node, callback){
37293         if(this.clearOnLoad){
37294             while(node.firstChild){
37295                 node.removeChild(node.firstChild);
37296             }
37297         }
37298         if(node.attributes.children){ // preloaded json children
37299             var cs = node.attributes.children;
37300             for(var i = 0, len = cs.length; i < len; i++){
37301                 node.appendChild(this.createNode(cs[i]));
37302             }
37303             if(typeof callback == "function"){
37304                 callback();
37305             }
37306         }else if(this.dataUrl){
37307             this.requestData(node, callback);
37308         }
37309     },
37310
37311     getParams: function(node){
37312         var buf = [], bp = this.baseParams;
37313         for(var key in bp){
37314             if(typeof bp[key] != "function"){
37315                 buf.push(encodeURIComponent(key), "=", encodeURIComponent(bp[key]), "&");
37316             }
37317         }
37318         var n = this.queryParam === false ? 'node' : this.queryParam;
37319         buf.push(n + "=", encodeURIComponent(node.id));
37320         return buf.join("");
37321     },
37322
37323     requestData : function(node, callback){
37324         if(this.fireEvent("beforeload", this, node, callback) !== false){
37325             this.transId = Roo.Ajax.request({
37326                 method:this.requestMethod,
37327                 url: this.dataUrl||this.url,
37328                 success: this.handleResponse,
37329                 failure: this.handleFailure,
37330                 scope: this,
37331                 argument: {callback: callback, node: node},
37332                 params: this.getParams(node)
37333             });
37334         }else{
37335             // if the load is cancelled, make sure we notify
37336             // the node that we are done
37337             if(typeof callback == "function"){
37338                 callback();
37339             }
37340         }
37341     },
37342
37343     isLoading : function(){
37344         return this.transId ? true : false;
37345     },
37346
37347     abort : function(){
37348         if(this.isLoading()){
37349             Roo.Ajax.abort(this.transId);
37350         }
37351     },
37352
37353     // private
37354     createNode : function(attr)
37355     {
37356         // apply baseAttrs, nice idea Corey!
37357         if(this.baseAttrs){
37358             Roo.applyIf(attr, this.baseAttrs);
37359         }
37360         if(this.applyLoader !== false){
37361             attr.loader = this;
37362         }
37363         // uiProvider = depreciated..
37364         
37365         if(typeof(attr.uiProvider) == 'string'){
37366            attr.uiProvider = this.uiProviders[attr.uiProvider] || 
37367                 /**  eval:var:attr */ eval(attr.uiProvider);
37368         }
37369         if(typeof(this.uiProviders['default']) != 'undefined') {
37370             attr.uiProvider = this.uiProviders['default'];
37371         }
37372         
37373         this.fireEvent('create', this, attr);
37374         
37375         attr.leaf  = typeof(attr.leaf) == 'string' ? attr.leaf * 1 : attr.leaf;
37376         return(attr.leaf ?
37377                         new Roo.tree.TreeNode(attr) :
37378                         new Roo.tree.AsyncTreeNode(attr));
37379     },
37380
37381     processResponse : function(response, node, callback)
37382     {
37383         var json = response.responseText;
37384         try {
37385             
37386             var o = Roo.decode(json);
37387             
37388             if (this.root === false && typeof(o.success) != undefined) {
37389                 this.root = 'data'; // the default behaviour for list like data..
37390                 }
37391                 
37392             if (this.root !== false &&  !o.success) {
37393                 // it's a failure condition.
37394                 var a = response.argument;
37395                 this.fireEvent("loadexception", this, a.node, response);
37396                 Roo.log("Load failed - should have a handler really");
37397                 return;
37398             }
37399             
37400             
37401             
37402             if (this.root !== false) {
37403                  o = o[this.root];
37404             }
37405             
37406             for(var i = 0, len = o.length; i < len; i++){
37407                 var n = this.createNode(o[i]);
37408                 if(n){
37409                     node.appendChild(n);
37410                 }
37411             }
37412             if(typeof callback == "function"){
37413                 callback(this, node);
37414             }
37415         }catch(e){
37416             this.handleFailure(response);
37417         }
37418     },
37419
37420     handleResponse : function(response){
37421         this.transId = false;
37422         var a = response.argument;
37423         this.processResponse(response, a.node, a.callback);
37424         this.fireEvent("load", this, a.node, response);
37425     },
37426
37427     handleFailure : function(response)
37428     {
37429         // should handle failure better..
37430         this.transId = false;
37431         var a = response.argument;
37432         this.fireEvent("loadexception", this, a.node, response);
37433         if(typeof a.callback == "function"){
37434             a.callback(this, a.node);
37435         }
37436     }
37437 });/*
37438  * Based on:
37439  * Ext JS Library 1.1.1
37440  * Copyright(c) 2006-2007, Ext JS, LLC.
37441  *
37442  * Originally Released Under LGPL - original licence link has changed is not relivant.
37443  *
37444  * Fork - LGPL
37445  * <script type="text/javascript">
37446  */
37447
37448 /**
37449 * @class Roo.tree.TreeFilter
37450 * Note this class is experimental and doesn't update the indent (lines) or expand collapse icons of the nodes
37451 * @param {TreePanel} tree
37452 * @param {Object} config (optional)
37453  */
37454 Roo.tree.TreeFilter = function(tree, config){
37455     this.tree = tree;
37456     this.filtered = {};
37457     Roo.apply(this, config);
37458 };
37459
37460 Roo.tree.TreeFilter.prototype = {
37461     clearBlank:false,
37462     reverse:false,
37463     autoClear:false,
37464     remove:false,
37465
37466      /**
37467      * Filter the data by a specific attribute.
37468      * @param {String/RegExp} value Either string that the attribute value
37469      * should start with or a RegExp to test against the attribute
37470      * @param {String} attr (optional) The attribute passed in your node's attributes collection. Defaults to "text".
37471      * @param {TreeNode} startNode (optional) The node to start the filter at.
37472      */
37473     filter : function(value, attr, startNode){
37474         attr = attr || "text";
37475         var f;
37476         if(typeof value == "string"){
37477             var vlen = value.length;
37478             // auto clear empty filter
37479             if(vlen == 0 && this.clearBlank){
37480                 this.clear();
37481                 return;
37482             }
37483             value = value.toLowerCase();
37484             f = function(n){
37485                 return n.attributes[attr].substr(0, vlen).toLowerCase() == value;
37486             };
37487         }else if(value.exec){ // regex?
37488             f = function(n){
37489                 return value.test(n.attributes[attr]);
37490             };
37491         }else{
37492             throw 'Illegal filter type, must be string or regex';
37493         }
37494         this.filterBy(f, null, startNode);
37495         },
37496
37497     /**
37498      * Filter by a function. The passed function will be called with each
37499      * node in the tree (or from the startNode). If the function returns true, the node is kept
37500      * otherwise it is filtered. If a node is filtered, its children are also filtered.
37501      * @param {Function} fn The filter function
37502      * @param {Object} scope (optional) The scope of the function (defaults to the current node)
37503      */
37504     filterBy : function(fn, scope, startNode){
37505         startNode = startNode || this.tree.root;
37506         if(this.autoClear){
37507             this.clear();
37508         }
37509         var af = this.filtered, rv = this.reverse;
37510         var f = function(n){
37511             if(n == startNode){
37512                 return true;
37513             }
37514             if(af[n.id]){
37515                 return false;
37516             }
37517             var m = fn.call(scope || n, n);
37518             if(!m || rv){
37519                 af[n.id] = n;
37520                 n.ui.hide();
37521                 return false;
37522             }
37523             return true;
37524         };
37525         startNode.cascade(f);
37526         if(this.remove){
37527            for(var id in af){
37528                if(typeof id != "function"){
37529                    var n = af[id];
37530                    if(n && n.parentNode){
37531                        n.parentNode.removeChild(n);
37532                    }
37533                }
37534            }
37535         }
37536     },
37537
37538     /**
37539      * Clears the current filter. Note: with the "remove" option
37540      * set a filter cannot be cleared.
37541      */
37542     clear : function(){
37543         var t = this.tree;
37544         var af = this.filtered;
37545         for(var id in af){
37546             if(typeof id != "function"){
37547                 var n = af[id];
37548                 if(n){
37549                     n.ui.show();
37550                 }
37551             }
37552         }
37553         this.filtered = {};
37554     }
37555 };
37556 /*
37557  * Based on:
37558  * Ext JS Library 1.1.1
37559  * Copyright(c) 2006-2007, Ext JS, LLC.
37560  *
37561  * Originally Released Under LGPL - original licence link has changed is not relivant.
37562  *
37563  * Fork - LGPL
37564  * <script type="text/javascript">
37565  */
37566  
37567
37568 /**
37569  * @class Roo.tree.TreeSorter
37570  * Provides sorting of nodes in a TreePanel
37571  * 
37572  * @cfg {Boolean} folderSort True to sort leaf nodes under non leaf nodes
37573  * @cfg {String} property The named attribute on the node to sort by (defaults to text)
37574  * @cfg {String} dir The direction to sort (asc or desc) (defaults to asc)
37575  * @cfg {String} leafAttr The attribute used to determine leaf nodes in folder sort (defaults to "leaf")
37576  * @cfg {Boolean} caseSensitive true for case sensitive sort (defaults to false)
37577  * @cfg {Function} sortType A custom "casting" function used to convert node values before sorting
37578  * @constructor
37579  * @param {TreePanel} tree
37580  * @param {Object} config
37581  */
37582 Roo.tree.TreeSorter = function(tree, config){
37583     Roo.apply(this, config);
37584     tree.on("beforechildrenrendered", this.doSort, this);
37585     tree.on("append", this.updateSort, this);
37586     tree.on("insert", this.updateSort, this);
37587     
37588     var dsc = this.dir && this.dir.toLowerCase() == "desc";
37589     var p = this.property || "text";
37590     var sortType = this.sortType;
37591     var fs = this.folderSort;
37592     var cs = this.caseSensitive === true;
37593     var leafAttr = this.leafAttr || 'leaf';
37594
37595     this.sortFn = function(n1, n2){
37596         if(fs){
37597             if(n1.attributes[leafAttr] && !n2.attributes[leafAttr]){
37598                 return 1;
37599             }
37600             if(!n1.attributes[leafAttr] && n2.attributes[leafAttr]){
37601                 return -1;
37602             }
37603         }
37604         var v1 = sortType ? sortType(n1) : (cs ? n1.attributes[p] : n1.attributes[p].toUpperCase());
37605         var v2 = sortType ? sortType(n2) : (cs ? n2.attributes[p] : n2.attributes[p].toUpperCase());
37606         if(v1 < v2){
37607                         return dsc ? +1 : -1;
37608                 }else if(v1 > v2){
37609                         return dsc ? -1 : +1;
37610         }else{
37611                 return 0;
37612         }
37613     };
37614 };
37615
37616 Roo.tree.TreeSorter.prototype = {
37617     doSort : function(node){
37618         node.sort(this.sortFn);
37619     },
37620     
37621     compareNodes : function(n1, n2){
37622         return (n1.text.toUpperCase() > n2.text.toUpperCase() ? 1 : -1);
37623     },
37624     
37625     updateSort : function(tree, node){
37626         if(node.childrenRendered){
37627             this.doSort.defer(1, this, [node]);
37628         }
37629     }
37630 };/*
37631  * Based on:
37632  * Ext JS Library 1.1.1
37633  * Copyright(c) 2006-2007, Ext JS, LLC.
37634  *
37635  * Originally Released Under LGPL - original licence link has changed is not relivant.
37636  *
37637  * Fork - LGPL
37638  * <script type="text/javascript">
37639  */
37640
37641 if(Roo.dd.DropZone){
37642     
37643 Roo.tree.TreeDropZone = function(tree, config){
37644     this.allowParentInsert = false;
37645     this.allowContainerDrop = false;
37646     this.appendOnly = false;
37647     Roo.tree.TreeDropZone.superclass.constructor.call(this, tree.innerCt, config);
37648     this.tree = tree;
37649     this.lastInsertClass = "x-tree-no-status";
37650     this.dragOverData = {};
37651 };
37652
37653 Roo.extend(Roo.tree.TreeDropZone, Roo.dd.DropZone, {
37654     ddGroup : "TreeDD",
37655     scroll:  true,
37656     
37657     expandDelay : 1000,
37658     
37659     expandNode : function(node){
37660         if(node.hasChildNodes() && !node.isExpanded()){
37661             node.expand(false, null, this.triggerCacheRefresh.createDelegate(this));
37662         }
37663     },
37664     
37665     queueExpand : function(node){
37666         this.expandProcId = this.expandNode.defer(this.expandDelay, this, [node]);
37667     },
37668     
37669     cancelExpand : function(){
37670         if(this.expandProcId){
37671             clearTimeout(this.expandProcId);
37672             this.expandProcId = false;
37673         }
37674     },
37675     
37676     isValidDropPoint : function(n, pt, dd, e, data){
37677         if(!n || !data){ return false; }
37678         var targetNode = n.node;
37679         var dropNode = data.node;
37680         // default drop rules
37681         if(!(targetNode && targetNode.isTarget && pt)){
37682             return false;
37683         }
37684         if(pt == "append" && targetNode.allowChildren === false){
37685             return false;
37686         }
37687         if((pt == "above" || pt == "below") && (targetNode.parentNode && targetNode.parentNode.allowChildren === false)){
37688             return false;
37689         }
37690         if(dropNode && (targetNode == dropNode || dropNode.contains(targetNode))){
37691             return false;
37692         }
37693         // reuse the object
37694         var overEvent = this.dragOverData;
37695         overEvent.tree = this.tree;
37696         overEvent.target = targetNode;
37697         overEvent.data = data;
37698         overEvent.point = pt;
37699         overEvent.source = dd;
37700         overEvent.rawEvent = e;
37701         overEvent.dropNode = dropNode;
37702         overEvent.cancel = false;  
37703         var result = this.tree.fireEvent("nodedragover", overEvent);
37704         return overEvent.cancel === false && result !== false;
37705     },
37706     
37707     getDropPoint : function(e, n, dd)
37708     {
37709         var tn = n.node;
37710         if(tn.isRoot){
37711             return tn.allowChildren !== false ? "append" : false; // always append for root
37712         }
37713         var dragEl = n.ddel;
37714         var t = Roo.lib.Dom.getY(dragEl), b = t + dragEl.offsetHeight;
37715         var y = Roo.lib.Event.getPageY(e);
37716         //var noAppend = tn.allowChildren === false || tn.isLeaf();
37717         
37718         // we may drop nodes anywhere, as long as allowChildren has not been set to false..
37719         var noAppend = tn.allowChildren === false;
37720         if(this.appendOnly || tn.parentNode.allowChildren === false){
37721             return noAppend ? false : "append";
37722         }
37723         var noBelow = false;
37724         if(!this.allowParentInsert){
37725             noBelow = tn.hasChildNodes() && tn.isExpanded();
37726         }
37727         var q = (b - t) / (noAppend ? 2 : 3);
37728         if(y >= t && y < (t + q)){
37729             return "above";
37730         }else if(!noBelow && (noAppend || y >= b-q && y <= b)){
37731             return "below";
37732         }else{
37733             return "append";
37734         }
37735     },
37736     
37737     onNodeEnter : function(n, dd, e, data)
37738     {
37739         this.cancelExpand();
37740     },
37741     
37742     onNodeOver : function(n, dd, e, data)
37743     {
37744        
37745         var pt = this.getDropPoint(e, n, dd);
37746         var node = n.node;
37747         
37748         // auto node expand check
37749         if(!this.expandProcId && pt == "append" && node.hasChildNodes() && !n.node.isExpanded()){
37750             this.queueExpand(node);
37751         }else if(pt != "append"){
37752             this.cancelExpand();
37753         }
37754         
37755         // set the insert point style on the target node
37756         var returnCls = this.dropNotAllowed;
37757         if(this.isValidDropPoint(n, pt, dd, e, data)){
37758            if(pt){
37759                var el = n.ddel;
37760                var cls;
37761                if(pt == "above"){
37762                    returnCls = n.node.isFirst() ? "x-tree-drop-ok-above" : "x-tree-drop-ok-between";
37763                    cls = "x-tree-drag-insert-above";
37764                }else if(pt == "below"){
37765                    returnCls = n.node.isLast() ? "x-tree-drop-ok-below" : "x-tree-drop-ok-between";
37766                    cls = "x-tree-drag-insert-below";
37767                }else{
37768                    returnCls = "x-tree-drop-ok-append";
37769                    cls = "x-tree-drag-append";
37770                }
37771                if(this.lastInsertClass != cls){
37772                    Roo.fly(el).replaceClass(this.lastInsertClass, cls);
37773                    this.lastInsertClass = cls;
37774                }
37775            }
37776        }
37777        return returnCls;
37778     },
37779     
37780     onNodeOut : function(n, dd, e, data){
37781         
37782         this.cancelExpand();
37783         this.removeDropIndicators(n);
37784     },
37785     
37786     onNodeDrop : function(n, dd, e, data){
37787         var point = this.getDropPoint(e, n, dd);
37788         var targetNode = n.node;
37789         targetNode.ui.startDrop();
37790         if(!this.isValidDropPoint(n, point, dd, e, data)){
37791             targetNode.ui.endDrop();
37792             return false;
37793         }
37794         // first try to find the drop node
37795         var dropNode = data.node || (dd.getTreeNode ? dd.getTreeNode(data, targetNode, point, e) : null);
37796         var dropEvent = {
37797             tree : this.tree,
37798             target: targetNode,
37799             data: data,
37800             point: point,
37801             source: dd,
37802             rawEvent: e,
37803             dropNode: dropNode,
37804             cancel: !dropNode   
37805         };
37806         var retval = this.tree.fireEvent("beforenodedrop", dropEvent);
37807         if(retval === false || dropEvent.cancel === true || !dropEvent.dropNode){
37808             targetNode.ui.endDrop();
37809             return false;
37810         }
37811         // allow target changing
37812         targetNode = dropEvent.target;
37813         if(point == "append" && !targetNode.isExpanded()){
37814             targetNode.expand(false, null, function(){
37815                 this.completeDrop(dropEvent);
37816             }.createDelegate(this));
37817         }else{
37818             this.completeDrop(dropEvent);
37819         }
37820         return true;
37821     },
37822     
37823     completeDrop : function(de){
37824         var ns = de.dropNode, p = de.point, t = de.target;
37825         if(!(ns instanceof Array)){
37826             ns = [ns];
37827         }
37828         var n;
37829         for(var i = 0, len = ns.length; i < len; i++){
37830             n = ns[i];
37831             if(p == "above"){
37832                 t.parentNode.insertBefore(n, t);
37833             }else if(p == "below"){
37834                 t.parentNode.insertBefore(n, t.nextSibling);
37835             }else{
37836                 t.appendChild(n);
37837             }
37838         }
37839         n.ui.focus();
37840         if(this.tree.hlDrop){
37841             n.ui.highlight();
37842         }
37843         t.ui.endDrop();
37844         this.tree.fireEvent("nodedrop", de);
37845     },
37846     
37847     afterNodeMoved : function(dd, data, e, targetNode, dropNode){
37848         if(this.tree.hlDrop){
37849             dropNode.ui.focus();
37850             dropNode.ui.highlight();
37851         }
37852         this.tree.fireEvent("nodedrop", this.tree, targetNode, data, dd, e);
37853     },
37854     
37855     getTree : function(){
37856         return this.tree;
37857     },
37858     
37859     removeDropIndicators : function(n){
37860         if(n && n.ddel){
37861             var el = n.ddel;
37862             Roo.fly(el).removeClass([
37863                     "x-tree-drag-insert-above",
37864                     "x-tree-drag-insert-below",
37865                     "x-tree-drag-append"]);
37866             this.lastInsertClass = "_noclass";
37867         }
37868     },
37869     
37870     beforeDragDrop : function(target, e, id){
37871         this.cancelExpand();
37872         return true;
37873     },
37874     
37875     afterRepair : function(data){
37876         if(data && Roo.enableFx){
37877             data.node.ui.highlight();
37878         }
37879         this.hideProxy();
37880     } 
37881     
37882 });
37883
37884 }
37885 /*
37886  * Based on:
37887  * Ext JS Library 1.1.1
37888  * Copyright(c) 2006-2007, Ext JS, LLC.
37889  *
37890  * Originally Released Under LGPL - original licence link has changed is not relivant.
37891  *
37892  * Fork - LGPL
37893  * <script type="text/javascript">
37894  */
37895  
37896
37897 if(Roo.dd.DragZone){
37898 Roo.tree.TreeDragZone = function(tree, config){
37899     Roo.tree.TreeDragZone.superclass.constructor.call(this, tree.getTreeEl(), config);
37900     this.tree = tree;
37901 };
37902
37903 Roo.extend(Roo.tree.TreeDragZone, Roo.dd.DragZone, {
37904     ddGroup : "TreeDD",
37905    
37906     onBeforeDrag : function(data, e){
37907         var n = data.node;
37908         return n && n.draggable && !n.disabled;
37909     },
37910      
37911     
37912     onInitDrag : function(e){
37913         var data = this.dragData;
37914         this.tree.getSelectionModel().select(data.node);
37915         this.proxy.update("");
37916         data.node.ui.appendDDGhost(this.proxy.ghost.dom);
37917         this.tree.fireEvent("startdrag", this.tree, data.node, e);
37918     },
37919     
37920     getRepairXY : function(e, data){
37921         return data.node.ui.getDDRepairXY();
37922     },
37923     
37924     onEndDrag : function(data, e){
37925         this.tree.fireEvent("enddrag", this.tree, data.node, e);
37926         
37927         
37928     },
37929     
37930     onValidDrop : function(dd, e, id){
37931         this.tree.fireEvent("dragdrop", this.tree, this.dragData.node, dd, e);
37932         this.hideProxy();
37933     },
37934     
37935     beforeInvalidDrop : function(e, id){
37936         // this scrolls the original position back into view
37937         var sm = this.tree.getSelectionModel();
37938         sm.clearSelections();
37939         sm.select(this.dragData.node);
37940     }
37941 });
37942 }/*
37943  * Based on:
37944  * Ext JS Library 1.1.1
37945  * Copyright(c) 2006-2007, Ext JS, LLC.
37946  *
37947  * Originally Released Under LGPL - original licence link has changed is not relivant.
37948  *
37949  * Fork - LGPL
37950  * <script type="text/javascript">
37951  */
37952 /**
37953  * @class Roo.tree.TreeEditor
37954  * @extends Roo.Editor
37955  * Provides editor functionality for inline tree node editing.  Any valid {@link Roo.form.Field} can be used
37956  * as the editor field.
37957  * @constructor
37958  * @param {Object} config (used to be the tree panel.)
37959  * @param {Object} oldconfig DEPRECIATED Either a prebuilt {@link Roo.form.Field} instance or a Field config object
37960  * 
37961  * @cfg {Roo.tree.TreePanel} tree The tree to bind to.
37962  * @cfg {Roo.form.TextField} field [required] The field configuration
37963  *
37964  * 
37965  */
37966 Roo.tree.TreeEditor = function(config, oldconfig) { // was -- (tree, config){
37967     var tree = config;
37968     var field;
37969     if (oldconfig) { // old style..
37970         field = oldconfig.events ? oldconfig : new Roo.form.TextField(oldconfig);
37971     } else {
37972         // new style..
37973         tree = config.tree;
37974         config.field = config.field  || {};
37975         config.field.xtype = 'TextField';
37976         field = Roo.factory(config.field, Roo.form);
37977     }
37978     config = config || {};
37979     
37980     
37981     this.addEvents({
37982         /**
37983          * @event beforenodeedit
37984          * Fires when editing is initiated, but before the value changes.  Editing can be canceled by returning
37985          * false from the handler of this event.
37986          * @param {Editor} this
37987          * @param {Roo.tree.Node} node 
37988          */
37989         "beforenodeedit" : true
37990     });
37991     
37992     //Roo.log(config);
37993     Roo.tree.TreeEditor.superclass.constructor.call(this, field, config);
37994
37995     this.tree = tree;
37996
37997     tree.on('beforeclick', this.beforeNodeClick, this);
37998     tree.getTreeEl().on('mousedown', this.hide, this);
37999     this.on('complete', this.updateNode, this);
38000     this.on('beforestartedit', this.fitToTree, this);
38001     this.on('startedit', this.bindScroll, this, {delay:10});
38002     this.on('specialkey', this.onSpecialKey, this);
38003 };
38004
38005 Roo.extend(Roo.tree.TreeEditor, Roo.Editor, {
38006     /**
38007      * @cfg {String} alignment
38008      * The position to align to (see {@link Roo.Element#alignTo} for more details, defaults to "l-l").
38009      */
38010     alignment: "l-l",
38011     // inherit
38012     autoSize: false,
38013     /**
38014      * @cfg {Boolean} hideEl
38015      * True to hide the bound element while the editor is displayed (defaults to false)
38016      */
38017     hideEl : false,
38018     /**
38019      * @cfg {String} cls
38020      * CSS class to apply to the editor (defaults to "x-small-editor x-tree-editor")
38021      */
38022     cls: "x-small-editor x-tree-editor",
38023     /**
38024      * @cfg {Boolean} shim
38025      * True to shim the editor if selects/iframes could be displayed beneath it (defaults to false)
38026      */
38027     shim:false,
38028     // inherit
38029     shadow:"frame",
38030     /**
38031      * @cfg {Number} maxWidth
38032      * The maximum width in pixels of the editor field (defaults to 250).  Note that if the maxWidth would exceed
38033      * the containing tree element's size, it will be automatically limited for you to the container width, taking
38034      * scroll and client offsets into account prior to each edit.
38035      */
38036     maxWidth: 250,
38037
38038     editDelay : 350,
38039
38040     // private
38041     fitToTree : function(ed, el){
38042         var td = this.tree.getTreeEl().dom, nd = el.dom;
38043         if(td.scrollLeft >  nd.offsetLeft){ // ensure the node left point is visible
38044             td.scrollLeft = nd.offsetLeft;
38045         }
38046         var w = Math.min(
38047                 this.maxWidth,
38048                 (td.clientWidth > 20 ? td.clientWidth : td.offsetWidth) - Math.max(0, nd.offsetLeft-td.scrollLeft) - /*cushion*/5);
38049         this.setSize(w, '');
38050         
38051         return this.fireEvent('beforenodeedit', this, this.editNode);
38052         
38053     },
38054
38055     // private
38056     triggerEdit : function(node){
38057         this.completeEdit();
38058         this.editNode = node;
38059         this.startEdit(node.ui.textNode, node.text);
38060     },
38061
38062     // private
38063     bindScroll : function(){
38064         this.tree.getTreeEl().on('scroll', this.cancelEdit, this);
38065     },
38066
38067     // private
38068     beforeNodeClick : function(node, e){
38069         var sinceLast = (this.lastClick ? this.lastClick.getElapsed() : 0);
38070         this.lastClick = new Date();
38071         if(sinceLast > this.editDelay && this.tree.getSelectionModel().isSelected(node)){
38072             e.stopEvent();
38073             this.triggerEdit(node);
38074             return false;
38075         }
38076         return true;
38077     },
38078
38079     // private
38080     updateNode : function(ed, value){
38081         this.tree.getTreeEl().un('scroll', this.cancelEdit, this);
38082         this.editNode.setText(value);
38083     },
38084
38085     // private
38086     onHide : function(){
38087         Roo.tree.TreeEditor.superclass.onHide.call(this);
38088         if(this.editNode){
38089             this.editNode.ui.focus();
38090         }
38091     },
38092
38093     // private
38094     onSpecialKey : function(field, e){
38095         var k = e.getKey();
38096         if(k == e.ESC){
38097             e.stopEvent();
38098             this.cancelEdit();
38099         }else if(k == e.ENTER && !e.hasModifier()){
38100             e.stopEvent();
38101             this.completeEdit();
38102         }
38103     }
38104 });//<Script type="text/javascript">
38105 /*
38106  * Based on:
38107  * Ext JS Library 1.1.1
38108  * Copyright(c) 2006-2007, Ext JS, LLC.
38109  *
38110  * Originally Released Under LGPL - original licence link has changed is not relivant.
38111  *
38112  * Fork - LGPL
38113  * <script type="text/javascript">
38114  */
38115  
38116 /**
38117  * Not documented??? - probably should be...
38118  */
38119
38120 Roo.tree.ColumnNodeUI = Roo.extend(Roo.tree.TreeNodeUI, {
38121     //focus: Roo.emptyFn, // prevent odd scrolling behavior
38122     
38123     renderElements : function(n, a, targetNode, bulkRender){
38124         //consel.log("renderElements?");
38125         this.indentMarkup = n.parentNode ? n.parentNode.ui.getChildIndent() : '';
38126
38127         var t = n.getOwnerTree();
38128         var tid = Pman.Tab.Document_TypesTree.tree.el.id;
38129         
38130         var cols = t.columns;
38131         var bw = t.borderWidth;
38132         var c = cols[0];
38133         var href = a.href ? a.href : Roo.isGecko ? "" : "#";
38134          var cb = typeof a.checked == "boolean";
38135         var tx = String.format('{0}',n.text || (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38136         var colcls = 'x-t-' + tid + '-c0';
38137         var buf = [
38138             '<li class="x-tree-node">',
38139             
38140                 
38141                 '<div class="x-tree-node-el ', a.cls,'">',
38142                     // extran...
38143                     '<div class="x-tree-col ', colcls, '" style="width:', c.width-bw, 'px;">',
38144                 
38145                 
38146                         '<span class="x-tree-node-indent">',this.indentMarkup,'</span>',
38147                         '<img src="', this.emptyIcon, '" class="x-tree-ec-icon  " />',
38148                         '<img src="', a.icon || this.emptyIcon, '" class="x-tree-node-icon',
38149                            (a.icon ? ' x-tree-node-inline-icon' : ''),
38150                            (a.iconCls ? ' '+a.iconCls : ''),
38151                            '" unselectable="on" />',
38152                         (cb ? ('<input class="x-tree-node-cb" type="checkbox" ' + 
38153                              (a.checked ? 'checked="checked" />' : ' />')) : ''),
38154                              
38155                         '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38156                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>',
38157                             '<span unselectable="on" qtip="' + tx + '">',
38158                              tx,
38159                              '</span></a>' ,
38160                     '</div>',
38161                      '<a class="x-tree-node-anchor" hidefocus="on" href="',href,'" tabIndex="1" ',
38162                             (a.hrefTarget ? ' target="' +a.hrefTarget + '"' : ''), '>'
38163                  ];
38164         for(var i = 1, len = cols.length; i < len; i++){
38165             c = cols[i];
38166             colcls = 'x-t-' + tid + '-c' +i;
38167             tx = String.format('{0}', (c.renderer ? c.renderer(a[c.dataIndex], n, a) : a[c.dataIndex]));
38168             buf.push('<div class="x-tree-col ', colcls, ' ' ,(c.cls?c.cls:''),'" style="width:',c.width-bw,'px;">',
38169                         '<div class="x-tree-col-text" qtip="' + tx +'">',tx,"</div>",
38170                       "</div>");
38171          }
38172          
38173          buf.push(
38174             '</a>',
38175             '<div class="x-clear"></div></div>',
38176             '<ul class="x-tree-node-ct" style="display:none;"></ul>',
38177             "</li>");
38178         
38179         if(bulkRender !== true && n.nextSibling && n.nextSibling.ui.getEl()){
38180             this.wrap = Roo.DomHelper.insertHtml("beforeBegin",
38181                                 n.nextSibling.ui.getEl(), buf.join(""));
38182         }else{
38183             this.wrap = Roo.DomHelper.insertHtml("beforeEnd", targetNode, buf.join(""));
38184         }
38185         var el = this.wrap.firstChild;
38186         this.elRow = el;
38187         this.elNode = el.firstChild;
38188         this.ranchor = el.childNodes[1];
38189         this.ctNode = this.wrap.childNodes[1];
38190         var cs = el.firstChild.childNodes;
38191         this.indentNode = cs[0];
38192         this.ecNode = cs[1];
38193         this.iconNode = cs[2];
38194         var index = 3;
38195         if(cb){
38196             this.checkbox = cs[3];
38197             index++;
38198         }
38199         this.anchor = cs[index];
38200         
38201         this.textNode = cs[index].firstChild;
38202         
38203         //el.on("click", this.onClick, this);
38204         //el.on("dblclick", this.onDblClick, this);
38205         
38206         
38207        // console.log(this);
38208     },
38209     initEvents : function(){
38210         Roo.tree.ColumnNodeUI.superclass.initEvents.call(this);
38211         
38212             
38213         var a = this.ranchor;
38214
38215         var el = Roo.get(a);
38216
38217         if(Roo.isOpera){ // opera render bug ignores the CSS
38218             el.setStyle("text-decoration", "none");
38219         }
38220
38221         el.on("click", this.onClick, this);
38222         el.on("dblclick", this.onDblClick, this);
38223         el.on("contextmenu", this.onContextMenu, this);
38224         
38225     },
38226     
38227     /*onSelectedChange : function(state){
38228         if(state){
38229             this.focus();
38230             this.addClass("x-tree-selected");
38231         }else{
38232             //this.blur();
38233             this.removeClass("x-tree-selected");
38234         }
38235     },*/
38236     addClass : function(cls){
38237         if(this.elRow){
38238             Roo.fly(this.elRow).addClass(cls);
38239         }
38240         
38241     },
38242     
38243     
38244     removeClass : function(cls){
38245         if(this.elRow){
38246             Roo.fly(this.elRow).removeClass(cls);
38247         }
38248     }
38249
38250     
38251     
38252 });//<Script type="text/javascript">
38253
38254 /*
38255  * Based on:
38256  * Ext JS Library 1.1.1
38257  * Copyright(c) 2006-2007, Ext JS, LLC.
38258  *
38259  * Originally Released Under LGPL - original licence link has changed is not relivant.
38260  *
38261  * Fork - LGPL
38262  * <script type="text/javascript">
38263  */
38264  
38265
38266 /**
38267  * @class Roo.tree.ColumnTree
38268  * @extends Roo.tree.TreePanel
38269  * @cfg {Object} columns  Including width, header, renderer, cls, dataIndex 
38270  * @cfg {int} borderWidth  compined right/left border allowance
38271  * @constructor
38272  * @param {String/HTMLElement/Element} el The container element
38273  * @param {Object} config
38274  */
38275 Roo.tree.ColumnTree =  function(el, config)
38276 {
38277    Roo.tree.ColumnTree.superclass.constructor.call(this, el , config);
38278    this.addEvents({
38279         /**
38280         * @event resize
38281         * Fire this event on a container when it resizes
38282         * @param {int} w Width
38283         * @param {int} h Height
38284         */
38285        "resize" : true
38286     });
38287     this.on('resize', this.onResize, this);
38288 };
38289
38290 Roo.extend(Roo.tree.ColumnTree, Roo.tree.TreePanel, {
38291     //lines:false,
38292     
38293     
38294     borderWidth: Roo.isBorderBox ? 0 : 2, 
38295     headEls : false,
38296     
38297     render : function(){
38298         // add the header.....
38299        
38300         Roo.tree.ColumnTree.superclass.render.apply(this);
38301         
38302         this.el.addClass('x-column-tree');
38303         
38304         this.headers = this.el.createChild(
38305             {cls:'x-tree-headers'},this.innerCt.dom);
38306    
38307         var cols = this.columns, c;
38308         var totalWidth = 0;
38309         this.headEls = [];
38310         var  len = cols.length;
38311         for(var i = 0; i < len; i++){
38312              c = cols[i];
38313              totalWidth += c.width;
38314             this.headEls.push(this.headers.createChild({
38315                  cls:'x-tree-hd ' + (c.cls?c.cls+'-hd':''),
38316                  cn: {
38317                      cls:'x-tree-hd-text',
38318                      html: c.header
38319                  },
38320                  style:'width:'+(c.width-this.borderWidth)+'px;'
38321              }));
38322         }
38323         this.headers.createChild({cls:'x-clear'});
38324         // prevent floats from wrapping when clipped
38325         this.headers.setWidth(totalWidth);
38326         //this.innerCt.setWidth(totalWidth);
38327         this.innerCt.setStyle({ overflow: 'auto' });
38328         this.onResize(this.width, this.height);
38329              
38330         
38331     },
38332     onResize : function(w,h)
38333     {
38334         this.height = h;
38335         this.width = w;
38336         // resize cols..
38337         this.innerCt.setWidth(this.width);
38338         this.innerCt.setHeight(this.height-20);
38339         
38340         // headers...
38341         var cols = this.columns, c;
38342         var totalWidth = 0;
38343         var expEl = false;
38344         var len = cols.length;
38345         for(var i = 0; i < len; i++){
38346             c = cols[i];
38347             if (this.autoExpandColumn !== false && c.dataIndex == this.autoExpandColumn) {
38348                 // it's the expander..
38349                 expEl  = this.headEls[i];
38350                 continue;
38351             }
38352             totalWidth += c.width;
38353             
38354         }
38355         if (expEl) {
38356             expEl.setWidth(  ((w - totalWidth)-this.borderWidth - 20));
38357         }
38358         this.headers.setWidth(w-20);
38359
38360         
38361         
38362         
38363     }
38364 });
38365 /*
38366  * Based on:
38367  * Ext JS Library 1.1.1
38368  * Copyright(c) 2006-2007, Ext JS, LLC.
38369  *
38370  * Originally Released Under LGPL - original licence link has changed is not relivant.
38371  *
38372  * Fork - LGPL
38373  * <script type="text/javascript">
38374  */
38375  
38376 /**
38377  * @class Roo.menu.Menu
38378  * @extends Roo.util.Observable
38379  * @children Roo.menu.BaseItem
38380  * A menu object.  This is the container to which you add all other menu items.  Menu can also serve a as a base class
38381  * when you want a specialzed menu based off of another component (like {@link Roo.menu.DateMenu} for example).
38382  * @constructor
38383  * Creates a new Menu
38384  * @param {Object} config Configuration options
38385  */
38386 Roo.menu.Menu = function(config){
38387     
38388     Roo.menu.Menu.superclass.constructor.call(this, config);
38389     
38390     this.id = this.id || Roo.id();
38391     this.addEvents({
38392         /**
38393          * @event beforeshow
38394          * Fires before this menu is displayed
38395          * @param {Roo.menu.Menu} this
38396          */
38397         beforeshow : true,
38398         /**
38399          * @event beforehide
38400          * Fires before this menu is hidden
38401          * @param {Roo.menu.Menu} this
38402          */
38403         beforehide : true,
38404         /**
38405          * @event show
38406          * Fires after this menu is displayed
38407          * @param {Roo.menu.Menu} this
38408          */
38409         show : true,
38410         /**
38411          * @event hide
38412          * Fires after this menu is hidden
38413          * @param {Roo.menu.Menu} this
38414          */
38415         hide : true,
38416         /**
38417          * @event click
38418          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
38419          * @param {Roo.menu.Menu} this
38420          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38421          * @param {Roo.EventObject} e
38422          */
38423         click : true,
38424         /**
38425          * @event mouseover
38426          * Fires when the mouse is hovering over this menu
38427          * @param {Roo.menu.Menu} this
38428          * @param {Roo.EventObject} e
38429          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38430          */
38431         mouseover : true,
38432         /**
38433          * @event mouseout
38434          * Fires when the mouse exits this menu
38435          * @param {Roo.menu.Menu} this
38436          * @param {Roo.EventObject} e
38437          * @param {Roo.menu.Item} menuItem The menu item that was clicked
38438          */
38439         mouseout : true,
38440         /**
38441          * @event itemclick
38442          * Fires when a menu item contained in this menu is clicked
38443          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
38444          * @param {Roo.EventObject} e
38445          */
38446         itemclick: true
38447     });
38448     if (this.registerMenu) {
38449         Roo.menu.MenuMgr.register(this);
38450     }
38451     
38452     var mis = this.items;
38453     this.items = new Roo.util.MixedCollection();
38454     if(mis){
38455         this.add.apply(this, mis);
38456     }
38457 };
38458
38459 Roo.extend(Roo.menu.Menu, Roo.util.Observable, {
38460     /**
38461      * @cfg {Number} minWidth The minimum width of the menu in pixels (defaults to 120)
38462      */
38463     minWidth : 120,
38464     /**
38465      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop"
38466      * for bottom-right shadow (defaults to "sides")
38467      */
38468     shadow : "sides",
38469     /**
38470      * @cfg {String} subMenuAlign The {@link Roo.Element#alignTo} anchor position value to use for submenus of
38471      * this menu (defaults to "tl-tr?")
38472      */
38473     subMenuAlign : "tl-tr?",
38474     /**
38475      * @cfg {String} defaultAlign The default {@link Roo.Element#alignTo) anchor position value for this menu
38476      * relative to its element of origin (defaults to "tl-bl?")
38477      */
38478     defaultAlign : "tl-bl?",
38479     /**
38480      * @cfg {Boolean} allowOtherMenus True to allow multiple menus to be displayed at the same time (defaults to false)
38481      */
38482     allowOtherMenus : false,
38483     /**
38484      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
38485      */
38486     registerMenu : true,
38487
38488     hidden:true,
38489
38490     // private
38491     render : function(){
38492         if(this.el){
38493             return;
38494         }
38495         var el = this.el = new Roo.Layer({
38496             cls: "x-menu",
38497             shadow:this.shadow,
38498             constrain: false,
38499             parentEl: this.parentEl || document.body,
38500             zindex:15000
38501         });
38502
38503         this.keyNav = new Roo.menu.MenuNav(this);
38504
38505         if(this.plain){
38506             el.addClass("x-menu-plain");
38507         }
38508         if(this.cls){
38509             el.addClass(this.cls);
38510         }
38511         // generic focus element
38512         this.focusEl = el.createChild({
38513             tag: "a", cls: "x-menu-focus", href: "#", onclick: "return false;", tabIndex:"-1"
38514         });
38515         var ul = el.createChild({tag: "ul", cls: "x-menu-list"});
38516         //disabling touch- as it's causing issues ..
38517         //ul.on(Roo.isTouch ? 'touchstart' : 'click'   , this.onClick, this);
38518         ul.on('click'   , this.onClick, this);
38519         
38520         
38521         ul.on("mouseover", this.onMouseOver, this);
38522         ul.on("mouseout", this.onMouseOut, this);
38523         this.items.each(function(item){
38524             if (item.hidden) {
38525                 return;
38526             }
38527             
38528             var li = document.createElement("li");
38529             li.className = "x-menu-list-item";
38530             ul.dom.appendChild(li);
38531             item.render(li, this);
38532         }, this);
38533         this.ul = ul;
38534         this.autoWidth();
38535     },
38536
38537     // private
38538     autoWidth : function(){
38539         var el = this.el, ul = this.ul;
38540         if(!el){
38541             return;
38542         }
38543         var w = this.width;
38544         if(w){
38545             el.setWidth(w);
38546         }else if(Roo.isIE){
38547             el.setWidth(this.minWidth);
38548             var t = el.dom.offsetWidth; // force recalc
38549             el.setWidth(ul.getWidth()+el.getFrameWidth("lr"));
38550         }
38551     },
38552
38553     // private
38554     delayAutoWidth : function(){
38555         if(this.rendered){
38556             if(!this.awTask){
38557                 this.awTask = new Roo.util.DelayedTask(this.autoWidth, this);
38558             }
38559             this.awTask.delay(20);
38560         }
38561     },
38562
38563     // private
38564     findTargetItem : function(e){
38565         var t = e.getTarget(".x-menu-list-item", this.ul,  true);
38566         if(t && t.menuItemId){
38567             return this.items.get(t.menuItemId);
38568         }
38569     },
38570
38571     // private
38572     onClick : function(e){
38573         Roo.log("menu.onClick");
38574         var t = this.findTargetItem(e);
38575         if(!t){
38576             return;
38577         }
38578         Roo.log(e);
38579         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
38580             if(t == this.activeItem && t.shouldDeactivate(e)){
38581                 this.activeItem.deactivate();
38582                 delete this.activeItem;
38583                 return;
38584             }
38585             if(t.canActivate){
38586                 this.setActiveItem(t, true);
38587             }
38588             return;
38589             
38590             
38591         }
38592         
38593         t.onClick(e);
38594         this.fireEvent("click", this, t, e);
38595     },
38596
38597     // private
38598     setActiveItem : function(item, autoExpand){
38599         if(item != this.activeItem){
38600             if(this.activeItem){
38601                 this.activeItem.deactivate();
38602             }
38603             this.activeItem = item;
38604             item.activate(autoExpand);
38605         }else if(autoExpand){
38606             item.expandMenu();
38607         }
38608     },
38609
38610     // private
38611     tryActivate : function(start, step){
38612         var items = this.items;
38613         for(var i = start, len = items.length; i >= 0 && i < len; i+= step){
38614             var item = items.get(i);
38615             if(!item.disabled && item.canActivate){
38616                 this.setActiveItem(item, false);
38617                 return item;
38618             }
38619         }
38620         return false;
38621     },
38622
38623     // private
38624     onMouseOver : function(e){
38625         var t;
38626         if(t = this.findTargetItem(e)){
38627             if(t.canActivate && !t.disabled){
38628                 this.setActiveItem(t, true);
38629             }
38630         }
38631         this.fireEvent("mouseover", this, e, t);
38632     },
38633
38634     // private
38635     onMouseOut : function(e){
38636         var t;
38637         if(t = this.findTargetItem(e)){
38638             if(t == this.activeItem && t.shouldDeactivate(e)){
38639                 this.activeItem.deactivate();
38640                 delete this.activeItem;
38641             }
38642         }
38643         this.fireEvent("mouseout", this, e, t);
38644     },
38645
38646     /**
38647      * Read-only.  Returns true if the menu is currently displayed, else false.
38648      * @type Boolean
38649      */
38650     isVisible : function(){
38651         return this.el && !this.hidden;
38652     },
38653
38654     /**
38655      * Displays this menu relative to another element
38656      * @param {String/HTMLElement/Roo.Element} element The element to align to
38657      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
38658      * the element (defaults to this.defaultAlign)
38659      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38660      */
38661     show : function(el, pos, parentMenu){
38662         this.parentMenu = parentMenu;
38663         if(!this.el){
38664             this.render();
38665         }
38666         this.fireEvent("beforeshow", this);
38667         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
38668     },
38669
38670     /**
38671      * Displays this menu at a specific xy position
38672      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
38673      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
38674      */
38675     showAt : function(xy, parentMenu, /* private: */_e){
38676         this.parentMenu = parentMenu;
38677         if(!this.el){
38678             this.render();
38679         }
38680         if(_e !== false){
38681             this.fireEvent("beforeshow", this);
38682             xy = this.el.adjustForConstraints(xy);
38683         }
38684         this.el.setXY(xy);
38685         this.el.show();
38686         this.hidden = false;
38687         this.focus();
38688         this.fireEvent("show", this);
38689     },
38690
38691     focus : function(){
38692         if(!this.hidden){
38693             this.doFocus.defer(50, this);
38694         }
38695     },
38696
38697     doFocus : function(){
38698         if(!this.hidden){
38699             this.focusEl.focus();
38700         }
38701     },
38702
38703     /**
38704      * Hides this menu and optionally all parent menus
38705      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
38706      */
38707     hide : function(deep){
38708         if(this.el && this.isVisible()){
38709             this.fireEvent("beforehide", this);
38710             if(this.activeItem){
38711                 this.activeItem.deactivate();
38712                 this.activeItem = null;
38713             }
38714             this.el.hide();
38715             this.hidden = true;
38716             this.fireEvent("hide", this);
38717         }
38718         if(deep === true && this.parentMenu){
38719             this.parentMenu.hide(true);
38720         }
38721     },
38722
38723     /**
38724      * Addds one or more items of any type supported by the Menu class, or that can be converted into menu items.
38725      * Any of the following are valid:
38726      * <ul>
38727      * <li>Any menu item object based on {@link Roo.menu.Item}</li>
38728      * <li>An HTMLElement object which will be converted to a menu item</li>
38729      * <li>A menu item config object that will be created as a new menu item</li>
38730      * <li>A string, which can either be '-' or 'separator' to add a menu separator, otherwise
38731      * it will be converted into a {@link Roo.menu.TextItem} and added</li>
38732      * </ul>
38733      * Usage:
38734      * <pre><code>
38735 // Create the menu
38736 var menu = new Roo.menu.Menu();
38737
38738 // Create a menu item to add by reference
38739 var menuItem = new Roo.menu.Item({ text: 'New Item!' });
38740
38741 // Add a bunch of items at once using different methods.
38742 // Only the last item added will be returned.
38743 var item = menu.add(
38744     menuItem,                // add existing item by ref
38745     'Dynamic Item',          // new TextItem
38746     '-',                     // new separator
38747     { text: 'Config Item' }  // new item by config
38748 );
38749 </code></pre>
38750      * @param {Mixed} args One or more menu items, menu item configs or other objects that can be converted to menu items
38751      * @return {Roo.menu.Item} The menu item that was added, or the last one if multiple items were added
38752      */
38753     add : function(){
38754         var a = arguments, l = a.length, item;
38755         for(var i = 0; i < l; i++){
38756             var el = a[i];
38757             if ((typeof(el) == "object") && el.xtype && el.xns) {
38758                 el = Roo.factory(el, Roo.menu);
38759             }
38760             
38761             if(el.render){ // some kind of Item
38762                 item = this.addItem(el);
38763             }else if(typeof el == "string"){ // string
38764                 if(el == "separator" || el == "-"){
38765                     item = this.addSeparator();
38766                 }else{
38767                     item = this.addText(el);
38768                 }
38769             }else if(el.tagName || el.el){ // element
38770                 item = this.addElement(el);
38771             }else if(typeof el == "object"){ // must be menu item config?
38772                 item = this.addMenuItem(el);
38773             }
38774         }
38775         return item;
38776     },
38777
38778     /**
38779      * Returns this menu's underlying {@link Roo.Element} object
38780      * @return {Roo.Element} The element
38781      */
38782     getEl : function(){
38783         if(!this.el){
38784             this.render();
38785         }
38786         return this.el;
38787     },
38788
38789     /**
38790      * Adds a separator bar to the menu
38791      * @return {Roo.menu.Item} The menu item that was added
38792      */
38793     addSeparator : function(){
38794         return this.addItem(new Roo.menu.Separator());
38795     },
38796
38797     /**
38798      * Adds an {@link Roo.Element} object to the menu
38799      * @param {String/HTMLElement/Roo.Element} el The element or DOM node to add, or its id
38800      * @return {Roo.menu.Item} The menu item that was added
38801      */
38802     addElement : function(el){
38803         return this.addItem(new Roo.menu.BaseItem(el));
38804     },
38805
38806     /**
38807      * Adds an existing object based on {@link Roo.menu.Item} to the menu
38808      * @param {Roo.menu.Item} item The menu item to add
38809      * @return {Roo.menu.Item} The menu item that was added
38810      */
38811     addItem : function(item){
38812         this.items.add(item);
38813         if(this.ul){
38814             var li = document.createElement("li");
38815             li.className = "x-menu-list-item";
38816             this.ul.dom.appendChild(li);
38817             item.render(li, this);
38818             this.delayAutoWidth();
38819         }
38820         return item;
38821     },
38822
38823     /**
38824      * Creates a new {@link Roo.menu.Item} based an the supplied config object and adds it to the menu
38825      * @param {Object} config A MenuItem config object
38826      * @return {Roo.menu.Item} The menu item that was added
38827      */
38828     addMenuItem : function(config){
38829         if(!(config instanceof Roo.menu.Item)){
38830             if(typeof config.checked == "boolean"){ // must be check menu item config?
38831                 config = new Roo.menu.CheckItem(config);
38832             }else{
38833                 config = new Roo.menu.Item(config);
38834             }
38835         }
38836         return this.addItem(config);
38837     },
38838
38839     /**
38840      * Creates a new {@link Roo.menu.TextItem} with the supplied text and adds it to the menu
38841      * @param {String} text The text to display in the menu item
38842      * @return {Roo.menu.Item} The menu item that was added
38843      */
38844     addText : function(text){
38845         return this.addItem(new Roo.menu.TextItem({ text : text }));
38846     },
38847
38848     /**
38849      * Inserts an existing object based on {@link Roo.menu.Item} to the menu at a specified index
38850      * @param {Number} index The index in the menu's list of current items where the new item should be inserted
38851      * @param {Roo.menu.Item} item The menu item to add
38852      * @return {Roo.menu.Item} The menu item that was added
38853      */
38854     insert : function(index, item){
38855         this.items.insert(index, item);
38856         if(this.ul){
38857             var li = document.createElement("li");
38858             li.className = "x-menu-list-item";
38859             this.ul.dom.insertBefore(li, this.ul.dom.childNodes[index]);
38860             item.render(li, this);
38861             this.delayAutoWidth();
38862         }
38863         return item;
38864     },
38865
38866     /**
38867      * Removes an {@link Roo.menu.Item} from the menu and destroys the object
38868      * @param {Roo.menu.Item} item The menu item to remove
38869      */
38870     remove : function(item){
38871         this.items.removeKey(item.id);
38872         item.destroy();
38873     },
38874
38875     /**
38876      * Removes and destroys all items in the menu
38877      */
38878     removeAll : function(){
38879         var f;
38880         while(f = this.items.first()){
38881             this.remove(f);
38882         }
38883     }
38884 });
38885
38886 // MenuNav is a private utility class used internally by the Menu
38887 Roo.menu.MenuNav = function(menu){
38888     Roo.menu.MenuNav.superclass.constructor.call(this, menu.el);
38889     this.scope = this.menu = menu;
38890 };
38891
38892 Roo.extend(Roo.menu.MenuNav, Roo.KeyNav, {
38893     doRelay : function(e, h){
38894         var k = e.getKey();
38895         if(!this.menu.activeItem && e.isNavKeyPress() && k != e.SPACE && k != e.RETURN){
38896             this.menu.tryActivate(0, 1);
38897             return false;
38898         }
38899         return h.call(this.scope || this, e, this.menu);
38900     },
38901
38902     up : function(e, m){
38903         if(!m.tryActivate(m.items.indexOf(m.activeItem)-1, -1)){
38904             m.tryActivate(m.items.length-1, -1);
38905         }
38906     },
38907
38908     down : function(e, m){
38909         if(!m.tryActivate(m.items.indexOf(m.activeItem)+1, 1)){
38910             m.tryActivate(0, 1);
38911         }
38912     },
38913
38914     right : function(e, m){
38915         if(m.activeItem){
38916             m.activeItem.expandMenu(true);
38917         }
38918     },
38919
38920     left : function(e, m){
38921         m.hide();
38922         if(m.parentMenu && m.parentMenu.activeItem){
38923             m.parentMenu.activeItem.activate();
38924         }
38925     },
38926
38927     enter : function(e, m){
38928         if(m.activeItem){
38929             e.stopPropagation();
38930             m.activeItem.onClick(e);
38931             m.fireEvent("click", this, m.activeItem);
38932             return true;
38933         }
38934     }
38935 });/*
38936  * Based on:
38937  * Ext JS Library 1.1.1
38938  * Copyright(c) 2006-2007, Ext JS, LLC.
38939  *
38940  * Originally Released Under LGPL - original licence link has changed is not relivant.
38941  *
38942  * Fork - LGPL
38943  * <script type="text/javascript">
38944  */
38945  
38946 /**
38947  * @class Roo.menu.MenuMgr
38948  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
38949  * @static
38950  */
38951 Roo.menu.MenuMgr = function(){
38952    var menus, active, groups = {}, attached = false, lastShow = new Date();
38953
38954    // private - called when first menu is created
38955    function init(){
38956        menus = {};
38957        active = new Roo.util.MixedCollection();
38958        Roo.get(document).addKeyListener(27, function(){
38959            if(active.length > 0){
38960                hideAll();
38961            }
38962        });
38963    }
38964
38965    // private
38966    function hideAll(){
38967        if(active && active.length > 0){
38968            var c = active.clone();
38969            c.each(function(m){
38970                m.hide();
38971            });
38972        }
38973    }
38974
38975    // private
38976    function onHide(m){
38977        active.remove(m);
38978        if(active.length < 1){
38979            Roo.get(document).un("mousedown", onMouseDown);
38980            attached = false;
38981        }
38982    }
38983
38984    // private
38985    function onShow(m){
38986        var last = active.last();
38987        lastShow = new Date();
38988        active.add(m);
38989        if(!attached){
38990            Roo.get(document).on("mousedown", onMouseDown);
38991            attached = true;
38992        }
38993        if(m.parentMenu){
38994           m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
38995           m.parentMenu.activeChild = m;
38996        }else if(last && last.isVisible()){
38997           m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
38998        }
38999    }
39000
39001    // private
39002    function onBeforeHide(m){
39003        if(m.activeChild){
39004            m.activeChild.hide();
39005        }
39006        if(m.autoHideTimer){
39007            clearTimeout(m.autoHideTimer);
39008            delete m.autoHideTimer;
39009        }
39010    }
39011
39012    // private
39013    function onBeforeShow(m){
39014        var pm = m.parentMenu;
39015        if(!pm && !m.allowOtherMenus){
39016            hideAll();
39017        }else if(pm && pm.activeChild && active != m){
39018            pm.activeChild.hide();
39019        }
39020    }
39021
39022    // private
39023    function onMouseDown(e){
39024        if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".x-menu")){
39025            hideAll();
39026        }
39027    }
39028
39029    // private
39030    function onBeforeCheck(mi, state){
39031        if(state){
39032            var g = groups[mi.group];
39033            for(var i = 0, l = g.length; i < l; i++){
39034                if(g[i] != mi){
39035                    g[i].setChecked(false);
39036                }
39037            }
39038        }
39039    }
39040
39041    return {
39042
39043        /**
39044         * Hides all menus that are currently visible
39045         */
39046        hideAll : function(){
39047             hideAll();  
39048        },
39049
39050        // private
39051        register : function(menu){
39052            if(!menus){
39053                init();
39054            }
39055            menus[menu.id] = menu;
39056            menu.on("beforehide", onBeforeHide);
39057            menu.on("hide", onHide);
39058            menu.on("beforeshow", onBeforeShow);
39059            menu.on("show", onShow);
39060            var g = menu.group;
39061            if(g && menu.events["checkchange"]){
39062                if(!groups[g]){
39063                    groups[g] = [];
39064                }
39065                groups[g].push(menu);
39066                menu.on("checkchange", onCheck);
39067            }
39068        },
39069
39070         /**
39071          * Returns a {@link Roo.menu.Menu} object
39072          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
39073          * be used to generate and return a new Menu instance.
39074          */
39075        get : function(menu){
39076            if(typeof menu == "string"){ // menu id
39077                return menus[menu];
39078            }else if(menu.events){  // menu instance
39079                return menu;
39080            }else if(typeof menu.length == 'number'){ // array of menu items?
39081                return new Roo.menu.Menu({items:menu});
39082            }else{ // otherwise, must be a config
39083                return new Roo.menu.Menu(menu);
39084            }
39085        },
39086
39087        // private
39088        unregister : function(menu){
39089            delete menus[menu.id];
39090            menu.un("beforehide", onBeforeHide);
39091            menu.un("hide", onHide);
39092            menu.un("beforeshow", onBeforeShow);
39093            menu.un("show", onShow);
39094            var g = menu.group;
39095            if(g && menu.events["checkchange"]){
39096                groups[g].remove(menu);
39097                menu.un("checkchange", onCheck);
39098            }
39099        },
39100
39101        // private
39102        registerCheckable : function(menuItem){
39103            var g = menuItem.group;
39104            if(g){
39105                if(!groups[g]){
39106                    groups[g] = [];
39107                }
39108                groups[g].push(menuItem);
39109                menuItem.on("beforecheckchange", onBeforeCheck);
39110            }
39111        },
39112
39113        // private
39114        unregisterCheckable : function(menuItem){
39115            var g = menuItem.group;
39116            if(g){
39117                groups[g].remove(menuItem);
39118                menuItem.un("beforecheckchange", onBeforeCheck);
39119            }
39120        }
39121    };
39122 }();/*
39123  * Based on:
39124  * Ext JS Library 1.1.1
39125  * Copyright(c) 2006-2007, Ext JS, LLC.
39126  *
39127  * Originally Released Under LGPL - original licence link has changed is not relivant.
39128  *
39129  * Fork - LGPL
39130  * <script type="text/javascript">
39131  */
39132  
39133
39134 /**
39135  * @class Roo.menu.BaseItem
39136  * @extends Roo.Component
39137  * @abstract
39138  * The base class for all items that render into menus.  BaseItem provides default rendering, activated state
39139  * management and base configuration options shared by all menu components.
39140  * @constructor
39141  * Creates a new BaseItem
39142  * @param {Object} config Configuration options
39143  */
39144 Roo.menu.BaseItem = function(config){
39145     Roo.menu.BaseItem.superclass.constructor.call(this, config);
39146
39147     this.addEvents({
39148         /**
39149          * @event click
39150          * Fires when this item is clicked
39151          * @param {Roo.menu.BaseItem} this
39152          * @param {Roo.EventObject} e
39153          */
39154         click: true,
39155         /**
39156          * @event activate
39157          * Fires when this item is activated
39158          * @param {Roo.menu.BaseItem} this
39159          */
39160         activate : true,
39161         /**
39162          * @event deactivate
39163          * Fires when this item is deactivated
39164          * @param {Roo.menu.BaseItem} this
39165          */
39166         deactivate : true
39167     });
39168
39169     if(this.handler){
39170         this.on("click", this.handler, this.scope, true);
39171     }
39172 };
39173
39174 Roo.extend(Roo.menu.BaseItem, Roo.Component, {
39175     /**
39176      * @cfg {Function} handler
39177      * A function that will handle the click event of this menu item (defaults to undefined)
39178      */
39179     /**
39180      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to false)
39181      */
39182     canActivate : false,
39183     
39184      /**
39185      * @cfg {Boolean} hidden True to prevent creation of this menu item (defaults to false)
39186      */
39187     hidden: false,
39188     
39189     /**
39190      * @cfg {String} activeClass The CSS class to use when the item becomes activated (defaults to "x-menu-item-active")
39191      */
39192     activeClass : "x-menu-item-active",
39193     /**
39194      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to true)
39195      */
39196     hideOnClick : true,
39197     /**
39198      * @cfg {Number} hideDelay Length of time in milliseconds to wait before hiding after a click (defaults to 100)
39199      */
39200     hideDelay : 100,
39201
39202     // private
39203     ctype: "Roo.menu.BaseItem",
39204
39205     // private
39206     actionMode : "container",
39207
39208     // private
39209     render : function(container, parentMenu){
39210         this.parentMenu = parentMenu;
39211         Roo.menu.BaseItem.superclass.render.call(this, container);
39212         this.container.menuItemId = this.id;
39213     },
39214
39215     // private
39216     onRender : function(container, position){
39217         this.el = Roo.get(this.el);
39218         container.dom.appendChild(this.el.dom);
39219     },
39220
39221     // private
39222     onClick : function(e){
39223         if(!this.disabled && this.fireEvent("click", this, e) !== false
39224                 && this.parentMenu.fireEvent("itemclick", this, e) !== false){
39225             this.handleClick(e);
39226         }else{
39227             e.stopEvent();
39228         }
39229     },
39230
39231     // private
39232     activate : function(){
39233         if(this.disabled){
39234             return false;
39235         }
39236         var li = this.container;
39237         li.addClass(this.activeClass);
39238         this.region = li.getRegion().adjust(2, 2, -2, -2);
39239         this.fireEvent("activate", this);
39240         return true;
39241     },
39242
39243     // private
39244     deactivate : function(){
39245         this.container.removeClass(this.activeClass);
39246         this.fireEvent("deactivate", this);
39247     },
39248
39249     // private
39250     shouldDeactivate : function(e){
39251         return !this.region || !this.region.contains(e.getPoint());
39252     },
39253
39254     // private
39255     handleClick : function(e){
39256         if(this.hideOnClick){
39257             this.parentMenu.hide.defer(this.hideDelay, this.parentMenu, [true]);
39258         }
39259     },
39260
39261     // private
39262     expandMenu : function(autoActivate){
39263         // do nothing
39264     },
39265
39266     // private
39267     hideMenu : function(){
39268         // do nothing
39269     }
39270 });/*
39271  * Based on:
39272  * Ext JS Library 1.1.1
39273  * Copyright(c) 2006-2007, Ext JS, LLC.
39274  *
39275  * Originally Released Under LGPL - original licence link has changed is not relivant.
39276  *
39277  * Fork - LGPL
39278  * <script type="text/javascript">
39279  */
39280  
39281 /**
39282  * @class Roo.menu.Adapter
39283  * @extends Roo.menu.BaseItem
39284  * @abstract
39285  * 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.
39286  * It provides basic rendering, activation management and enable/disable logic required to work in menus.
39287  * @constructor
39288  * Creates a new Adapter
39289  * @param {Object} config Configuration options
39290  */
39291 Roo.menu.Adapter = function(component, config){
39292     Roo.menu.Adapter.superclass.constructor.call(this, config);
39293     this.component = component;
39294 };
39295 Roo.extend(Roo.menu.Adapter, Roo.menu.BaseItem, {
39296     // private
39297     canActivate : true,
39298
39299     // private
39300     onRender : function(container, position){
39301         this.component.render(container);
39302         this.el = this.component.getEl();
39303     },
39304
39305     // private
39306     activate : function(){
39307         if(this.disabled){
39308             return false;
39309         }
39310         this.component.focus();
39311         this.fireEvent("activate", this);
39312         return true;
39313     },
39314
39315     // private
39316     deactivate : function(){
39317         this.fireEvent("deactivate", this);
39318     },
39319
39320     // private
39321     disable : function(){
39322         this.component.disable();
39323         Roo.menu.Adapter.superclass.disable.call(this);
39324     },
39325
39326     // private
39327     enable : function(){
39328         this.component.enable();
39329         Roo.menu.Adapter.superclass.enable.call(this);
39330     }
39331 });/*
39332  * Based on:
39333  * Ext JS Library 1.1.1
39334  * Copyright(c) 2006-2007, Ext JS, LLC.
39335  *
39336  * Originally Released Under LGPL - original licence link has changed is not relivant.
39337  *
39338  * Fork - LGPL
39339  * <script type="text/javascript">
39340  */
39341
39342 /**
39343  * @class Roo.menu.TextItem
39344  * @extends Roo.menu.BaseItem
39345  * Adds a static text string to a menu, usually used as either a heading or group separator.
39346  * Note: old style constructor with text is still supported.
39347  * 
39348  * @constructor
39349  * Creates a new TextItem
39350  * @param {Object} cfg Configuration
39351  */
39352 Roo.menu.TextItem = function(cfg){
39353     if (typeof(cfg) == 'string') {
39354         this.text = cfg;
39355     } else {
39356         Roo.apply(this,cfg);
39357     }
39358     
39359     Roo.menu.TextItem.superclass.constructor.call(this);
39360 };
39361
39362 Roo.extend(Roo.menu.TextItem, Roo.menu.BaseItem, {
39363     /**
39364      * @cfg {String} text Text to show on item.
39365      */
39366     text : '',
39367     
39368     /**
39369      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39370      */
39371     hideOnClick : false,
39372     /**
39373      * @cfg {String} itemCls The default CSS class to use for text items (defaults to "x-menu-text")
39374      */
39375     itemCls : "x-menu-text",
39376
39377     // private
39378     onRender : function(){
39379         var s = document.createElement("span");
39380         s.className = this.itemCls;
39381         s.innerHTML = this.text;
39382         this.el = s;
39383         Roo.menu.TextItem.superclass.onRender.apply(this, arguments);
39384     }
39385 });/*
39386  * Based on:
39387  * Ext JS Library 1.1.1
39388  * Copyright(c) 2006-2007, Ext JS, LLC.
39389  *
39390  * Originally Released Under LGPL - original licence link has changed is not relivant.
39391  *
39392  * Fork - LGPL
39393  * <script type="text/javascript">
39394  */
39395
39396 /**
39397  * @class Roo.menu.Separator
39398  * @extends Roo.menu.BaseItem
39399  * Adds a separator bar to a menu, used to divide logical groups of menu items. Generally you will
39400  * add one of these by using "-" in you call to add() or in your items config rather than creating one directly.
39401  * @constructor
39402  * @param {Object} config Configuration options
39403  */
39404 Roo.menu.Separator = function(config){
39405     Roo.menu.Separator.superclass.constructor.call(this, config);
39406 };
39407
39408 Roo.extend(Roo.menu.Separator, Roo.menu.BaseItem, {
39409     /**
39410      * @cfg {String} itemCls The default CSS class to use for separators (defaults to "x-menu-sep")
39411      */
39412     itemCls : "x-menu-sep",
39413     /**
39414      * @cfg {Boolean} hideOnClick True to hide the containing menu after this item is clicked (defaults to false)
39415      */
39416     hideOnClick : false,
39417
39418     // private
39419     onRender : function(li){
39420         var s = document.createElement("span");
39421         s.className = this.itemCls;
39422         s.innerHTML = "&#160;";
39423         this.el = s;
39424         li.addClass("x-menu-sep-li");
39425         Roo.menu.Separator.superclass.onRender.apply(this, arguments);
39426     }
39427 });/*
39428  * Based on:
39429  * Ext JS Library 1.1.1
39430  * Copyright(c) 2006-2007, Ext JS, LLC.
39431  *
39432  * Originally Released Under LGPL - original licence link has changed is not relivant.
39433  *
39434  * Fork - LGPL
39435  * <script type="text/javascript">
39436  */
39437 /**
39438  * @class Roo.menu.Item
39439  * @extends Roo.menu.BaseItem
39440  * A base class for all menu items that require menu-related functionality (like sub-menus) and are not static
39441  * display items.  Item extends the base functionality of {@link Roo.menu.BaseItem} by adding menu-specific
39442  * activation and click handling.
39443  * @constructor
39444  * Creates a new Item
39445  * @param {Object} config Configuration options
39446  */
39447 Roo.menu.Item = function(config){
39448     Roo.menu.Item.superclass.constructor.call(this, config);
39449     if(this.menu){
39450         this.menu = Roo.menu.MenuMgr.get(this.menu);
39451     }
39452 };
39453 Roo.extend(Roo.menu.Item, Roo.menu.BaseItem, {
39454     /**
39455      * @cfg {Roo.menu.Menu} menu
39456      * A Sub menu
39457      */
39458     /**
39459      * @cfg {String} text
39460      * The text to show on the menu item.
39461      */
39462     text: '',
39463      /**
39464      * @cfg {String} HTML to render in menu
39465      * The text to show on the menu item (HTML version).
39466      */
39467     html: '',
39468     /**
39469      * @cfg {String} icon
39470      * The path to an icon to display in this menu item (defaults to Roo.BLANK_IMAGE_URL)
39471      */
39472     icon: undefined,
39473     /**
39474      * @cfg {String} itemCls The default CSS class to use for menu items (defaults to "x-menu-item")
39475      */
39476     itemCls : "x-menu-item",
39477     /**
39478      * @cfg {Boolean} canActivate True if this item can be visually activated (defaults to true)
39479      */
39480     canActivate : true,
39481     /**
39482      * @cfg {Number} showDelay Length of time in milliseconds to wait before showing this item (defaults to 200)
39483      */
39484     showDelay: 200,
39485     // doc'd in BaseItem
39486     hideDelay: 200,
39487
39488     // private
39489     ctype: "Roo.menu.Item",
39490     
39491     // private
39492     onRender : function(container, position){
39493         var el = document.createElement("a");
39494         el.hideFocus = true;
39495         el.unselectable = "on";
39496         el.href = this.href || "#";
39497         if(this.hrefTarget){
39498             el.target = this.hrefTarget;
39499         }
39500         el.className = this.itemCls + (this.menu ?  " x-menu-item-arrow" : "") + (this.cls ?  " " + this.cls : "");
39501         
39502         var html = this.html.length ? this.html  : String.format('{0}',this.text);
39503         
39504         el.innerHTML = String.format(
39505                 '<img src="{0}" class="x-menu-item-icon {1}" />' + html,
39506                 this.icon || Roo.BLANK_IMAGE_URL, this.iconCls || '');
39507         this.el = el;
39508         Roo.menu.Item.superclass.onRender.call(this, container, position);
39509     },
39510
39511     /**
39512      * Sets the text to display in this menu item
39513      * @param {String} text The text to display
39514      * @param {Boolean} isHTML true to indicate text is pure html.
39515      */
39516     setText : function(text, isHTML){
39517         if (isHTML) {
39518             this.html = text;
39519         } else {
39520             this.text = text;
39521             this.html = '';
39522         }
39523         if(this.rendered){
39524             var html = this.html.length ? this.html  : String.format('{0}',this.text);
39525      
39526             this.el.update(String.format(
39527                 '<img src="{0}" class="x-menu-item-icon {2}">' + html,
39528                 this.icon || Roo.BLANK_IMAGE_URL, this.text, this.iconCls || ''));
39529             this.parentMenu.autoWidth();
39530         }
39531     },
39532
39533     // private
39534     handleClick : function(e){
39535         if(!this.href){ // if no link defined, stop the event automatically
39536             e.stopEvent();
39537         }
39538         Roo.menu.Item.superclass.handleClick.apply(this, arguments);
39539     },
39540
39541     // private
39542     activate : function(autoExpand){
39543         if(Roo.menu.Item.superclass.activate.apply(this, arguments)){
39544             this.focus();
39545             if(autoExpand){
39546                 this.expandMenu();
39547             }
39548         }
39549         return true;
39550     },
39551
39552     // private
39553     shouldDeactivate : function(e){
39554         if(Roo.menu.Item.superclass.shouldDeactivate.call(this, e)){
39555             if(this.menu && this.menu.isVisible()){
39556                 return !this.menu.getEl().getRegion().contains(e.getPoint());
39557             }
39558             return true;
39559         }
39560         return false;
39561     },
39562
39563     // private
39564     deactivate : function(){
39565         Roo.menu.Item.superclass.deactivate.apply(this, arguments);
39566         this.hideMenu();
39567     },
39568
39569     // private
39570     expandMenu : function(autoActivate){
39571         if(!this.disabled && this.menu){
39572             clearTimeout(this.hideTimer);
39573             delete this.hideTimer;
39574             if(!this.menu.isVisible() && !this.showTimer){
39575                 this.showTimer = this.deferExpand.defer(this.showDelay, this, [autoActivate]);
39576             }else if (this.menu.isVisible() && autoActivate){
39577                 this.menu.tryActivate(0, 1);
39578             }
39579         }
39580     },
39581
39582     // private
39583     deferExpand : function(autoActivate){
39584         delete this.showTimer;
39585         this.menu.show(this.container, this.parentMenu.subMenuAlign || "tl-tr?", this.parentMenu);
39586         if(autoActivate){
39587             this.menu.tryActivate(0, 1);
39588         }
39589     },
39590
39591     // private
39592     hideMenu : function(){
39593         clearTimeout(this.showTimer);
39594         delete this.showTimer;
39595         if(!this.hideTimer && this.menu && this.menu.isVisible()){
39596             this.hideTimer = this.deferHide.defer(this.hideDelay, this);
39597         }
39598     },
39599
39600     // private
39601     deferHide : function(){
39602         delete this.hideTimer;
39603         this.menu.hide();
39604     }
39605 });/*
39606  * Based on:
39607  * Ext JS Library 1.1.1
39608  * Copyright(c) 2006-2007, Ext JS, LLC.
39609  *
39610  * Originally Released Under LGPL - original licence link has changed is not relivant.
39611  *
39612  * Fork - LGPL
39613  * <script type="text/javascript">
39614  */
39615  
39616 /**
39617  * @class Roo.menu.CheckItem
39618  * @extends Roo.menu.Item
39619  * Adds a menu item that contains a checkbox by default, but can also be part of a radio group.
39620  * @constructor
39621  * Creates a new CheckItem
39622  * @param {Object} config Configuration options
39623  */
39624 Roo.menu.CheckItem = function(config){
39625     Roo.menu.CheckItem.superclass.constructor.call(this, config);
39626     this.addEvents({
39627         /**
39628          * @event beforecheckchange
39629          * Fires before the checked value is set, providing an opportunity to cancel if needed
39630          * @param {Roo.menu.CheckItem} this
39631          * @param {Boolean} checked The new checked value that will be set
39632          */
39633         "beforecheckchange" : true,
39634         /**
39635          * @event checkchange
39636          * Fires after the checked value has been set
39637          * @param {Roo.menu.CheckItem} this
39638          * @param {Boolean} checked The checked value that was set
39639          */
39640         "checkchange" : true
39641     });
39642     if(this.checkHandler){
39643         this.on('checkchange', this.checkHandler, this.scope);
39644     }
39645 };
39646 Roo.extend(Roo.menu.CheckItem, Roo.menu.Item, {
39647     /**
39648      * @cfg {String} group
39649      * All check items with the same group name will automatically be grouped into a single-select
39650      * radio button group (defaults to '')
39651      */
39652     /**
39653      * @cfg {String} itemCls The default CSS class to use for check items (defaults to "x-menu-item x-menu-check-item")
39654      */
39655     itemCls : "x-menu-item x-menu-check-item",
39656     /**
39657      * @cfg {String} groupClass The default CSS class to use for radio group check items (defaults to "x-menu-group-item")
39658      */
39659     groupClass : "x-menu-group-item",
39660
39661     /**
39662      * @cfg {Boolean} checked True to initialize this checkbox as checked (defaults to false).  Note that
39663      * if this checkbox is part of a radio group (group = true) only the last item in the group that is
39664      * initialized with checked = true will be rendered as checked.
39665      */
39666     checked: false,
39667
39668     // private
39669     ctype: "Roo.menu.CheckItem",
39670
39671     // private
39672     onRender : function(c){
39673         Roo.menu.CheckItem.superclass.onRender.apply(this, arguments);
39674         if(this.group){
39675             this.el.addClass(this.groupClass);
39676         }
39677         Roo.menu.MenuMgr.registerCheckable(this);
39678         if(this.checked){
39679             this.checked = false;
39680             this.setChecked(true, true);
39681         }
39682     },
39683
39684     // private
39685     destroy : function(){
39686         if(this.rendered){
39687             Roo.menu.MenuMgr.unregisterCheckable(this);
39688         }
39689         Roo.menu.CheckItem.superclass.destroy.apply(this, arguments);
39690     },
39691
39692     /**
39693      * Set the checked state of this item
39694      * @param {Boolean} checked The new checked value
39695      * @param {Boolean} suppressEvent (optional) True to prevent the checkchange event from firing (defaults to false)
39696      */
39697     setChecked : function(state, suppressEvent){
39698         if(this.checked != state && this.fireEvent("beforecheckchange", this, state) !== false){
39699             if(this.container){
39700                 this.container[state ? "addClass" : "removeClass"]("x-menu-item-checked");
39701             }
39702             this.checked = state;
39703             if(suppressEvent !== true){
39704                 this.fireEvent("checkchange", this, state);
39705             }
39706         }
39707     },
39708
39709     // private
39710     handleClick : function(e){
39711        if(!this.disabled && !(this.checked && this.group)){// disable unselect on radio item
39712            this.setChecked(!this.checked);
39713        }
39714        Roo.menu.CheckItem.superclass.handleClick.apply(this, arguments);
39715     }
39716 });/*
39717  * Based on:
39718  * Ext JS Library 1.1.1
39719  * Copyright(c) 2006-2007, Ext JS, LLC.
39720  *
39721  * Originally Released Under LGPL - original licence link has changed is not relivant.
39722  *
39723  * Fork - LGPL
39724  * <script type="text/javascript">
39725  */
39726  
39727 /**
39728  * @class Roo.menu.DateItem
39729  * @extends Roo.menu.Adapter
39730  * A menu item that wraps the {@link Roo.DatPicker} component.
39731  * @constructor
39732  * Creates a new DateItem
39733  * @param {Object} config Configuration options
39734  */
39735 Roo.menu.DateItem = function(config){
39736     Roo.menu.DateItem.superclass.constructor.call(this, new Roo.DatePicker(config), config);
39737     /** The Roo.DatePicker object @type Roo.DatePicker */
39738     this.picker = this.component;
39739     this.addEvents({select: true});
39740     
39741     this.picker.on("render", function(picker){
39742         picker.getEl().swallowEvent("click");
39743         picker.container.addClass("x-menu-date-item");
39744     });
39745
39746     this.picker.on("select", this.onSelect, this);
39747 };
39748
39749 Roo.extend(Roo.menu.DateItem, Roo.menu.Adapter, {
39750     // private
39751     onSelect : function(picker, date){
39752         this.fireEvent("select", this, date, picker);
39753         Roo.menu.DateItem.superclass.handleClick.call(this);
39754     }
39755 });/*
39756  * Based on:
39757  * Ext JS Library 1.1.1
39758  * Copyright(c) 2006-2007, Ext JS, LLC.
39759  *
39760  * Originally Released Under LGPL - original licence link has changed is not relivant.
39761  *
39762  * Fork - LGPL
39763  * <script type="text/javascript">
39764  */
39765  
39766 /**
39767  * @class Roo.menu.ColorItem
39768  * @extends Roo.menu.Adapter
39769  * A menu item that wraps the {@link Roo.ColorPalette} component.
39770  * @constructor
39771  * Creates a new ColorItem
39772  * @param {Object} config Configuration options
39773  */
39774 Roo.menu.ColorItem = function(config){
39775     Roo.menu.ColorItem.superclass.constructor.call(this, new Roo.ColorPalette(config), config);
39776     /** The Roo.ColorPalette object @type Roo.ColorPalette */
39777     this.palette = this.component;
39778     this.relayEvents(this.palette, ["select"]);
39779     if(this.selectHandler){
39780         this.on('select', this.selectHandler, this.scope);
39781     }
39782 };
39783 Roo.extend(Roo.menu.ColorItem, Roo.menu.Adapter);/*
39784  * Based on:
39785  * Ext JS Library 1.1.1
39786  * Copyright(c) 2006-2007, Ext JS, LLC.
39787  *
39788  * Originally Released Under LGPL - original licence link has changed is not relivant.
39789  *
39790  * Fork - LGPL
39791  * <script type="text/javascript">
39792  */
39793  
39794
39795 /**
39796  * @class Roo.menu.DateMenu
39797  * @extends Roo.menu.Menu
39798  * A menu containing a {@link Roo.menu.DateItem} component (which provides a date picker).
39799  * @constructor
39800  * Creates a new DateMenu
39801  * @param {Object} config Configuration options
39802  */
39803 Roo.menu.DateMenu = function(config){
39804     Roo.menu.DateMenu.superclass.constructor.call(this, config);
39805     this.plain = true;
39806     var di = new Roo.menu.DateItem(config);
39807     this.add(di);
39808     /**
39809      * The {@link Roo.DatePicker} instance for this DateMenu
39810      * @type DatePicker
39811      */
39812     this.picker = di.picker;
39813     /**
39814      * @event select
39815      * @param {DatePicker} picker
39816      * @param {Date} date
39817      */
39818     this.relayEvents(di, ["select"]);
39819     this.on('beforeshow', function(){
39820         if(this.picker){
39821             this.picker.hideMonthPicker(false);
39822         }
39823     }, this);
39824 };
39825 Roo.extend(Roo.menu.DateMenu, Roo.menu.Menu, {
39826     cls:'x-date-menu'
39827 });/*
39828  * Based on:
39829  * Ext JS Library 1.1.1
39830  * Copyright(c) 2006-2007, Ext JS, LLC.
39831  *
39832  * Originally Released Under LGPL - original licence link has changed is not relivant.
39833  *
39834  * Fork - LGPL
39835  * <script type="text/javascript">
39836  */
39837  
39838
39839 /**
39840  * @class Roo.menu.ColorMenu
39841  * @extends Roo.menu.Menu
39842  * A menu containing a {@link Roo.menu.ColorItem} component (which provides a basic color picker).
39843  * @constructor
39844  * Creates a new ColorMenu
39845  * @param {Object} config Configuration options
39846  */
39847 Roo.menu.ColorMenu = function(config){
39848     Roo.menu.ColorMenu.superclass.constructor.call(this, config);
39849     this.plain = true;
39850     var ci = new Roo.menu.ColorItem(config);
39851     this.add(ci);
39852     /**
39853      * The {@link Roo.ColorPalette} instance for this ColorMenu
39854      * @type ColorPalette
39855      */
39856     this.palette = ci.palette;
39857     /**
39858      * @event select
39859      * @param {ColorPalette} palette
39860      * @param {String} color
39861      */
39862     this.relayEvents(ci, ["select"]);
39863 };
39864 Roo.extend(Roo.menu.ColorMenu, Roo.menu.Menu);/*
39865  * Based on:
39866  * Ext JS Library 1.1.1
39867  * Copyright(c) 2006-2007, Ext JS, LLC.
39868  *
39869  * Originally Released Under LGPL - original licence link has changed is not relivant.
39870  *
39871  * Fork - LGPL
39872  * <script type="text/javascript">
39873  */
39874  
39875 /**
39876  * @class Roo.form.TextItem
39877  * @extends Roo.BoxComponent
39878  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39879  * @constructor
39880  * Creates a new TextItem
39881  * @param {Object} config Configuration options
39882  */
39883 Roo.form.TextItem = function(config){
39884     Roo.form.TextItem.superclass.constructor.call(this, config);
39885 };
39886
39887 Roo.extend(Roo.form.TextItem, Roo.BoxComponent,  {
39888     
39889     /**
39890      * @cfg {String} tag the tag for this item (default div)
39891      */
39892     tag : 'div',
39893     /**
39894      * @cfg {String} html the content for this item
39895      */
39896     html : '',
39897     
39898     getAutoCreate : function()
39899     {
39900         var cfg = {
39901             id: this.id,
39902             tag: this.tag,
39903             html: this.html,
39904             cls: 'x-form-item'
39905         };
39906         
39907         return cfg;
39908         
39909     },
39910     
39911     onRender : function(ct, position)
39912     {
39913         Roo.form.TextItem.superclass.onRender.call(this, ct, position);
39914         
39915         if(!this.el){
39916             var cfg = this.getAutoCreate();
39917             if(!cfg.name){
39918                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
39919             }
39920             if (!cfg.name.length) {
39921                 delete cfg.name;
39922             }
39923             this.el = ct.createChild(cfg, position);
39924         }
39925     },
39926     /*
39927      * setHTML
39928      * @param {String} html update the Contents of the element.
39929      */
39930     setHTML : function(html)
39931     {
39932         this.fieldEl.dom.innerHTML = html;
39933     }
39934     
39935 });/*
39936  * Based on:
39937  * Ext JS Library 1.1.1
39938  * Copyright(c) 2006-2007, Ext JS, LLC.
39939  *
39940  * Originally Released Under LGPL - original licence link has changed is not relivant.
39941  *
39942  * Fork - LGPL
39943  * <script type="text/javascript">
39944  */
39945  
39946 /**
39947  * @class Roo.form.Field
39948  * @extends Roo.BoxComponent
39949  * Base class for form fields that provides default event handling, sizing, value handling and other functionality.
39950  * @constructor
39951  * Creates a new Field
39952  * @param {Object} config Configuration options
39953  */
39954 Roo.form.Field = function(config){
39955     Roo.form.Field.superclass.constructor.call(this, config);
39956 };
39957
39958 Roo.extend(Roo.form.Field, Roo.BoxComponent,  {
39959     /**
39960      * @cfg {String} fieldLabel Label to use when rendering a form.
39961      */
39962        /**
39963      * @cfg {String} qtip Mouse over tip
39964      */
39965      
39966     /**
39967      * @cfg {String} invalidClass The CSS class to use when marking a field invalid (defaults to "x-form-invalid")
39968      */
39969     invalidClass : "x-form-invalid",
39970     /**
39971      * @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")
39972      */
39973     invalidText : "The value in this field is invalid",
39974     /**
39975      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
39976      */
39977     focusClass : "x-form-focus",
39978     /**
39979      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
39980       automatic validation (defaults to "keyup").
39981      */
39982     validationEvent : "keyup",
39983     /**
39984      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
39985      */
39986     validateOnBlur : true,
39987     /**
39988      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
39989      */
39990     validationDelay : 250,
39991     /**
39992      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
39993      * {tag: "input", type: "text", size: "20", autocomplete: "off"})
39994      */
39995     defaultAutoCreate : {tag: "input", type: "text", size: "20", autocomplete: "new-password"},
39996     /**
39997      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field")
39998      */
39999     fieldClass : "x-form-field",
40000     /**
40001      * @cfg {String} msgTarget The location where error text should display.  Should be one of the following values (defaults to 'qtip'):
40002      *<pre>
40003 Value         Description
40004 -----------   ----------------------------------------------------------------------
40005 qtip          Display a quick tip when the user hovers over the field
40006 title         Display a default browser title attribute popup
40007 under         Add a block div beneath the field containing the error text
40008 side          Add an error icon to the right of the field with a popup on hover
40009 [element id]  Add the error text directly to the innerHTML of the specified element
40010 </pre>
40011      */
40012     msgTarget : 'qtip',
40013     /**
40014      * @cfg {String} msgFx <b>Experimental</b> The effect used when displaying a validation message under the field (defaults to 'normal').
40015      */
40016     msgFx : 'normal',
40017
40018     /**
40019      * @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.
40020      */
40021     readOnly : false,
40022
40023     /**
40024      * @cfg {Boolean} disabled True to disable the field (defaults to false).
40025      */
40026     disabled : false,
40027
40028     /**
40029      * @cfg {String} inputType The type attribute for input fields -- e.g. radio, text, password (defaults to "text").
40030      */
40031     inputType : undefined,
40032     
40033     /**
40034      * @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).
40035          */
40036         tabIndex : undefined,
40037         
40038     // private
40039     isFormField : true,
40040
40041     // private
40042     hasFocus : false,
40043     /**
40044      * @property {Roo.Element} fieldEl
40045      * Element Containing the rendered Field (with label etc.)
40046      */
40047     /**
40048      * @cfg {Mixed} value A value to initialize this field with.
40049      */
40050     value : undefined,
40051
40052     /**
40053      * @cfg {String} name The field's HTML name attribute.
40054      */
40055     /**
40056      * @cfg {String} cls A CSS class to apply to the field's underlying element.
40057      */
40058     // private
40059     loadedValue : false,
40060      
40061      
40062         // private ??
40063         initComponent : function(){
40064         Roo.form.Field.superclass.initComponent.call(this);
40065         this.addEvents({
40066             /**
40067              * @event focus
40068              * Fires when this field receives input focus.
40069              * @param {Roo.form.Field} this
40070              */
40071             focus : true,
40072             /**
40073              * @event blur
40074              * Fires when this field loses input focus.
40075              * @param {Roo.form.Field} this
40076              */
40077             blur : true,
40078             /**
40079              * @event specialkey
40080              * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
40081              * {@link Roo.EventObject#getKey} to determine which key was pressed.
40082              * @param {Roo.form.Field} this
40083              * @param {Roo.EventObject} e The event object
40084              */
40085             specialkey : true,
40086             /**
40087              * @event change
40088              * Fires just before the field blurs if the field value has changed.
40089              * @param {Roo.form.Field} this
40090              * @param {Mixed} newValue The new value
40091              * @param {Mixed} oldValue The original value
40092              */
40093             change : true,
40094             /**
40095              * @event invalid
40096              * Fires after the field has been marked as invalid.
40097              * @param {Roo.form.Field} this
40098              * @param {String} msg The validation message
40099              */
40100             invalid : true,
40101             /**
40102              * @event valid
40103              * Fires after the field has been validated with no errors.
40104              * @param {Roo.form.Field} this
40105              */
40106             valid : true,
40107              /**
40108              * @event keyup
40109              * Fires after the key up
40110              * @param {Roo.form.Field} this
40111              * @param {Roo.EventObject}  e The event Object
40112              */
40113             keyup : true
40114         });
40115     },
40116
40117     /**
40118      * Returns the name attribute of the field if available
40119      * @return {String} name The field name
40120      */
40121     getName: function(){
40122          return this.rendered && this.el.dom.name ? this.el.dom.name : (this.hiddenName || '');
40123     },
40124
40125     // private
40126     onRender : function(ct, position){
40127         Roo.form.Field.superclass.onRender.call(this, ct, position);
40128         if(!this.el){
40129             var cfg = this.getAutoCreate();
40130             if(!cfg.name){
40131                 cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
40132             }
40133             if (!cfg.name.length) {
40134                 delete cfg.name;
40135             }
40136             if(this.inputType){
40137                 cfg.type = this.inputType;
40138             }
40139             this.el = ct.createChild(cfg, position);
40140         }
40141         var type = this.el.dom.type;
40142         if(type){
40143             if(type == 'password'){
40144                 type = 'text';
40145             }
40146             this.el.addClass('x-form-'+type);
40147         }
40148         if(this.readOnly){
40149             this.el.dom.readOnly = true;
40150         }
40151         if(this.tabIndex !== undefined){
40152             this.el.dom.setAttribute('tabIndex', this.tabIndex);
40153         }
40154
40155         this.el.addClass([this.fieldClass, this.cls]);
40156         this.initValue();
40157     },
40158
40159     /**
40160      * Apply the behaviors of this component to an existing element. <b>This is used instead of render().</b>
40161      * @param {String/HTMLElement/Element} el The id of the node, a DOM node or an existing Element
40162      * @return {Roo.form.Field} this
40163      */
40164     applyTo : function(target){
40165         this.allowDomMove = false;
40166         this.el = Roo.get(target);
40167         this.render(this.el.dom.parentNode);
40168         return this;
40169     },
40170
40171     // private
40172     initValue : function(){
40173         if(this.value !== undefined){
40174             this.setValue(this.value);
40175         }else if(this.el.dom.value.length > 0){
40176             this.setValue(this.el.dom.value);
40177         }
40178     },
40179
40180     /**
40181      * Returns true if this field has been changed since it was originally loaded and is not disabled.
40182      * DEPRICATED  - it never worked well - use hasChanged/resetHasChanged.
40183      */
40184     isDirty : function() {
40185         if(this.disabled) {
40186             return false;
40187         }
40188         return String(this.getValue()) !== String(this.originalValue);
40189     },
40190
40191     /**
40192      * stores the current value in loadedValue
40193      */
40194     resetHasChanged : function()
40195     {
40196         this.loadedValue = String(this.getValue());
40197     },
40198     /**
40199      * checks the current value against the 'loaded' value.
40200      * Note - will return false if 'resetHasChanged' has not been called first.
40201      */
40202     hasChanged : function()
40203     {
40204         if(this.disabled || this.readOnly) {
40205             return false;
40206         }
40207         return this.loadedValue !== false && String(this.getValue()) !== this.loadedValue;
40208     },
40209     
40210     
40211     
40212     // private
40213     afterRender : function(){
40214         Roo.form.Field.superclass.afterRender.call(this);
40215         this.initEvents();
40216     },
40217
40218     // private
40219     fireKey : function(e){
40220         //Roo.log('field ' + e.getKey());
40221         if(e.isNavKeyPress()){
40222             this.fireEvent("specialkey", this, e);
40223         }
40224     },
40225
40226     /**
40227      * Resets the current field value to the originally loaded value and clears any validation messages
40228      */
40229     reset : function(){
40230         this.setValue(this.resetValue);
40231         this.originalValue = this.getValue();
40232         this.clearInvalid();
40233     },
40234
40235     // private
40236     initEvents : function(){
40237         // safari killled keypress - so keydown is now used..
40238         this.el.on("keydown" , this.fireKey,  this);
40239         this.el.on("focus", this.onFocus,  this);
40240         this.el.on("blur", this.onBlur,  this);
40241         this.el.relayEvent('keyup', this);
40242
40243         // reference to original value for reset
40244         this.originalValue = this.getValue();
40245         this.resetValue =  this.getValue();
40246     },
40247
40248     // private
40249     onFocus : function(){
40250         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40251             this.el.addClass(this.focusClass);
40252         }
40253         if(!this.hasFocus){
40254             this.hasFocus = true;
40255             this.startValue = this.getValue();
40256             this.fireEvent("focus", this);
40257         }
40258     },
40259
40260     beforeBlur : Roo.emptyFn,
40261
40262     // private
40263     onBlur : function(){
40264         this.beforeBlur();
40265         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
40266             this.el.removeClass(this.focusClass);
40267         }
40268         this.hasFocus = false;
40269         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
40270             this.validate();
40271         }
40272         var v = this.getValue();
40273         if(String(v) !== String(this.startValue)){
40274             this.fireEvent('change', this, v, this.startValue);
40275         }
40276         this.fireEvent("blur", this);
40277     },
40278
40279     /**
40280      * Returns whether or not the field value is currently valid
40281      * @param {Boolean} preventMark True to disable marking the field invalid
40282      * @return {Boolean} True if the value is valid, else false
40283      */
40284     isValid : function(preventMark){
40285         if(this.disabled){
40286             return true;
40287         }
40288         var restore = this.preventMark;
40289         this.preventMark = preventMark === true;
40290         var v = this.validateValue(this.processValue(this.getRawValue()));
40291         this.preventMark = restore;
40292         return v;
40293     },
40294
40295     /**
40296      * Validates the field value
40297      * @return {Boolean} True if the value is valid, else false
40298      */
40299     validate : function(){
40300         if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
40301             this.clearInvalid();
40302             return true;
40303         }
40304         return false;
40305     },
40306
40307     processValue : function(value){
40308         return value;
40309     },
40310
40311     // private
40312     // Subclasses should provide the validation implementation by overriding this
40313     validateValue : function(value){
40314         return true;
40315     },
40316
40317     /**
40318      * Mark this field as invalid
40319      * @param {String} msg The validation message
40320      */
40321     markInvalid : function(msg){
40322         if(!this.rendered || this.preventMark){ // not rendered
40323             return;
40324         }
40325         
40326         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40327         
40328         obj.el.addClass(this.invalidClass);
40329         msg = msg || this.invalidText;
40330         switch(this.msgTarget){
40331             case 'qtip':
40332                 obj.el.dom.qtip = msg;
40333                 obj.el.dom.qclass = 'x-form-invalid-tip';
40334                 if(Roo.QuickTips){ // fix for floating editors interacting with DND
40335                     Roo.QuickTips.enable();
40336                 }
40337                 break;
40338             case 'title':
40339                 this.el.dom.title = msg;
40340                 break;
40341             case 'under':
40342                 if(!this.errorEl){
40343                     var elp = this.el.findParent('.x-form-element', 5, true);
40344                     this.errorEl = elp.createChild({cls:'x-form-invalid-msg'});
40345                     this.errorEl.setWidth(elp.getWidth(true)-20);
40346                 }
40347                 this.errorEl.update(msg);
40348                 Roo.form.Field.msgFx[this.msgFx].show(this.errorEl, this);
40349                 break;
40350             case 'side':
40351                 if(!this.errorIcon){
40352                     var elp = this.el.findParent('.x-form-element', 5, true);
40353                     this.errorIcon = elp.createChild({cls:'x-form-invalid-icon'});
40354                 }
40355                 this.alignErrorIcon();
40356                 this.errorIcon.dom.qtip = msg;
40357                 this.errorIcon.dom.qclass = 'x-form-invalid-tip';
40358                 this.errorIcon.show();
40359                 this.on('resize', this.alignErrorIcon, this);
40360                 break;
40361             default:
40362                 var t = Roo.getDom(this.msgTarget);
40363                 t.innerHTML = msg;
40364                 t.style.display = this.msgDisplay;
40365                 break;
40366         }
40367         this.fireEvent('invalid', this, msg);
40368     },
40369
40370     // private
40371     alignErrorIcon : function(){
40372         this.errorIcon.alignTo(this.el, 'tl-tr', [2, 0]);
40373     },
40374
40375     /**
40376      * Clear any invalid styles/messages for this field
40377      */
40378     clearInvalid : function(){
40379         if(!this.rendered || this.preventMark){ // not rendered
40380             return;
40381         }
40382         var obj = (typeof(this.combo) != 'undefined') ? this.combo : this; // fix the combox array!!
40383         
40384         obj.el.removeClass(this.invalidClass);
40385         switch(this.msgTarget){
40386             case 'qtip':
40387                 obj.el.dom.qtip = '';
40388                 break;
40389             case 'title':
40390                 this.el.dom.title = '';
40391                 break;
40392             case 'under':
40393                 if(this.errorEl){
40394                     Roo.form.Field.msgFx[this.msgFx].hide(this.errorEl, this);
40395                 }
40396                 break;
40397             case 'side':
40398                 if(this.errorIcon){
40399                     this.errorIcon.dom.qtip = '';
40400                     this.errorIcon.hide();
40401                     this.un('resize', this.alignErrorIcon, this);
40402                 }
40403                 break;
40404             default:
40405                 var t = Roo.getDom(this.msgTarget);
40406                 t.innerHTML = '';
40407                 t.style.display = 'none';
40408                 break;
40409         }
40410         this.fireEvent('valid', this);
40411     },
40412
40413     /**
40414      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
40415      * @return {Mixed} value The field value
40416      */
40417     getRawValue : function(){
40418         var v = this.el.getValue();
40419         
40420         return v;
40421     },
40422
40423     /**
40424      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
40425      * @return {Mixed} value The field value
40426      */
40427     getValue : function(){
40428         var v = this.el.getValue();
40429          
40430         return v;
40431     },
40432
40433     /**
40434      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
40435      * @param {Mixed} value The value to set
40436      */
40437     setRawValue : function(v){
40438         return this.el.dom.value = (v === null || v === undefined ? '' : v);
40439     },
40440
40441     /**
40442      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
40443      * @param {Mixed} value The value to set
40444      */
40445     setValue : function(v){
40446         this.value = v;
40447         if(this.rendered){
40448             this.el.dom.value = (v === null || v === undefined ? '' : v);
40449              this.validate();
40450         }
40451     },
40452
40453     adjustSize : function(w, h){
40454         var s = Roo.form.Field.superclass.adjustSize.call(this, w, h);
40455         s.width = this.adjustWidth(this.el.dom.tagName, s.width);
40456         return s;
40457     },
40458
40459     adjustWidth : function(tag, w){
40460         tag = tag.toLowerCase();
40461         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
40462             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
40463                 if(tag == 'input'){
40464                     return w + 2;
40465                 }
40466                 if(tag == 'textarea'){
40467                     return w-2;
40468                 }
40469             }else if(Roo.isOpera){
40470                 if(tag == 'input'){
40471                     return w + 2;
40472                 }
40473                 if(tag == 'textarea'){
40474                     return w-2;
40475                 }
40476             }
40477         }
40478         return w;
40479     }
40480 });
40481
40482
40483 // anything other than normal should be considered experimental
40484 Roo.form.Field.msgFx = {
40485     normal : {
40486         show: function(msgEl, f){
40487             msgEl.setDisplayed('block');
40488         },
40489
40490         hide : function(msgEl, f){
40491             msgEl.setDisplayed(false).update('');
40492         }
40493     },
40494
40495     slide : {
40496         show: function(msgEl, f){
40497             msgEl.slideIn('t', {stopFx:true});
40498         },
40499
40500         hide : function(msgEl, f){
40501             msgEl.slideOut('t', {stopFx:true,useDisplay:true});
40502         }
40503     },
40504
40505     slideRight : {
40506         show: function(msgEl, f){
40507             msgEl.fixDisplay();
40508             msgEl.alignTo(f.el, 'tl-tr');
40509             msgEl.slideIn('l', {stopFx:true});
40510         },
40511
40512         hide : function(msgEl, f){
40513             msgEl.slideOut('l', {stopFx:true,useDisplay:true});
40514         }
40515     }
40516 };/*
40517  * Based on:
40518  * Ext JS Library 1.1.1
40519  * Copyright(c) 2006-2007, Ext JS, LLC.
40520  *
40521  * Originally Released Under LGPL - original licence link has changed is not relivant.
40522  *
40523  * Fork - LGPL
40524  * <script type="text/javascript">
40525  */
40526  
40527
40528 /**
40529  * @class Roo.form.TextField
40530  * @extends Roo.form.Field
40531  * Basic text field.  Can be used as a direct replacement for traditional text inputs, or as the base
40532  * class for more sophisticated input controls (like {@link Roo.form.TextArea} and {@link Roo.form.ComboBox}).
40533  * @constructor
40534  * Creates a new TextField
40535  * @param {Object} config Configuration options
40536  */
40537 Roo.form.TextField = function(config){
40538     Roo.form.TextField.superclass.constructor.call(this, config);
40539     this.addEvents({
40540         /**
40541          * @event autosize
40542          * Fires when the autosize function is triggered.  The field may or may not have actually changed size
40543          * according to the default logic, but this event provides a hook for the developer to apply additional
40544          * logic at runtime to resize the field if needed.
40545              * @param {Roo.form.Field} this This text field
40546              * @param {Number} width The new field width
40547              */
40548         autosize : true
40549     });
40550 };
40551
40552 Roo.extend(Roo.form.TextField, Roo.form.Field,  {
40553     /**
40554      * @cfg {Boolean} grow True if this field should automatically grow and shrink to its content
40555      */
40556     grow : false,
40557     /**
40558      * @cfg {Number} growMin The minimum width to allow when grow = true (defaults to 30)
40559      */
40560     growMin : 30,
40561     /**
40562      * @cfg {Number} growMax The maximum width to allow when grow = true (defaults to 800)
40563      */
40564     growMax : 800,
40565     /**
40566      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
40567      */
40568     vtype : null,
40569     /**
40570      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
40571      */
40572     maskRe : null,
40573     /**
40574      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
40575      */
40576     disableKeyFilter : false,
40577     /**
40578      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
40579      */
40580     allowBlank : true,
40581     /**
40582      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
40583      */
40584     minLength : 0,
40585     /**
40586      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
40587      */
40588     maxLength : Number.MAX_VALUE,
40589     /**
40590      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
40591      */
40592     minLengthText : "The minimum length for this field is {0}",
40593     /**
40594      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
40595      */
40596     maxLengthText : "The maximum length for this field is {0}",
40597     /**
40598      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
40599      */
40600     selectOnFocus : false,
40601     /**
40602      * @cfg {Boolean} allowLeadingSpace True to prevent the stripping of leading white space 
40603      */    
40604     allowLeadingSpace : false,
40605     /**
40606      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
40607      */
40608     blankText : "This field is required",
40609     /**
40610      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
40611      * If available, this function will be called only after the basic validators all return true, and will be passed the
40612      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
40613      */
40614     validator : null,
40615     /**
40616      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
40617      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
40618      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
40619      */
40620     regex : null,
40621     /**
40622      * @cfg {String} regexText The error text to display if {@link #regex} is used and the test fails during validation (defaults to "")
40623      */
40624     regexText : "",
40625     /**
40626      * @cfg {String} emptyText The default text to display in an empty field - placeholder... (defaults to null).
40627      */
40628     emptyText : null,
40629    
40630
40631     // private
40632     initEvents : function()
40633     {
40634         if (this.emptyText) {
40635             this.el.attr('placeholder', this.emptyText);
40636         }
40637         
40638         Roo.form.TextField.superclass.initEvents.call(this);
40639         if(this.validationEvent == 'keyup'){
40640             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
40641             this.el.on('keyup', this.filterValidation, this);
40642         }
40643         else if(this.validationEvent !== false){
40644             this.el.on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
40645         }
40646         
40647         if(this.selectOnFocus){
40648             this.on("focus", this.preFocus, this);
40649         }
40650         if (!this.allowLeadingSpace) {
40651             this.on('blur', this.cleanLeadingSpace, this);
40652         }
40653         
40654         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
40655             this.el.on("keypress", this.filterKeys, this);
40656         }
40657         if(this.grow){
40658             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
40659             this.el.on("click", this.autoSize,  this);
40660         }
40661         if(this.el.is('input[type=password]') && Roo.isSafari){
40662             this.el.on('keydown', this.SafariOnKeyDown, this);
40663         }
40664     },
40665
40666     processValue : function(value){
40667         if(this.stripCharsRe){
40668             var newValue = value.replace(this.stripCharsRe, '');
40669             if(newValue !== value){
40670                 this.setRawValue(newValue);
40671                 return newValue;
40672             }
40673         }
40674         return value;
40675     },
40676
40677     filterValidation : function(e){
40678         if(!e.isNavKeyPress()){
40679             this.validationTask.delay(this.validationDelay);
40680         }
40681     },
40682
40683     // private
40684     onKeyUp : function(e){
40685         if(!e.isNavKeyPress()){
40686             this.autoSize();
40687         }
40688     },
40689     // private - clean the leading white space
40690     cleanLeadingSpace : function(e)
40691     {
40692         if ( this.inputType == 'file') {
40693             return;
40694         }
40695         
40696         this.setValue((this.getValue() + '').replace(/^\s+/,''));
40697     },
40698     /**
40699      * Resets the current field value to the originally-loaded value and clears any validation messages.
40700      *  
40701      */
40702     reset : function(){
40703         Roo.form.TextField.superclass.reset.call(this);
40704        
40705     }, 
40706     // private
40707     preFocus : function(){
40708         
40709         if(this.selectOnFocus){
40710             this.el.dom.select();
40711         }
40712     },
40713
40714     
40715     // private
40716     filterKeys : function(e){
40717         var k = e.getKey();
40718         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
40719             return;
40720         }
40721         var c = e.getCharCode(), cc = String.fromCharCode(c);
40722         if(Roo.isIE && (e.isSpecialKey() || !cc)){
40723             return;
40724         }
40725         if(!this.maskRe.test(cc)){
40726             e.stopEvent();
40727         }
40728     },
40729
40730     setValue : function(v){
40731         
40732         Roo.form.TextField.superclass.setValue.apply(this, arguments);
40733         
40734         this.autoSize();
40735     },
40736
40737     /**
40738      * Validates a value according to the field's validation rules and marks the field as invalid
40739      * if the validation fails
40740      * @param {Mixed} value The value to validate
40741      * @return {Boolean} True if the value is valid, else false
40742      */
40743     validateValue : function(value){
40744         if(value.length < 1)  { // if it's blank
40745              if(this.allowBlank){
40746                 this.clearInvalid();
40747                 return true;
40748              }else{
40749                 this.markInvalid(this.blankText);
40750                 return false;
40751              }
40752         }
40753         if(value.length < this.minLength){
40754             this.markInvalid(String.format(this.minLengthText, this.minLength));
40755             return false;
40756         }
40757         if(value.length > this.maxLength){
40758             this.markInvalid(String.format(this.maxLengthText, this.maxLength));
40759             return false;
40760         }
40761         if(this.vtype){
40762             var vt = Roo.form.VTypes;
40763             if(!vt[this.vtype](value, this)){
40764                 this.markInvalid(this.vtypeText || vt[this.vtype +'Text']);
40765                 return false;
40766             }
40767         }
40768         if(typeof this.validator == "function"){
40769             var msg = this.validator(value);
40770             if(msg !== true){
40771                 this.markInvalid(msg);
40772                 return false;
40773             }
40774         }
40775         if(this.regex && !this.regex.test(value)){
40776             this.markInvalid(this.regexText);
40777             return false;
40778         }
40779         return true;
40780     },
40781
40782     /**
40783      * Selects text in this field
40784      * @param {Number} start (optional) The index where the selection should start (defaults to 0)
40785      * @param {Number} end (optional) The index where the selection should end (defaults to the text length)
40786      */
40787     selectText : function(start, end){
40788         var v = this.getRawValue();
40789         if(v.length > 0){
40790             start = start === undefined ? 0 : start;
40791             end = end === undefined ? v.length : end;
40792             var d = this.el.dom;
40793             if(d.setSelectionRange){
40794                 d.setSelectionRange(start, end);
40795             }else if(d.createTextRange){
40796                 var range = d.createTextRange();
40797                 range.moveStart("character", start);
40798                 range.moveEnd("character", v.length-end);
40799                 range.select();
40800             }
40801         }
40802     },
40803
40804     /**
40805      * Automatically grows the field to accomodate the width of the text up to the maximum field width allowed.
40806      * This only takes effect if grow = true, and fires the autosize event.
40807      */
40808     autoSize : function(){
40809         if(!this.grow || !this.rendered){
40810             return;
40811         }
40812         if(!this.metrics){
40813             this.metrics = Roo.util.TextMetrics.createInstance(this.el);
40814         }
40815         var el = this.el;
40816         var v = el.dom.value;
40817         var d = document.createElement('div');
40818         d.appendChild(document.createTextNode(v));
40819         v = d.innerHTML;
40820         d = null;
40821         v += "&#160;";
40822         var w = Math.min(this.growMax, Math.max(this.metrics.getWidth(v) + /* add extra padding */ 10, this.growMin));
40823         this.el.setWidth(w);
40824         this.fireEvent("autosize", this, w);
40825     },
40826     
40827     // private
40828     SafariOnKeyDown : function(event)
40829     {
40830         // this is a workaround for a password hang bug on chrome/ webkit.
40831         
40832         var isSelectAll = false;
40833         
40834         if(this.el.dom.selectionEnd > 0){
40835             isSelectAll = (this.el.dom.selectionEnd - this.el.dom.selectionStart - this.getValue().length == 0) ? true : false;
40836         }
40837         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
40838             event.preventDefault();
40839             this.setValue('');
40840             return;
40841         }
40842         
40843         if(isSelectAll && event.getCharCode() > 31){ // backspace and delete key
40844             
40845             event.preventDefault();
40846             // this is very hacky as keydown always get's upper case.
40847             
40848             var cc = String.fromCharCode(event.getCharCode());
40849             
40850             
40851             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
40852             
40853         }
40854         
40855         
40856     }
40857 });/*
40858  * Based on:
40859  * Ext JS Library 1.1.1
40860  * Copyright(c) 2006-2007, Ext JS, LLC.
40861  *
40862  * Originally Released Under LGPL - original licence link has changed is not relivant.
40863  *
40864  * Fork - LGPL
40865  * <script type="text/javascript">
40866  */
40867  
40868 /**
40869  * @class Roo.form.Hidden
40870  * @extends Roo.form.TextField
40871  * Simple Hidden element used on forms 
40872  * 
40873  * usage: form.add(new Roo.form.HiddenField({ 'name' : 'test1' }));
40874  * 
40875  * @constructor
40876  * Creates a new Hidden form element.
40877  * @param {Object} config Configuration options
40878  */
40879
40880
40881
40882 // easy hidden field...
40883 Roo.form.Hidden = function(config){
40884     Roo.form.Hidden.superclass.constructor.call(this, config);
40885 };
40886   
40887 Roo.extend(Roo.form.Hidden, Roo.form.TextField, {
40888     fieldLabel:      '',
40889     inputType:      'hidden',
40890     width:          50,
40891     allowBlank:     true,
40892     labelSeparator: '',
40893     hidden:         true,
40894     itemCls :       'x-form-item-display-none'
40895
40896
40897 });
40898
40899
40900 /*
40901  * Based on:
40902  * Ext JS Library 1.1.1
40903  * Copyright(c) 2006-2007, Ext JS, LLC.
40904  *
40905  * Originally Released Under LGPL - original licence link has changed is not relivant.
40906  *
40907  * Fork - LGPL
40908  * <script type="text/javascript">
40909  */
40910  
40911 /**
40912  * @class Roo.form.TriggerField
40913  * @extends Roo.form.TextField
40914  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
40915  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
40916  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
40917  * for which you can provide a custom implementation.  For example:
40918  * <pre><code>
40919 var trigger = new Roo.form.TriggerField();
40920 trigger.onTriggerClick = myTriggerFn;
40921 trigger.applyTo('my-field');
40922 </code></pre>
40923  *
40924  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
40925  * {@link Roo.form.DateField} and {@link Roo.form.ComboBox} are perfect examples of this.
40926  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
40927  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
40928  * @constructor
40929  * Create a new TriggerField.
40930  * @param {Object} config Configuration options (valid {@Roo.form.TextField} config options will also be applied
40931  * to the base TextField)
40932  */
40933 Roo.form.TriggerField = function(config){
40934     this.mimicing = false;
40935     Roo.form.TriggerField.superclass.constructor.call(this, config);
40936 };
40937
40938 Roo.extend(Roo.form.TriggerField, Roo.form.TextField,  {
40939     /**
40940      * @cfg {String} triggerClass A CSS class to apply to the trigger
40941      */
40942     /**
40943      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
40944      * {tag: "input", type: "text", size: "16", autocomplete: "off"})
40945      */
40946     defaultAutoCreate : {tag: "input", type: "text", size: "16", autocomplete: "new-password"},
40947     /**
40948      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
40949      */
40950     hideTrigger:false,
40951
40952     /** @cfg {Boolean} grow @hide */
40953     /** @cfg {Number} growMin @hide */
40954     /** @cfg {Number} growMax @hide */
40955
40956     /**
40957      * @hide 
40958      * @method
40959      */
40960     autoSize: Roo.emptyFn,
40961     // private
40962     monitorTab : true,
40963     // private
40964     deferHeight : true,
40965
40966     
40967     actionMode : 'wrap',
40968     // private
40969     onResize : function(w, h){
40970         Roo.form.TriggerField.superclass.onResize.apply(this, arguments);
40971         if(typeof w == 'number'){
40972             var x = w - this.trigger.getWidth();
40973             this.el.setWidth(this.adjustWidth('input', x));
40974             this.trigger.setStyle('left', x+'px');
40975         }
40976     },
40977
40978     // private
40979     adjustSize : Roo.BoxComponent.prototype.adjustSize,
40980
40981     // private
40982     getResizeEl : function(){
40983         return this.wrap;
40984     },
40985
40986     // private
40987     getPositionEl : function(){
40988         return this.wrap;
40989     },
40990
40991     // private
40992     alignErrorIcon : function(){
40993         this.errorIcon.alignTo(this.wrap, 'tl-tr', [2, 0]);
40994     },
40995
40996     // private
40997     onRender : function(ct, position){
40998         Roo.form.TriggerField.superclass.onRender.call(this, ct, position);
40999         this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
41000         this.trigger = this.wrap.createChild(this.triggerConfig ||
41001                 {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.triggerClass});
41002         if(this.hideTrigger){
41003             this.trigger.setDisplayed(false);
41004         }
41005         this.initTrigger();
41006         if(!this.width){
41007             this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
41008         }
41009     },
41010
41011     // private
41012     initTrigger : function(){
41013         this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
41014         this.trigger.addClassOnOver('x-form-trigger-over');
41015         this.trigger.addClassOnClick('x-form-trigger-click');
41016     },
41017
41018     // private
41019     onDestroy : function(){
41020         if(this.trigger){
41021             this.trigger.removeAllListeners();
41022             this.trigger.remove();
41023         }
41024         if(this.wrap){
41025             this.wrap.remove();
41026         }
41027         Roo.form.TriggerField.superclass.onDestroy.call(this);
41028     },
41029
41030     // private
41031     onFocus : function(){
41032         Roo.form.TriggerField.superclass.onFocus.call(this);
41033         if(!this.mimicing){
41034             this.wrap.addClass('x-trigger-wrap-focus');
41035             this.mimicing = true;
41036             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
41037             if(this.monitorTab){
41038                 this.el.on("keydown", this.checkTab, this);
41039             }
41040         }
41041     },
41042
41043     // private
41044     checkTab : function(e){
41045         if(e.getKey() == e.TAB){
41046             this.triggerBlur();
41047         }
41048     },
41049
41050     // private
41051     onBlur : function(){
41052         // do nothing
41053     },
41054
41055     // private
41056     mimicBlur : function(e, t){
41057         if(!this.wrap.contains(t) && this.validateBlur()){
41058             this.triggerBlur();
41059         }
41060     },
41061
41062     // private
41063     triggerBlur : function(){
41064         this.mimicing = false;
41065         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
41066         if(this.monitorTab){
41067             this.el.un("keydown", this.checkTab, this);
41068         }
41069         this.wrap.removeClass('x-trigger-wrap-focus');
41070         Roo.form.TriggerField.superclass.onBlur.call(this);
41071     },
41072
41073     // private
41074     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
41075     validateBlur : function(e, t){
41076         return true;
41077     },
41078
41079     // private
41080     onDisable : function(){
41081         Roo.form.TriggerField.superclass.onDisable.call(this);
41082         if(this.wrap){
41083             this.wrap.addClass('x-item-disabled');
41084         }
41085     },
41086
41087     // private
41088     onEnable : function(){
41089         Roo.form.TriggerField.superclass.onEnable.call(this);
41090         if(this.wrap){
41091             this.wrap.removeClass('x-item-disabled');
41092         }
41093     },
41094
41095     // private
41096     onShow : function(){
41097         var ae = this.getActionEl();
41098         
41099         if(ae){
41100             ae.dom.style.display = '';
41101             ae.dom.style.visibility = 'visible';
41102         }
41103     },
41104
41105     // private
41106     
41107     onHide : function(){
41108         var ae = this.getActionEl();
41109         ae.dom.style.display = 'none';
41110     },
41111
41112     /**
41113      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
41114      * by an implementing function.
41115      * @method
41116      * @param {EventObject} e
41117      */
41118     onTriggerClick : Roo.emptyFn
41119 });
41120
41121 // TwinTriggerField is not a public class to be used directly.  It is meant as an abstract base class
41122 // to be extended by an implementing class.  For an example of implementing this class, see the custom
41123 // SearchField implementation here: http://extjs.com/deploy/ext/examples/form/custom.html
41124 Roo.form.TwinTriggerField = Roo.extend(Roo.form.TriggerField, {
41125     initComponent : function(){
41126         Roo.form.TwinTriggerField.superclass.initComponent.call(this);
41127
41128         this.triggerConfig = {
41129             tag:'span', cls:'x-form-twin-triggers', cn:[
41130             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger1Class},
41131             {tag: "img", src: Roo.BLANK_IMAGE_URL, cls: "x-form-trigger " + this.trigger2Class}
41132         ]};
41133     },
41134
41135     getTrigger : function(index){
41136         return this.triggers[index];
41137     },
41138
41139     initTrigger : function(){
41140         var ts = this.trigger.select('.x-form-trigger', true);
41141         this.wrap.setStyle('overflow', 'hidden');
41142         var triggerField = this;
41143         ts.each(function(t, all, index){
41144             t.hide = function(){
41145                 var w = triggerField.wrap.getWidth();
41146                 this.dom.style.display = 'none';
41147                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41148             };
41149             t.show = function(){
41150                 var w = triggerField.wrap.getWidth();
41151                 this.dom.style.display = '';
41152                 triggerField.el.setWidth(w-triggerField.trigger.getWidth());
41153             };
41154             var triggerIndex = 'Trigger'+(index+1);
41155
41156             if(this['hide'+triggerIndex]){
41157                 t.dom.style.display = 'none';
41158             }
41159             t.on("click", this['on'+triggerIndex+'Click'], this, {preventDefault:true});
41160             t.addClassOnOver('x-form-trigger-over');
41161             t.addClassOnClick('x-form-trigger-click');
41162         }, this);
41163         this.triggers = ts.elements;
41164     },
41165
41166     onTrigger1Click : Roo.emptyFn,
41167     onTrigger2Click : Roo.emptyFn
41168 });/*
41169  * Based on:
41170  * Ext JS Library 1.1.1
41171  * Copyright(c) 2006-2007, Ext JS, LLC.
41172  *
41173  * Originally Released Under LGPL - original licence link has changed is not relivant.
41174  *
41175  * Fork - LGPL
41176  * <script type="text/javascript">
41177  */
41178  
41179 /**
41180  * @class Roo.form.TextArea
41181  * @extends Roo.form.TextField
41182  * Multiline text field.  Can be used as a direct replacement for traditional textarea fields, plus adds
41183  * support for auto-sizing.
41184  * @constructor
41185  * Creates a new TextArea
41186  * @param {Object} config Configuration options
41187  */
41188 Roo.form.TextArea = function(config){
41189     Roo.form.TextArea.superclass.constructor.call(this, config);
41190     // these are provided exchanges for backwards compat
41191     // minHeight/maxHeight were replaced by growMin/growMax to be
41192     // compatible with TextField growing config values
41193     if(this.minHeight !== undefined){
41194         this.growMin = this.minHeight;
41195     }
41196     if(this.maxHeight !== undefined){
41197         this.growMax = this.maxHeight;
41198     }
41199 };
41200
41201 Roo.extend(Roo.form.TextArea, Roo.form.TextField,  {
41202     /**
41203      * @cfg {Number} growMin The minimum height to allow when grow = true (defaults to 60)
41204      */
41205     growMin : 60,
41206     /**
41207      * @cfg {Number} growMax The maximum height to allow when grow = true (defaults to 1000)
41208      */
41209     growMax: 1000,
41210     /**
41211      * @cfg {Boolean} preventScrollbars True to prevent scrollbars from appearing regardless of how much text is
41212      * in the field (equivalent to setting overflow: hidden, defaults to false)
41213      */
41214     preventScrollbars: false,
41215     /**
41216      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
41217      * {tag: "textarea", style: "width:300px;height:60px;", autocomplete: "off"})
41218      */
41219
41220     // private
41221     onRender : function(ct, position){
41222         if(!this.el){
41223             this.defaultAutoCreate = {
41224                 tag: "textarea",
41225                 style:"width:300px;height:60px;",
41226                 autocomplete: "new-password"
41227             };
41228         }
41229         Roo.form.TextArea.superclass.onRender.call(this, ct, position);
41230         if(this.grow){
41231             this.textSizeEl = Roo.DomHelper.append(document.body, {
41232                 tag: "pre", cls: "x-form-grow-sizer"
41233             });
41234             if(this.preventScrollbars){
41235                 this.el.setStyle("overflow", "hidden");
41236             }
41237             this.el.setHeight(this.growMin);
41238         }
41239     },
41240
41241     onDestroy : function(){
41242         if(this.textSizeEl){
41243             this.textSizeEl.parentNode.removeChild(this.textSizeEl);
41244         }
41245         Roo.form.TextArea.superclass.onDestroy.call(this);
41246     },
41247
41248     // private
41249     onKeyUp : function(e){
41250         if(!e.isNavKeyPress() || e.getKey() == e.ENTER){
41251             this.autoSize();
41252         }
41253     },
41254
41255     /**
41256      * Automatically grows the field to accomodate the height of the text up to the maximum field height allowed.
41257      * This only takes effect if grow = true, and fires the autosize event if the height changes.
41258      */
41259     autoSize : function(){
41260         if(!this.grow || !this.textSizeEl){
41261             return;
41262         }
41263         var el = this.el;
41264         var v = el.dom.value;
41265         var ts = this.textSizeEl;
41266
41267         ts.innerHTML = '';
41268         ts.appendChild(document.createTextNode(v));
41269         v = ts.innerHTML;
41270
41271         Roo.fly(ts).setWidth(this.el.getWidth());
41272         if(v.length < 1){
41273             v = "&#160;&#160;";
41274         }else{
41275             if(Roo.isIE){
41276                 v = v.replace(/\n/g, '<p>&#160;</p>');
41277             }
41278             v += "&#160;\n&#160;";
41279         }
41280         ts.innerHTML = v;
41281         var h = Math.min(this.growMax, Math.max(ts.offsetHeight, this.growMin));
41282         if(h != this.lastHeight){
41283             this.lastHeight = h;
41284             this.el.setHeight(h);
41285             this.fireEvent("autosize", this, h);
41286         }
41287     }
41288 });/*
41289  * Based on:
41290  * Ext JS Library 1.1.1
41291  * Copyright(c) 2006-2007, Ext JS, LLC.
41292  *
41293  * Originally Released Under LGPL - original licence link has changed is not relivant.
41294  *
41295  * Fork - LGPL
41296  * <script type="text/javascript">
41297  */
41298  
41299
41300 /**
41301  * @class Roo.form.NumberField
41302  * @extends Roo.form.TextField
41303  * Numeric text field that provides automatic keystroke filtering and numeric validation.
41304  * @constructor
41305  * Creates a new NumberField
41306  * @param {Object} config Configuration options
41307  */
41308 Roo.form.NumberField = function(config){
41309     Roo.form.NumberField.superclass.constructor.call(this, config);
41310 };
41311
41312 Roo.extend(Roo.form.NumberField, Roo.form.TextField,  {
41313     /**
41314      * @cfg {String} fieldClass The default CSS class for the field (defaults to "x-form-field x-form-num-field")
41315      */
41316     fieldClass: "x-form-field x-form-num-field",
41317     /**
41318      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
41319      */
41320     allowDecimals : true,
41321     /**
41322      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
41323      */
41324     decimalSeparator : ".",
41325     /**
41326      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
41327      */
41328     decimalPrecision : 2,
41329     /**
41330      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
41331      */
41332     allowNegative : true,
41333     /**
41334      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
41335      */
41336     minValue : Number.NEGATIVE_INFINITY,
41337     /**
41338      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
41339      */
41340     maxValue : Number.MAX_VALUE,
41341     /**
41342      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
41343      */
41344     minText : "The minimum value for this field is {0}",
41345     /**
41346      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
41347      */
41348     maxText : "The maximum value for this field is {0}",
41349     /**
41350      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
41351      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
41352      */
41353     nanText : "{0} is not a valid number",
41354
41355     // private
41356     initEvents : function(){
41357         Roo.form.NumberField.superclass.initEvents.call(this);
41358         var allowed = "0123456789";
41359         if(this.allowDecimals){
41360             allowed += this.decimalSeparator;
41361         }
41362         if(this.allowNegative){
41363             allowed += "-";
41364         }
41365         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
41366         var keyPress = function(e){
41367             var k = e.getKey();
41368             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
41369                 return;
41370             }
41371             var c = e.getCharCode();
41372             if(allowed.indexOf(String.fromCharCode(c)) === -1){
41373                 e.stopEvent();
41374             }
41375         };
41376         this.el.on("keypress", keyPress, this);
41377     },
41378
41379     // private
41380     validateValue : function(value){
41381         if(!Roo.form.NumberField.superclass.validateValue.call(this, value)){
41382             return false;
41383         }
41384         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41385              return true;
41386         }
41387         var num = this.parseValue(value);
41388         if(isNaN(num)){
41389             this.markInvalid(String.format(this.nanText, value));
41390             return false;
41391         }
41392         if(num < this.minValue){
41393             this.markInvalid(String.format(this.minText, this.minValue));
41394             return false;
41395         }
41396         if(num > this.maxValue){
41397             this.markInvalid(String.format(this.maxText, this.maxValue));
41398             return false;
41399         }
41400         return true;
41401     },
41402
41403     getValue : function(){
41404         return this.fixPrecision(this.parseValue(Roo.form.NumberField.superclass.getValue.call(this)));
41405     },
41406
41407     // private
41408     parseValue : function(value){
41409         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
41410         return isNaN(value) ? '' : value;
41411     },
41412
41413     // private
41414     fixPrecision : function(value){
41415         var nan = isNaN(value);
41416         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
41417             return nan ? '' : value;
41418         }
41419         return parseFloat(value).toFixed(this.decimalPrecision);
41420     },
41421
41422     setValue : function(v){
41423         v = this.fixPrecision(v);
41424         Roo.form.NumberField.superclass.setValue.call(this, String(v).replace(".", this.decimalSeparator));
41425     },
41426
41427     // private
41428     decimalPrecisionFcn : function(v){
41429         return Math.floor(v);
41430     },
41431
41432     beforeBlur : function(){
41433         var v = this.parseValue(this.getRawValue());
41434         if(v){
41435             this.setValue(v);
41436         }
41437     }
41438 });/*
41439  * Based on:
41440  * Ext JS Library 1.1.1
41441  * Copyright(c) 2006-2007, Ext JS, LLC.
41442  *
41443  * Originally Released Under LGPL - original licence link has changed is not relivant.
41444  *
41445  * Fork - LGPL
41446  * <script type="text/javascript">
41447  */
41448  
41449 /**
41450  * @class Roo.form.DateField
41451  * @extends Roo.form.TriggerField
41452  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41453 * @constructor
41454 * Create a new DateField
41455 * @param {Object} config
41456  */
41457 Roo.form.DateField = function(config)
41458 {
41459     Roo.form.DateField.superclass.constructor.call(this, config);
41460     
41461       this.addEvents({
41462          
41463         /**
41464          * @event select
41465          * Fires when a date is selected
41466              * @param {Roo.form.DateField} combo This combo box
41467              * @param {Date} date The date selected
41468              */
41469         'select' : true
41470          
41471     });
41472     
41473     
41474     if(typeof this.minValue == "string") {
41475         this.minValue = this.parseDate(this.minValue);
41476     }
41477     if(typeof this.maxValue == "string") {
41478         this.maxValue = this.parseDate(this.maxValue);
41479     }
41480     this.ddMatch = null;
41481     if(this.disabledDates){
41482         var dd = this.disabledDates;
41483         var re = "(?:";
41484         for(var i = 0; i < dd.length; i++){
41485             re += dd[i];
41486             if(i != dd.length-1) {
41487                 re += "|";
41488             }
41489         }
41490         this.ddMatch = new RegExp(re + ")");
41491     }
41492 };
41493
41494 Roo.extend(Roo.form.DateField, Roo.form.TriggerField,  {
41495     /**
41496      * @cfg {String} format
41497      * The default date format string which can be overriden for localization support.  The format must be
41498      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41499      */
41500     format : "m/d/y",
41501     /**
41502      * @cfg {String} altFormats
41503      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41504      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41505      */
41506     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
41507     /**
41508      * @cfg {Array} disabledDays
41509      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41510      */
41511     disabledDays : null,
41512     /**
41513      * @cfg {String} disabledDaysText
41514      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41515      */
41516     disabledDaysText : "Disabled",
41517     /**
41518      * @cfg {Array} disabledDates
41519      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41520      * expression so they are very powerful. Some examples:
41521      * <ul>
41522      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41523      * <li>["03/08", "09/16"] would disable those days for every year</li>
41524      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41525      * <li>["03/../2006"] would disable every day in March 2006</li>
41526      * <li>["^03"] would disable every day in every March</li>
41527      * </ul>
41528      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41529      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41530      */
41531     disabledDates : null,
41532     /**
41533      * @cfg {String} disabledDatesText
41534      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41535      */
41536     disabledDatesText : "Disabled",
41537     /**
41538      * @cfg {Date/String} minValue
41539      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41540      * valid format (defaults to null).
41541      */
41542     minValue : null,
41543     /**
41544      * @cfg {Date/String} maxValue
41545      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41546      * valid format (defaults to null).
41547      */
41548     maxValue : null,
41549     /**
41550      * @cfg {String} minText
41551      * The error text to display when the date in the cell is before minValue (defaults to
41552      * 'The date in this field must be after {minValue}').
41553      */
41554     minText : "The date in this field must be equal to or after {0}",
41555     /**
41556      * @cfg {String} maxText
41557      * The error text to display when the date in the cell is after maxValue (defaults to
41558      * 'The date in this field must be before {maxValue}').
41559      */
41560     maxText : "The date in this field must be equal to or before {0}",
41561     /**
41562      * @cfg {String} invalidText
41563      * The error text to display when the date in the field is invalid (defaults to
41564      * '{value} is not a valid date - it must be in the format {format}').
41565      */
41566     invalidText : "{0} is not a valid date - it must be in the format {1}",
41567     /**
41568      * @cfg {String} triggerClass
41569      * An additional CSS class used to style the trigger button.  The trigger will always get the
41570      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41571      * which displays a calendar icon).
41572      */
41573     triggerClass : 'x-form-date-trigger',
41574     
41575
41576     /**
41577      * @cfg {Boolean} useIso
41578      * if enabled, then the date field will use a hidden field to store the 
41579      * real value as iso formated date. default (false)
41580      */ 
41581     useIso : false,
41582     /**
41583      * @cfg {String/Object} autoCreate
41584      * A DomHelper element spec, or true for a default element spec (defaults to
41585      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41586      */ 
41587     // private
41588     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "off"},
41589     
41590     // private
41591     hiddenField: false,
41592     
41593     onRender : function(ct, position)
41594     {
41595         Roo.form.DateField.superclass.onRender.call(this, ct, position);
41596         if (this.useIso) {
41597             //this.el.dom.removeAttribute('name'); 
41598             Roo.log("Changing name?");
41599             this.el.dom.setAttribute('name', this.name + '____hidden___' ); 
41600             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41601                     'before', true);
41602             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41603             // prevent input submission
41604             this.hiddenName = this.name;
41605         }
41606             
41607             
41608     },
41609     
41610     // private
41611     validateValue : function(value)
41612     {
41613         value = this.formatDate(value);
41614         if(!Roo.form.DateField.superclass.validateValue.call(this, value)){
41615             Roo.log('super failed');
41616             return false;
41617         }
41618         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41619              return true;
41620         }
41621         var svalue = value;
41622         value = this.parseDate(value);
41623         if(!value){
41624             Roo.log('parse date failed' + svalue);
41625             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41626             return false;
41627         }
41628         var time = value.getTime();
41629         if(this.minValue && time < this.minValue.getTime()){
41630             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
41631             return false;
41632         }
41633         if(this.maxValue && time > this.maxValue.getTime()){
41634             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
41635             return false;
41636         }
41637         if(this.disabledDays){
41638             var day = value.getDay();
41639             for(var i = 0; i < this.disabledDays.length; i++) {
41640                 if(day === this.disabledDays[i]){
41641                     this.markInvalid(this.disabledDaysText);
41642                     return false;
41643                 }
41644             }
41645         }
41646         var fvalue = this.formatDate(value);
41647         if(this.ddMatch && this.ddMatch.test(fvalue)){
41648             this.markInvalid(String.format(this.disabledDatesText, fvalue));
41649             return false;
41650         }
41651         return true;
41652     },
41653
41654     // private
41655     // Provides logic to override the default TriggerField.validateBlur which just returns true
41656     validateBlur : function(){
41657         return !this.menu || !this.menu.isVisible();
41658     },
41659     
41660     getName: function()
41661     {
41662         // returns hidden if it's set..
41663         if (!this.rendered) {return ''};
41664         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
41665         
41666     },
41667
41668     /**
41669      * Returns the current date value of the date field.
41670      * @return {Date} The date value
41671      */
41672     getValue : function(){
41673         
41674         return  this.hiddenField ?
41675                 this.hiddenField.value :
41676                 this.parseDate(Roo.form.DateField.superclass.getValue.call(this)) || "";
41677     },
41678
41679     /**
41680      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
41681      * date, using DateField.format as the date format, according to the same rules as {@link Date#parseDate}
41682      * (the default format used is "m/d/y").
41683      * <br />Usage:
41684      * <pre><code>
41685 //All of these calls set the same date value (May 4, 2006)
41686
41687 //Pass a date object:
41688 var dt = new Date('5/4/06');
41689 dateField.setValue(dt);
41690
41691 //Pass a date string (default format):
41692 dateField.setValue('5/4/06');
41693
41694 //Pass a date string (custom format):
41695 dateField.format = 'Y-m-d';
41696 dateField.setValue('2006-5-4');
41697 </code></pre>
41698      * @param {String/Date} date The date or valid date string
41699      */
41700     setValue : function(date){
41701         if (this.hiddenField) {
41702             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
41703         }
41704         Roo.form.DateField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
41705         // make sure the value field is always stored as a date..
41706         this.value = this.parseDate(date);
41707         
41708         
41709     },
41710
41711     // private
41712     parseDate : function(value){
41713         if(!value || value instanceof Date){
41714             return value;
41715         }
41716         var v = Date.parseDate(value, this.format);
41717          if (!v && this.useIso) {
41718             v = Date.parseDate(value, 'Y-m-d');
41719         }
41720         if(!v && this.altFormats){
41721             if(!this.altFormatsArray){
41722                 this.altFormatsArray = this.altFormats.split("|");
41723             }
41724             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
41725                 v = Date.parseDate(value, this.altFormatsArray[i]);
41726             }
41727         }
41728         return v;
41729     },
41730
41731     // private
41732     formatDate : function(date, fmt){
41733         return (!date || !(date instanceof Date)) ?
41734                date : date.dateFormat(fmt || this.format);
41735     },
41736
41737     // private
41738     menuListeners : {
41739         select: function(m, d){
41740             
41741             this.setValue(d);
41742             this.fireEvent('select', this, d);
41743         },
41744         show : function(){ // retain focus styling
41745             this.onFocus();
41746         },
41747         hide : function(){
41748             this.focus.defer(10, this);
41749             var ml = this.menuListeners;
41750             this.menu.un("select", ml.select,  this);
41751             this.menu.un("show", ml.show,  this);
41752             this.menu.un("hide", ml.hide,  this);
41753         }
41754     },
41755
41756     // private
41757     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
41758     onTriggerClick : function(){
41759         if(this.disabled){
41760             return;
41761         }
41762         if(this.menu == null){
41763             this.menu = new Roo.menu.DateMenu();
41764         }
41765         Roo.apply(this.menu.picker,  {
41766             showClear: this.allowBlank,
41767             minDate : this.minValue,
41768             maxDate : this.maxValue,
41769             disabledDatesRE : this.ddMatch,
41770             disabledDatesText : this.disabledDatesText,
41771             disabledDays : this.disabledDays,
41772             disabledDaysText : this.disabledDaysText,
41773             format : this.useIso ? 'Y-m-d' : this.format,
41774             minText : String.format(this.minText, this.formatDate(this.minValue)),
41775             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
41776         });
41777         this.menu.on(Roo.apply({}, this.menuListeners, {
41778             scope:this
41779         }));
41780         this.menu.picker.setValue(this.getValue() || new Date());
41781         this.menu.show(this.el, "tl-bl?");
41782     },
41783
41784     beforeBlur : function(){
41785         var v = this.parseDate(this.getRawValue());
41786         if(v){
41787             this.setValue(v);
41788         }
41789     },
41790
41791     /*@
41792      * overide
41793      * 
41794      */
41795     isDirty : function() {
41796         if(this.disabled) {
41797             return false;
41798         }
41799         
41800         if(typeof(this.startValue) === 'undefined'){
41801             return false;
41802         }
41803         
41804         return String(this.getValue()) !== String(this.startValue);
41805         
41806     },
41807     // @overide
41808     cleanLeadingSpace : function(e)
41809     {
41810        return;
41811     }
41812     
41813 });/*
41814  * Based on:
41815  * Ext JS Library 1.1.1
41816  * Copyright(c) 2006-2007, Ext JS, LLC.
41817  *
41818  * Originally Released Under LGPL - original licence link has changed is not relivant.
41819  *
41820  * Fork - LGPL
41821  * <script type="text/javascript">
41822  */
41823  
41824 /**
41825  * @class Roo.form.MonthField
41826  * @extends Roo.form.TriggerField
41827  * Provides a date input field with a {@link Roo.DatePicker} dropdown and automatic date validation.
41828 * @constructor
41829 * Create a new MonthField
41830 * @param {Object} config
41831  */
41832 Roo.form.MonthField = function(config){
41833     
41834     Roo.form.MonthField.superclass.constructor.call(this, config);
41835     
41836       this.addEvents({
41837          
41838         /**
41839          * @event select
41840          * Fires when a date is selected
41841              * @param {Roo.form.MonthFieeld} combo This combo box
41842              * @param {Date} date The date selected
41843              */
41844         'select' : true
41845          
41846     });
41847     
41848     
41849     if(typeof this.minValue == "string") {
41850         this.minValue = this.parseDate(this.minValue);
41851     }
41852     if(typeof this.maxValue == "string") {
41853         this.maxValue = this.parseDate(this.maxValue);
41854     }
41855     this.ddMatch = null;
41856     if(this.disabledDates){
41857         var dd = this.disabledDates;
41858         var re = "(?:";
41859         for(var i = 0; i < dd.length; i++){
41860             re += dd[i];
41861             if(i != dd.length-1) {
41862                 re += "|";
41863             }
41864         }
41865         this.ddMatch = new RegExp(re + ")");
41866     }
41867 };
41868
41869 Roo.extend(Roo.form.MonthField, Roo.form.TriggerField,  {
41870     /**
41871      * @cfg {String} format
41872      * The default date format string which can be overriden for localization support.  The format must be
41873      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
41874      */
41875     format : "M Y",
41876     /**
41877      * @cfg {String} altFormats
41878      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
41879      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
41880      */
41881     altFormats : "M Y|m/Y|m-y|m-Y|my|mY",
41882     /**
41883      * @cfg {Array} disabledDays
41884      * An array of days to disable, 0 based. For example, [0, 6] disables Sunday and Saturday (defaults to null).
41885      */
41886     disabledDays : [0,1,2,3,4,5,6],
41887     /**
41888      * @cfg {String} disabledDaysText
41889      * The tooltip to display when the date falls on a disabled day (defaults to 'Disabled')
41890      */
41891     disabledDaysText : "Disabled",
41892     /**
41893      * @cfg {Array} disabledDates
41894      * An array of "dates" to disable, as strings. These strings will be used to build a dynamic regular
41895      * expression so they are very powerful. Some examples:
41896      * <ul>
41897      * <li>["03/08/2003", "09/16/2003"] would disable those exact dates</li>
41898      * <li>["03/08", "09/16"] would disable those days for every year</li>
41899      * <li>["^03/08"] would only match the beginning (useful if you are using short years)</li>
41900      * <li>["03/../2006"] would disable every day in March 2006</li>
41901      * <li>["^03"] would disable every day in every March</li>
41902      * </ul>
41903      * In order to support regular expressions, if you are using a date format that has "." in it, you will have to
41904      * escape the dot when restricting dates. For example: ["03\\.08\\.03"].
41905      */
41906     disabledDates : null,
41907     /**
41908      * @cfg {String} disabledDatesText
41909      * The tooltip text to display when the date falls on a disabled date (defaults to 'Disabled')
41910      */
41911     disabledDatesText : "Disabled",
41912     /**
41913      * @cfg {Date/String} minValue
41914      * The minimum allowed date. Can be either a Javascript date object or a string date in a
41915      * valid format (defaults to null).
41916      */
41917     minValue : null,
41918     /**
41919      * @cfg {Date/String} maxValue
41920      * The maximum allowed date. Can be either a Javascript date object or a string date in a
41921      * valid format (defaults to null).
41922      */
41923     maxValue : null,
41924     /**
41925      * @cfg {String} minText
41926      * The error text to display when the date in the cell is before minValue (defaults to
41927      * 'The date in this field must be after {minValue}').
41928      */
41929     minText : "The date in this field must be equal to or after {0}",
41930     /**
41931      * @cfg {String} maxTextf
41932      * The error text to display when the date in the cell is after maxValue (defaults to
41933      * 'The date in this field must be before {maxValue}').
41934      */
41935     maxText : "The date in this field must be equal to or before {0}",
41936     /**
41937      * @cfg {String} invalidText
41938      * The error text to display when the date in the field is invalid (defaults to
41939      * '{value} is not a valid date - it must be in the format {format}').
41940      */
41941     invalidText : "{0} is not a valid date - it must be in the format {1}",
41942     /**
41943      * @cfg {String} triggerClass
41944      * An additional CSS class used to style the trigger button.  The trigger will always get the
41945      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-date-trigger'
41946      * which displays a calendar icon).
41947      */
41948     triggerClass : 'x-form-date-trigger',
41949     
41950
41951     /**
41952      * @cfg {Boolean} useIso
41953      * if enabled, then the date field will use a hidden field to store the 
41954      * real value as iso formated date. default (true)
41955      */ 
41956     useIso : true,
41957     /**
41958      * @cfg {String/Object} autoCreate
41959      * A DomHelper element spec, or true for a default element spec (defaults to
41960      * {tag: "input", type: "text", size: "10", autocomplete: "off"})
41961      */ 
41962     // private
41963     defaultAutoCreate : {tag: "input", type: "text", size: "10", autocomplete: "new-password"},
41964     
41965     // private
41966     hiddenField: false,
41967     
41968     hideMonthPicker : false,
41969     
41970     onRender : function(ct, position)
41971     {
41972         Roo.form.MonthField.superclass.onRender.call(this, ct, position);
41973         if (this.useIso) {
41974             this.el.dom.removeAttribute('name'); 
41975             this.hiddenField = this.el.insertSibling({ tag:'input', type:'hidden', name: this.name },
41976                     'before', true);
41977             this.hiddenField.value = this.value ? this.formatDate(this.value, 'Y-m-d') : '';
41978             // prevent input submission
41979             this.hiddenName = this.name;
41980         }
41981             
41982             
41983     },
41984     
41985     // private
41986     validateValue : function(value)
41987     {
41988         value = this.formatDate(value);
41989         if(!Roo.form.MonthField.superclass.validateValue.call(this, value)){
41990             return false;
41991         }
41992         if(value.length < 1){ // if it's blank and textfield didn't flag it then it's valid
41993              return true;
41994         }
41995         var svalue = value;
41996         value = this.parseDate(value);
41997         if(!value){
41998             this.markInvalid(String.format(this.invalidText, svalue, this.format));
41999             return false;
42000         }
42001         var time = value.getTime();
42002         if(this.minValue && time < this.minValue.getTime()){
42003             this.markInvalid(String.format(this.minText, this.formatDate(this.minValue)));
42004             return false;
42005         }
42006         if(this.maxValue && time > this.maxValue.getTime()){
42007             this.markInvalid(String.format(this.maxText, this.formatDate(this.maxValue)));
42008             return false;
42009         }
42010         /*if(this.disabledDays){
42011             var day = value.getDay();
42012             for(var i = 0; i < this.disabledDays.length; i++) {
42013                 if(day === this.disabledDays[i]){
42014                     this.markInvalid(this.disabledDaysText);
42015                     return false;
42016                 }
42017             }
42018         }
42019         */
42020         var fvalue = this.formatDate(value);
42021         /*if(this.ddMatch && this.ddMatch.test(fvalue)){
42022             this.markInvalid(String.format(this.disabledDatesText, fvalue));
42023             return false;
42024         }
42025         */
42026         return true;
42027     },
42028
42029     // private
42030     // Provides logic to override the default TriggerField.validateBlur which just returns true
42031     validateBlur : function(){
42032         return !this.menu || !this.menu.isVisible();
42033     },
42034
42035     /**
42036      * Returns the current date value of the date field.
42037      * @return {Date} The date value
42038      */
42039     getValue : function(){
42040         
42041         
42042         
42043         return  this.hiddenField ?
42044                 this.hiddenField.value :
42045                 this.parseDate(Roo.form.MonthField.superclass.getValue.call(this)) || "";
42046     },
42047
42048     /**
42049      * Sets the value of the date field.  You can pass a date object or any string that can be parsed into a valid
42050      * date, using MonthField.format as the date format, according to the same rules as {@link Date#parseDate}
42051      * (the default format used is "m/d/y").
42052      * <br />Usage:
42053      * <pre><code>
42054 //All of these calls set the same date value (May 4, 2006)
42055
42056 //Pass a date object:
42057 var dt = new Date('5/4/06');
42058 monthField.setValue(dt);
42059
42060 //Pass a date string (default format):
42061 monthField.setValue('5/4/06');
42062
42063 //Pass a date string (custom format):
42064 monthField.format = 'Y-m-d';
42065 monthField.setValue('2006-5-4');
42066 </code></pre>
42067      * @param {String/Date} date The date or valid date string
42068      */
42069     setValue : function(date){
42070         Roo.log('month setValue' + date);
42071         // can only be first of month..
42072         
42073         var val = this.parseDate(date);
42074         
42075         if (this.hiddenField) {
42076             this.hiddenField.value = this.formatDate(this.parseDate(date), 'Y-m-d');
42077         }
42078         Roo.form.MonthField.superclass.setValue.call(this, this.formatDate(this.parseDate(date)));
42079         this.value = this.parseDate(date);
42080     },
42081
42082     // private
42083     parseDate : function(value){
42084         if(!value || value instanceof Date){
42085             value = value ? Date.parseDate(value.format('Y-m') + '-01', 'Y-m-d') : null;
42086             return value;
42087         }
42088         var v = Date.parseDate(value, this.format);
42089         if (!v && this.useIso) {
42090             v = Date.parseDate(value, 'Y-m-d');
42091         }
42092         if (v) {
42093             // 
42094             v = Date.parseDate(v.format('Y-m') +'-01', 'Y-m-d');
42095         }
42096         
42097         
42098         if(!v && this.altFormats){
42099             if(!this.altFormatsArray){
42100                 this.altFormatsArray = this.altFormats.split("|");
42101             }
42102             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
42103                 v = Date.parseDate(value, this.altFormatsArray[i]);
42104             }
42105         }
42106         return v;
42107     },
42108
42109     // private
42110     formatDate : function(date, fmt){
42111         return (!date || !(date instanceof Date)) ?
42112                date : date.dateFormat(fmt || this.format);
42113     },
42114
42115     // private
42116     menuListeners : {
42117         select: function(m, d){
42118             this.setValue(d);
42119             this.fireEvent('select', this, d);
42120         },
42121         show : function(){ // retain focus styling
42122             this.onFocus();
42123         },
42124         hide : function(){
42125             this.focus.defer(10, this);
42126             var ml = this.menuListeners;
42127             this.menu.un("select", ml.select,  this);
42128             this.menu.un("show", ml.show,  this);
42129             this.menu.un("hide", ml.hide,  this);
42130         }
42131     },
42132     // private
42133     // Implements the default empty TriggerField.onTriggerClick function to display the DatePicker
42134     onTriggerClick : function(){
42135         if(this.disabled){
42136             return;
42137         }
42138         if(this.menu == null){
42139             this.menu = new Roo.menu.DateMenu();
42140            
42141         }
42142         
42143         Roo.apply(this.menu.picker,  {
42144             
42145             showClear: this.allowBlank,
42146             minDate : this.minValue,
42147             maxDate : this.maxValue,
42148             disabledDatesRE : this.ddMatch,
42149             disabledDatesText : this.disabledDatesText,
42150             
42151             format : this.useIso ? 'Y-m-d' : this.format,
42152             minText : String.format(this.minText, this.formatDate(this.minValue)),
42153             maxText : String.format(this.maxText, this.formatDate(this.maxValue))
42154             
42155         });
42156          this.menu.on(Roo.apply({}, this.menuListeners, {
42157             scope:this
42158         }));
42159        
42160         
42161         var m = this.menu;
42162         var p = m.picker;
42163         
42164         // hide month picker get's called when we called by 'before hide';
42165         
42166         var ignorehide = true;
42167         p.hideMonthPicker  = function(disableAnim){
42168             if (ignorehide) {
42169                 return;
42170             }
42171              if(this.monthPicker){
42172                 Roo.log("hideMonthPicker called");
42173                 if(disableAnim === true){
42174                     this.monthPicker.hide();
42175                 }else{
42176                     this.monthPicker.slideOut('t', {duration:.2});
42177                     p.setValue(new Date(m.picker.mpSelYear, m.picker.mpSelMonth, 1));
42178                     p.fireEvent("select", this, this.value);
42179                     m.hide();
42180                 }
42181             }
42182         }
42183         
42184         Roo.log('picker set value');
42185         Roo.log(this.getValue());
42186         p.setValue(this.getValue() ? this.parseDate(this.getValue()) : new Date());
42187         m.show(this.el, 'tl-bl?');
42188         ignorehide  = false;
42189         // this will trigger hideMonthPicker..
42190         
42191         
42192         // hidden the day picker
42193         Roo.select('.x-date-picker table', true).first().dom.style.visibility = "hidden";
42194         
42195         
42196         
42197       
42198         
42199         p.showMonthPicker.defer(100, p);
42200     
42201         
42202        
42203     },
42204
42205     beforeBlur : function(){
42206         var v = this.parseDate(this.getRawValue());
42207         if(v){
42208             this.setValue(v);
42209         }
42210     }
42211
42212     /** @cfg {Boolean} grow @hide */
42213     /** @cfg {Number} growMin @hide */
42214     /** @cfg {Number} growMax @hide */
42215     /**
42216      * @hide
42217      * @method autoSize
42218      */
42219 });/*
42220  * Based on:
42221  * Ext JS Library 1.1.1
42222  * Copyright(c) 2006-2007, Ext JS, LLC.
42223  *
42224  * Originally Released Under LGPL - original licence link has changed is not relivant.
42225  *
42226  * Fork - LGPL
42227  * <script type="text/javascript">
42228  */
42229  
42230
42231 /**
42232  * @class Roo.form.ComboBox
42233  * @extends Roo.form.TriggerField
42234  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
42235  * @constructor
42236  * Create a new ComboBox.
42237  * @param {Object} config Configuration options
42238  */
42239 Roo.form.ComboBox = function(config){
42240     Roo.form.ComboBox.superclass.constructor.call(this, config);
42241     this.addEvents({
42242         /**
42243          * @event expand
42244          * Fires when the dropdown list is expanded
42245              * @param {Roo.form.ComboBox} combo This combo box
42246              */
42247         'expand' : true,
42248         /**
42249          * @event collapse
42250          * Fires when the dropdown list is collapsed
42251              * @param {Roo.form.ComboBox} combo This combo box
42252              */
42253         'collapse' : true,
42254         /**
42255          * @event beforeselect
42256          * Fires before a list item is selected. Return false to cancel the selection.
42257              * @param {Roo.form.ComboBox} combo This combo box
42258              * @param {Roo.data.Record} record The data record returned from the underlying store
42259              * @param {Number} index The index of the selected item in the dropdown list
42260              */
42261         'beforeselect' : true,
42262         /**
42263          * @event select
42264          * Fires when a list item is selected
42265              * @param {Roo.form.ComboBox} combo This combo box
42266              * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
42267              * @param {Number} index The index of the selected item in the dropdown list
42268              */
42269         'select' : true,
42270         /**
42271          * @event beforequery
42272          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
42273          * The event object passed has these properties:
42274              * @param {Roo.form.ComboBox} combo This combo box
42275              * @param {String} query The query
42276              * @param {Boolean} forceAll true to force "all" query
42277              * @param {Boolean} cancel true to cancel the query
42278              * @param {Object} e The query event object
42279              */
42280         'beforequery': true,
42281          /**
42282          * @event add
42283          * Fires when the 'add' icon is pressed (add a listener to enable add button)
42284              * @param {Roo.form.ComboBox} combo This combo box
42285              */
42286         'add' : true,
42287         /**
42288          * @event edit
42289          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
42290              * @param {Roo.form.ComboBox} combo This combo box
42291              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
42292              */
42293         'edit' : true
42294         
42295         
42296     });
42297     if(this.transform){
42298         this.allowDomMove = false;
42299         var s = Roo.getDom(this.transform);
42300         if(!this.hiddenName){
42301             this.hiddenName = s.name;
42302         }
42303         if(!this.store){
42304             this.mode = 'local';
42305             var d = [], opts = s.options;
42306             for(var i = 0, len = opts.length;i < len; i++){
42307                 var o = opts[i];
42308                 var value = (Roo.isIE ? o.getAttributeNode('value').specified : o.hasAttribute('value')) ? o.value : o.text;
42309                 if(o.selected) {
42310                     this.value = value;
42311                 }
42312                 d.push([value, o.text]);
42313             }
42314             this.store = new Roo.data.SimpleStore({
42315                 'id': 0,
42316                 fields: ['value', 'text'],
42317                 data : d
42318             });
42319             this.valueField = 'value';
42320             this.displayField = 'text';
42321         }
42322         s.name = Roo.id(); // wipe out the name in case somewhere else they have a reference
42323         if(!this.lazyRender){
42324             this.target = true;
42325             this.el = Roo.DomHelper.insertBefore(s, this.autoCreate || this.defaultAutoCreate);
42326             s.parentNode.removeChild(s); // remove it
42327             this.render(this.el.parentNode);
42328         }else{
42329             s.parentNode.removeChild(s); // remove it
42330         }
42331
42332     }
42333     if (this.store) {
42334         this.store = Roo.factory(this.store, Roo.data);
42335     }
42336     
42337     this.selectedIndex = -1;
42338     if(this.mode == 'local'){
42339         if(config.queryDelay === undefined){
42340             this.queryDelay = 10;
42341         }
42342         if(config.minChars === undefined){
42343             this.minChars = 0;
42344         }
42345     }
42346 };
42347
42348 Roo.extend(Roo.form.ComboBox, Roo.form.TriggerField, {
42349     /**
42350      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
42351      */
42352     /**
42353      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
42354      * rendering into an Roo.Editor, defaults to false)
42355      */
42356     /**
42357      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
42358      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
42359      */
42360     /**
42361      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
42362      */
42363     /**
42364      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
42365      * the dropdown list (defaults to undefined, with no header element)
42366      */
42367
42368      /**
42369      * @cfg {String/Roo.Template} tpl The template to use to render the output
42370      */
42371      
42372     // private
42373     defaultAutoCreate : {tag: "input", type: "text", size: "24", autocomplete: "off"},
42374     /**
42375      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
42376      */
42377     listWidth: undefined,
42378     /**
42379      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
42380      * mode = 'remote' or 'text' if mode = 'local')
42381      */
42382     displayField: undefined,
42383     /**
42384      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
42385      * mode = 'remote' or 'value' if mode = 'local'). 
42386      * Note: use of a valueField requires the user make a selection
42387      * in order for a value to be mapped.
42388      */
42389     valueField: undefined,
42390     
42391     
42392     /**
42393      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
42394      * field's data value (defaults to the underlying DOM element's name)
42395      */
42396     hiddenName: undefined,
42397     /**
42398      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
42399      */
42400     listClass: '',
42401     /**
42402      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
42403      */
42404     selectedClass: 'x-combo-selected',
42405     /**
42406      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
42407      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
42408      * which displays a downward arrow icon).
42409      */
42410     triggerClass : 'x-form-arrow-trigger',
42411     /**
42412      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
42413      */
42414     shadow:'sides',
42415     /**
42416      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
42417      * anchor positions (defaults to 'tl-bl')
42418      */
42419     listAlign: 'tl-bl?',
42420     /**
42421      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
42422      */
42423     maxHeight: 300,
42424     /**
42425      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
42426      * query specified by the allQuery config option (defaults to 'query')
42427      */
42428     triggerAction: 'query',
42429     /**
42430      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
42431      * (defaults to 4, does not apply if editable = false)
42432      */
42433     minChars : 4,
42434     /**
42435      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
42436      * delay (typeAheadDelay) if it matches a known value (defaults to false)
42437      */
42438     typeAhead: false,
42439     /**
42440      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
42441      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
42442      */
42443     queryDelay: 500,
42444     /**
42445      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
42446      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
42447      */
42448     pageSize: 0,
42449     /**
42450      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
42451      * when editable = true (defaults to false)
42452      */
42453     selectOnFocus:false,
42454     /**
42455      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
42456      */
42457     queryParam: 'query',
42458     /**
42459      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
42460      * when mode = 'remote' (defaults to 'Loading...')
42461      */
42462     loadingText: 'Loading...',
42463     /**
42464      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
42465      */
42466     resizable: false,
42467     /**
42468      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
42469      */
42470     handleHeight : 8,
42471     /**
42472      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
42473      * traditional select (defaults to true)
42474      */
42475     editable: true,
42476     /**
42477      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
42478      */
42479     allQuery: '',
42480     /**
42481      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
42482      */
42483     mode: 'remote',
42484     /**
42485      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
42486      * listWidth has a higher value)
42487      */
42488     minListWidth : 70,
42489     /**
42490      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
42491      * allow the user to set arbitrary text into the field (defaults to false)
42492      */
42493     forceSelection:false,
42494     /**
42495      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
42496      * if typeAhead = true (defaults to 250)
42497      */
42498     typeAheadDelay : 250,
42499     /**
42500      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
42501      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
42502      */
42503     valueNotFoundText : undefined,
42504     /**
42505      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
42506      */
42507     blockFocus : false,
42508     
42509     /**
42510      * @cfg {Boolean} disableClear Disable showing of clear button.
42511      */
42512     disableClear : false,
42513     /**
42514      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
42515      */
42516     alwaysQuery : false,
42517     
42518     //private
42519     addicon : false,
42520     editicon: false,
42521     
42522     // element that contains real text value.. (when hidden is used..)
42523      
42524     // private
42525     onRender : function(ct, position)
42526     {
42527         Roo.form.ComboBox.superclass.onRender.call(this, ct, position);
42528         
42529         if(this.hiddenName){
42530             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
42531                     'before', true);
42532             this.hiddenField.value =
42533                 this.hiddenValue !== undefined ? this.hiddenValue :
42534                 this.value !== undefined ? this.value : '';
42535
42536             // prevent input submission
42537             this.el.dom.removeAttribute('name');
42538              
42539              
42540         }
42541         
42542         if(Roo.isGecko){
42543             this.el.dom.setAttribute('autocomplete', 'off');
42544         }
42545
42546         var cls = 'x-combo-list';
42547
42548         this.list = new Roo.Layer({
42549             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
42550         });
42551
42552         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
42553         this.list.setWidth(lw);
42554         this.list.swallowEvent('mousewheel');
42555         this.assetHeight = 0;
42556
42557         if(this.title){
42558             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
42559             this.assetHeight += this.header.getHeight();
42560         }
42561
42562         this.innerList = this.list.createChild({cls:cls+'-inner'});
42563         this.innerList.on('mouseover', this.onViewOver, this);
42564         this.innerList.on('mousemove', this.onViewMove, this);
42565         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42566         
42567         if(this.allowBlank && !this.pageSize && !this.disableClear){
42568             this.footer = this.list.createChild({cls:cls+'-ft'});
42569             this.pageTb = new Roo.Toolbar(this.footer);
42570            
42571         }
42572         if(this.pageSize){
42573             this.footer = this.list.createChild({cls:cls+'-ft'});
42574             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
42575                     {pageSize: this.pageSize});
42576             
42577         }
42578         
42579         if (this.pageTb && this.allowBlank && !this.disableClear) {
42580             var _this = this;
42581             this.pageTb.add(new Roo.Toolbar.Fill(), {
42582                 cls: 'x-btn-icon x-btn-clear',
42583                 text: '&#160;',
42584                 handler: function()
42585                 {
42586                     _this.collapse();
42587                     _this.clearValue();
42588                     _this.onSelect(false, -1);
42589                 }
42590             });
42591         }
42592         if (this.footer) {
42593             this.assetHeight += this.footer.getHeight();
42594         }
42595         
42596
42597         if(!this.tpl){
42598             this.tpl = '<div class="'+cls+'-item">{' + this.displayField + '}</div>';
42599         }
42600
42601         this.view = new Roo.View(this.innerList, this.tpl, {
42602             singleSelect:true,
42603             store: this.store,
42604             selectedClass: this.selectedClass
42605         });
42606
42607         this.view.on('click', this.onViewClick, this);
42608
42609         this.store.on('beforeload', this.onBeforeLoad, this);
42610         this.store.on('load', this.onLoad, this);
42611         this.store.on('loadexception', this.onLoadException, this);
42612
42613         if(this.resizable){
42614             this.resizer = new Roo.Resizable(this.list,  {
42615                pinned:true, handles:'se'
42616             });
42617             this.resizer.on('resize', function(r, w, h){
42618                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
42619                 this.listWidth = w;
42620                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
42621                 this.restrictHeight();
42622             }, this);
42623             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
42624         }
42625         if(!this.editable){
42626             this.editable = true;
42627             this.setEditable(false);
42628         }  
42629         
42630         
42631         if (typeof(this.events.add.listeners) != 'undefined') {
42632             
42633             this.addicon = this.wrap.createChild(
42634                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
42635        
42636             this.addicon.on('click', function(e) {
42637                 this.fireEvent('add', this);
42638             }, this);
42639         }
42640         if (typeof(this.events.edit.listeners) != 'undefined') {
42641             
42642             this.editicon = this.wrap.createChild(
42643                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
42644             if (this.addicon) {
42645                 this.editicon.setStyle('margin-left', '40px');
42646             }
42647             this.editicon.on('click', function(e) {
42648                 
42649                 // we fire even  if inothing is selected..
42650                 this.fireEvent('edit', this, this.lastData );
42651                 
42652             }, this);
42653         }
42654         
42655         
42656         
42657     },
42658
42659     // private
42660     initEvents : function(){
42661         Roo.form.ComboBox.superclass.initEvents.call(this);
42662
42663         this.keyNav = new Roo.KeyNav(this.el, {
42664             "up" : function(e){
42665                 this.inKeyMode = true;
42666                 this.selectPrev();
42667             },
42668
42669             "down" : function(e){
42670                 if(!this.isExpanded()){
42671                     this.onTriggerClick();
42672                 }else{
42673                     this.inKeyMode = true;
42674                     this.selectNext();
42675                 }
42676             },
42677
42678             "enter" : function(e){
42679                 this.onViewClick();
42680                 //return true;
42681             },
42682
42683             "esc" : function(e){
42684                 this.collapse();
42685             },
42686
42687             "tab" : function(e){
42688                 this.onViewClick(false);
42689                 this.fireEvent("specialkey", this, e);
42690                 return true;
42691             },
42692
42693             scope : this,
42694
42695             doRelay : function(foo, bar, hname){
42696                 if(hname == 'down' || this.scope.isExpanded()){
42697                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
42698                 }
42699                 return true;
42700             },
42701
42702             forceKeyDown: true
42703         });
42704         this.queryDelay = Math.max(this.queryDelay || 10,
42705                 this.mode == 'local' ? 10 : 250);
42706         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
42707         if(this.typeAhead){
42708             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
42709         }
42710         if(this.editable !== false){
42711             this.el.on("keyup", this.onKeyUp, this);
42712         }
42713         if(this.forceSelection){
42714             this.on('blur', this.doForce, this);
42715         }
42716     },
42717
42718     onDestroy : function(){
42719         if(this.view){
42720             this.view.setStore(null);
42721             this.view.el.removeAllListeners();
42722             this.view.el.remove();
42723             this.view.purgeListeners();
42724         }
42725         if(this.list){
42726             this.list.destroy();
42727         }
42728         if(this.store){
42729             this.store.un('beforeload', this.onBeforeLoad, this);
42730             this.store.un('load', this.onLoad, this);
42731             this.store.un('loadexception', this.onLoadException, this);
42732         }
42733         Roo.form.ComboBox.superclass.onDestroy.call(this);
42734     },
42735
42736     // private
42737     fireKey : function(e){
42738         if(e.isNavKeyPress() && !this.list.isVisible()){
42739             this.fireEvent("specialkey", this, e);
42740         }
42741     },
42742
42743     // private
42744     onResize: function(w, h){
42745         Roo.form.ComboBox.superclass.onResize.apply(this, arguments);
42746         
42747         if(typeof w != 'number'){
42748             // we do not handle it!?!?
42749             return;
42750         }
42751         var tw = this.trigger.getWidth();
42752         tw += this.addicon ? this.addicon.getWidth() : 0;
42753         tw += this.editicon ? this.editicon.getWidth() : 0;
42754         var x = w - tw;
42755         this.el.setWidth( this.adjustWidth('input', x));
42756             
42757         this.trigger.setStyle('left', x+'px');
42758         
42759         if(this.list && this.listWidth === undefined){
42760             var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
42761             this.list.setWidth(lw);
42762             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
42763         }
42764         
42765     
42766         
42767     },
42768
42769     /**
42770      * Allow or prevent the user from directly editing the field text.  If false is passed,
42771      * the user will only be able to select from the items defined in the dropdown list.  This method
42772      * is the runtime equivalent of setting the 'editable' config option at config time.
42773      * @param {Boolean} value True to allow the user to directly edit the field text
42774      */
42775     setEditable : function(value){
42776         if(value == this.editable){
42777             return;
42778         }
42779         this.editable = value;
42780         if(!value){
42781             this.el.dom.setAttribute('readOnly', true);
42782             this.el.on('mousedown', this.onTriggerClick,  this);
42783             this.el.addClass('x-combo-noedit');
42784         }else{
42785             this.el.dom.setAttribute('readOnly', false);
42786             this.el.un('mousedown', this.onTriggerClick,  this);
42787             this.el.removeClass('x-combo-noedit');
42788         }
42789     },
42790
42791     // private
42792     onBeforeLoad : function(){
42793         if(!this.hasFocus){
42794             return;
42795         }
42796         this.innerList.update(this.loadingText ?
42797                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
42798         this.restrictHeight();
42799         this.selectedIndex = -1;
42800     },
42801
42802     // private
42803     onLoad : function(){
42804         if(!this.hasFocus){
42805             return;
42806         }
42807         if(this.store.getCount() > 0){
42808             this.expand();
42809             this.restrictHeight();
42810             if(this.lastQuery == this.allQuery){
42811                 if(this.editable){
42812                     this.el.dom.select();
42813                 }
42814                 if(!this.selectByValue(this.value, true)){
42815                     this.select(0, true);
42816                 }
42817             }else{
42818                 this.selectNext();
42819                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
42820                     this.taTask.delay(this.typeAheadDelay);
42821                 }
42822             }
42823         }else{
42824             this.onEmptyResults();
42825         }
42826         //this.el.focus();
42827     },
42828     // private
42829     onLoadException : function()
42830     {
42831         this.collapse();
42832         Roo.log(this.store.reader.jsonData);
42833         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
42834             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
42835         }
42836         
42837         
42838     },
42839     // private
42840     onTypeAhead : function(){
42841         if(this.store.getCount() > 0){
42842             var r = this.store.getAt(0);
42843             var newValue = r.data[this.displayField];
42844             var len = newValue.length;
42845             var selStart = this.getRawValue().length;
42846             if(selStart != len){
42847                 this.setRawValue(newValue);
42848                 this.selectText(selStart, newValue.length);
42849             }
42850         }
42851     },
42852
42853     // private
42854     onSelect : function(record, index){
42855         if(this.fireEvent('beforeselect', this, record, index) !== false){
42856             this.setFromData(index > -1 ? record.data : false);
42857             this.collapse();
42858             this.fireEvent('select', this, record, index);
42859         }
42860     },
42861
42862     /**
42863      * Returns the currently selected field value or empty string if no value is set.
42864      * @return {String} value The selected value
42865      */
42866     getValue : function(){
42867         if(this.valueField){
42868             return typeof this.value != 'undefined' ? this.value : '';
42869         }
42870         return Roo.form.ComboBox.superclass.getValue.call(this);
42871     },
42872
42873     /**
42874      * Clears any text/value currently set in the field
42875      */
42876     clearValue : function(){
42877         if(this.hiddenField){
42878             this.hiddenField.value = '';
42879         }
42880         this.value = '';
42881         this.setRawValue('');
42882         this.lastSelectionText = '';
42883         
42884     },
42885
42886     /**
42887      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
42888      * will be displayed in the field.  If the value does not match the data value of an existing item,
42889      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
42890      * Otherwise the field will be blank (although the value will still be set).
42891      * @param {String} value The value to match
42892      */
42893     setValue : function(v){
42894         var text = v;
42895         if(this.valueField){
42896             var r = this.findRecord(this.valueField, v);
42897             if(r){
42898                 text = r.data[this.displayField];
42899             }else if(this.valueNotFoundText !== undefined){
42900                 text = this.valueNotFoundText;
42901             }
42902         }
42903         this.lastSelectionText = text;
42904         if(this.hiddenField){
42905             this.hiddenField.value = v;
42906         }
42907         Roo.form.ComboBox.superclass.setValue.call(this, text);
42908         this.value = v;
42909     },
42910     /**
42911      * @property {Object} the last set data for the element
42912      */
42913     
42914     lastData : false,
42915     /**
42916      * Sets the value of the field based on a object which is related to the record format for the store.
42917      * @param {Object} value the value to set as. or false on reset?
42918      */
42919     setFromData : function(o){
42920         var dv = ''; // display value
42921         var vv = ''; // value value..
42922         this.lastData = o;
42923         if (this.displayField) {
42924             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
42925         } else {
42926             // this is an error condition!!!
42927             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
42928         }
42929         
42930         if(this.valueField){
42931             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
42932         }
42933         if(this.hiddenField){
42934             this.hiddenField.value = vv;
42935             
42936             this.lastSelectionText = dv;
42937             Roo.form.ComboBox.superclass.setValue.call(this, dv);
42938             this.value = vv;
42939             return;
42940         }
42941         // no hidden field.. - we store the value in 'value', but still display
42942         // display field!!!!
42943         this.lastSelectionText = dv;
42944         Roo.form.ComboBox.superclass.setValue.call(this, dv);
42945         this.value = vv;
42946         
42947         
42948     },
42949     // private
42950     reset : function(){
42951         // overridden so that last data is reset..
42952         this.setValue(this.resetValue);
42953         this.originalValue = this.getValue();
42954         this.clearInvalid();
42955         this.lastData = false;
42956         if (this.view) {
42957             this.view.clearSelections();
42958         }
42959     },
42960     // private
42961     findRecord : function(prop, value){
42962         var record;
42963         if(this.store.getCount() > 0){
42964             this.store.each(function(r){
42965                 if(r.data[prop] == value){
42966                     record = r;
42967                     return false;
42968                 }
42969                 return true;
42970             });
42971         }
42972         return record;
42973     },
42974     
42975     getName: function()
42976     {
42977         // returns hidden if it's set..
42978         if (!this.rendered) {return ''};
42979         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
42980         
42981     },
42982     // private
42983     onViewMove : function(e, t){
42984         this.inKeyMode = false;
42985     },
42986
42987     // private
42988     onViewOver : function(e, t){
42989         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
42990             return;
42991         }
42992         var item = this.view.findItemFromChild(t);
42993         if(item){
42994             var index = this.view.indexOf(item);
42995             this.select(index, false);
42996         }
42997     },
42998
42999     // private
43000     onViewClick : function(doFocus)
43001     {
43002         var index = this.view.getSelectedIndexes()[0];
43003         var r = this.store.getAt(index);
43004         if(r){
43005             this.onSelect(r, index);
43006         }
43007         if(doFocus !== false && !this.blockFocus){
43008             this.el.focus();
43009         }
43010     },
43011
43012     // private
43013     restrictHeight : function(){
43014         this.innerList.dom.style.height = '';
43015         var inner = this.innerList.dom;
43016         var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
43017         this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
43018         this.list.beginUpdate();
43019         this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
43020         this.list.alignTo(this.el, this.listAlign);
43021         this.list.endUpdate();
43022     },
43023
43024     // private
43025     onEmptyResults : function(){
43026         this.collapse();
43027     },
43028
43029     /**
43030      * Returns true if the dropdown list is expanded, else false.
43031      */
43032     isExpanded : function(){
43033         return this.list.isVisible();
43034     },
43035
43036     /**
43037      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
43038      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43039      * @param {String} value The data value of the item to select
43040      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43041      * selected item if it is not currently in view (defaults to true)
43042      * @return {Boolean} True if the value matched an item in the list, else false
43043      */
43044     selectByValue : function(v, scrollIntoView){
43045         if(v !== undefined && v !== null){
43046             var r = this.findRecord(this.valueField || this.displayField, v);
43047             if(r){
43048                 this.select(this.store.indexOf(r), scrollIntoView);
43049                 return true;
43050             }
43051         }
43052         return false;
43053     },
43054
43055     /**
43056      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
43057      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
43058      * @param {Number} index The zero-based index of the list item to select
43059      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
43060      * selected item if it is not currently in view (defaults to true)
43061      */
43062     select : function(index, scrollIntoView){
43063         this.selectedIndex = index;
43064         this.view.select(index);
43065         if(scrollIntoView !== false){
43066             var el = this.view.getNode(index);
43067             if(el){
43068                 this.innerList.scrollChildIntoView(el, false);
43069             }
43070         }
43071     },
43072
43073     // private
43074     selectNext : function(){
43075         var ct = this.store.getCount();
43076         if(ct > 0){
43077             if(this.selectedIndex == -1){
43078                 this.select(0);
43079             }else if(this.selectedIndex < ct-1){
43080                 this.select(this.selectedIndex+1);
43081             }
43082         }
43083     },
43084
43085     // private
43086     selectPrev : function(){
43087         var ct = this.store.getCount();
43088         if(ct > 0){
43089             if(this.selectedIndex == -1){
43090                 this.select(0);
43091             }else if(this.selectedIndex != 0){
43092                 this.select(this.selectedIndex-1);
43093             }
43094         }
43095     },
43096
43097     // private
43098     onKeyUp : function(e){
43099         if(this.editable !== false && !e.isSpecialKey()){
43100             this.lastKey = e.getKey();
43101             this.dqTask.delay(this.queryDelay);
43102         }
43103     },
43104
43105     // private
43106     validateBlur : function(){
43107         return !this.list || !this.list.isVisible();   
43108     },
43109
43110     // private
43111     initQuery : function(){
43112         this.doQuery(this.getRawValue());
43113     },
43114
43115     // private
43116     doForce : function(){
43117         if(this.el.dom.value.length > 0){
43118             this.el.dom.value =
43119                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
43120              
43121         }
43122     },
43123
43124     /**
43125      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
43126      * query allowing the query action to be canceled if needed.
43127      * @param {String} query The SQL query to execute
43128      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
43129      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
43130      * saved in the current store (defaults to false)
43131      */
43132     doQuery : function(q, forceAll){
43133         if(q === undefined || q === null){
43134             q = '';
43135         }
43136         var qe = {
43137             query: q,
43138             forceAll: forceAll,
43139             combo: this,
43140             cancel:false
43141         };
43142         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
43143             return false;
43144         }
43145         q = qe.query;
43146         forceAll = qe.forceAll;
43147         if(forceAll === true || (q.length >= this.minChars)){
43148             if(this.lastQuery != q || this.alwaysQuery){
43149                 this.lastQuery = q;
43150                 if(this.mode == 'local'){
43151                     this.selectedIndex = -1;
43152                     if(forceAll){
43153                         this.store.clearFilter();
43154                     }else{
43155                         this.store.filter(this.displayField, q);
43156                     }
43157                     this.onLoad();
43158                 }else{
43159                     this.store.baseParams[this.queryParam] = q;
43160                     this.store.load({
43161                         params: this.getParams(q)
43162                     });
43163                     this.expand();
43164                 }
43165             }else{
43166                 this.selectedIndex = -1;
43167                 this.onLoad();   
43168             }
43169         }
43170     },
43171
43172     // private
43173     getParams : function(q){
43174         var p = {};
43175         //p[this.queryParam] = q;
43176         if(this.pageSize){
43177             p.start = 0;
43178             p.limit = this.pageSize;
43179         }
43180         return p;
43181     },
43182
43183     /**
43184      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
43185      */
43186     collapse : function(){
43187         if(!this.isExpanded()){
43188             return;
43189         }
43190         this.list.hide();
43191         Roo.get(document).un('mousedown', this.collapseIf, this);
43192         Roo.get(document).un('mousewheel', this.collapseIf, this);
43193         if (!this.editable) {
43194             Roo.get(document).un('keydown', this.listKeyPress, this);
43195         }
43196         this.fireEvent('collapse', this);
43197     },
43198
43199     // private
43200     collapseIf : function(e){
43201         if(!e.within(this.wrap) && !e.within(this.list)){
43202             this.collapse();
43203         }
43204     },
43205
43206     /**
43207      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
43208      */
43209     expand : function(){
43210         if(this.isExpanded() || !this.hasFocus){
43211             return;
43212         }
43213         this.list.alignTo(this.el, this.listAlign);
43214         this.list.show();
43215         Roo.get(document).on('mousedown', this.collapseIf, this);
43216         Roo.get(document).on('mousewheel', this.collapseIf, this);
43217         if (!this.editable) {
43218             Roo.get(document).on('keydown', this.listKeyPress, this);
43219         }
43220         
43221         this.fireEvent('expand', this);
43222     },
43223
43224     // private
43225     // Implements the default empty TriggerField.onTriggerClick function
43226     onTriggerClick : function(){
43227         if(this.disabled){
43228             return;
43229         }
43230         if(this.isExpanded()){
43231             this.collapse();
43232             if (!this.blockFocus) {
43233                 this.el.focus();
43234             }
43235             
43236         }else {
43237             this.hasFocus = true;
43238             if(this.triggerAction == 'all') {
43239                 this.doQuery(this.allQuery, true);
43240             } else {
43241                 this.doQuery(this.getRawValue());
43242             }
43243             if (!this.blockFocus) {
43244                 this.el.focus();
43245             }
43246         }
43247     },
43248     listKeyPress : function(e)
43249     {
43250         //Roo.log('listkeypress');
43251         // scroll to first matching element based on key pres..
43252         if (e.isSpecialKey()) {
43253             return false;
43254         }
43255         var k = String.fromCharCode(e.getKey()).toUpperCase();
43256         //Roo.log(k);
43257         var match  = false;
43258         var csel = this.view.getSelectedNodes();
43259         var cselitem = false;
43260         if (csel.length) {
43261             var ix = this.view.indexOf(csel[0]);
43262             cselitem  = this.store.getAt(ix);
43263             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
43264                 cselitem = false;
43265             }
43266             
43267         }
43268         
43269         this.store.each(function(v) { 
43270             if (cselitem) {
43271                 // start at existing selection.
43272                 if (cselitem.id == v.id) {
43273                     cselitem = false;
43274                 }
43275                 return;
43276             }
43277                 
43278             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
43279                 match = this.store.indexOf(v);
43280                 return false;
43281             }
43282         }, this);
43283         
43284         if (match === false) {
43285             return true; // no more action?
43286         }
43287         // scroll to?
43288         this.view.select(match);
43289         var sn = Roo.get(this.view.getSelectedNodes()[0]);
43290         sn.scrollIntoView(sn.dom.parentNode, false);
43291     } 
43292
43293     /** 
43294     * @cfg {Boolean} grow 
43295     * @hide 
43296     */
43297     /** 
43298     * @cfg {Number} growMin 
43299     * @hide 
43300     */
43301     /** 
43302     * @cfg {Number} growMax 
43303     * @hide 
43304     */
43305     /**
43306      * @hide
43307      * @method autoSize
43308      */
43309 });/*
43310  * Copyright(c) 2010-2012, Roo J Solutions Limited
43311  *
43312  * Licence LGPL
43313  *
43314  */
43315
43316 /**
43317  * @class Roo.form.ComboBoxArray
43318  * @extends Roo.form.TextField
43319  * A facebook style adder... for lists of email / people / countries  etc...
43320  * pick multiple items from a combo box, and shows each one.
43321  *
43322  *  Fred [x]  Brian [x]  [Pick another |v]
43323  *
43324  *
43325  *  For this to work: it needs various extra information
43326  *    - normal combo problay has
43327  *      name, hiddenName
43328  *    + displayField, valueField
43329  *
43330  *    For our purpose...
43331  *
43332  *
43333  *   If we change from 'extends' to wrapping...
43334  *   
43335  *  
43336  *
43337  
43338  
43339  * @constructor
43340  * Create a new ComboBoxArray.
43341  * @param {Object} config Configuration options
43342  */
43343  
43344
43345 Roo.form.ComboBoxArray = function(config)
43346 {
43347     this.addEvents({
43348         /**
43349          * @event beforeremove
43350          * Fires before remove the value from the list
43351              * @param {Roo.form.ComboBoxArray} _self This combo box array
43352              * @param {Roo.form.ComboBoxArray.Item} item removed item
43353              */
43354         'beforeremove' : true,
43355         /**
43356          * @event remove
43357          * Fires when remove the value from the list
43358              * @param {Roo.form.ComboBoxArray} _self This combo box array
43359              * @param {Roo.form.ComboBoxArray.Item} item removed item
43360              */
43361         'remove' : true
43362         
43363         
43364     });
43365     
43366     Roo.form.ComboBoxArray.superclass.constructor.call(this, config);
43367     
43368     this.items = new Roo.util.MixedCollection(false);
43369     
43370     // construct the child combo...
43371     
43372     
43373     
43374     
43375    
43376     
43377 }
43378
43379  
43380 Roo.extend(Roo.form.ComboBoxArray, Roo.form.TextField,
43381
43382     /**
43383      * @cfg {Roo.form.ComboBox} combo [required] The combo box that is wrapped
43384      */
43385     
43386     lastData : false,
43387     
43388     // behavies liek a hiddne field
43389     inputType:      'hidden',
43390     /**
43391      * @cfg {Number} width The width of the box that displays the selected element
43392      */ 
43393     width:          300,
43394
43395     
43396     
43397     /**
43398      * @cfg {String} name    The name of the visable items on this form (eg. titles not ids)
43399      */
43400     name : false,
43401     /**
43402      * @cfg {String} hiddenName    The hidden name of the field, often contains an comma seperated list of names
43403      */
43404     hiddenName : false,
43405       /**
43406      * @cfg {String} seperator    The value seperator normally ',' 
43407      */
43408     seperator : ',',
43409     
43410     // private the array of items that are displayed..
43411     items  : false,
43412     // private - the hidden field el.
43413     hiddenEl : false,
43414     // private - the filed el..
43415     el : false,
43416     
43417     //validateValue : function() { return true; }, // all values are ok!
43418     //onAddClick: function() { },
43419     
43420     onRender : function(ct, position) 
43421     {
43422         
43423         // create the standard hidden element
43424         //Roo.form.ComboBoxArray.superclass.onRender.call(this, ct, position);
43425         
43426         
43427         // give fake names to child combo;
43428         this.combo.hiddenName = this.hiddenName ? (this.hiddenName+'-subcombo') : this.hiddenName;
43429         this.combo.name = this.name ? (this.name+'-subcombo') : this.name;
43430         
43431         this.combo = Roo.factory(this.combo, Roo.form);
43432         this.combo.onRender(ct, position);
43433         if (typeof(this.combo.width) != 'undefined') {
43434             this.combo.onResize(this.combo.width,0);
43435         }
43436         
43437         this.combo.initEvents();
43438         
43439         // assigned so form know we need to do this..
43440         this.store          = this.combo.store;
43441         this.valueField     = this.combo.valueField;
43442         this.displayField   = this.combo.displayField ;
43443         
43444         
43445         this.combo.wrap.addClass('x-cbarray-grp');
43446         
43447         var cbwrap = this.combo.wrap.createChild(
43448             {tag: 'div', cls: 'x-cbarray-cb'},
43449             this.combo.el.dom
43450         );
43451         
43452              
43453         this.hiddenEl = this.combo.wrap.createChild({
43454             tag: 'input',  type:'hidden' , name: this.hiddenName, value : ''
43455         });
43456         this.el = this.combo.wrap.createChild({
43457             tag: 'input',  type:'hidden' , name: this.name, value : ''
43458         });
43459          //   this.el.dom.removeAttribute("name");
43460         
43461         
43462         this.outerWrap = this.combo.wrap;
43463         this.wrap = cbwrap;
43464         
43465         this.outerWrap.setWidth(this.width);
43466         this.outerWrap.dom.removeChild(this.el.dom);
43467         
43468         this.wrap.dom.appendChild(this.el.dom);
43469         this.outerWrap.dom.removeChild(this.combo.trigger.dom);
43470         this.combo.wrap.dom.appendChild(this.combo.trigger.dom);
43471         
43472         this.combo.trigger.setStyle('position','relative');
43473         this.combo.trigger.setStyle('left', '0px');
43474         this.combo.trigger.setStyle('top', '2px');
43475         
43476         this.combo.el.setStyle('vertical-align', 'text-bottom');
43477         
43478         //this.trigger.setStyle('vertical-align', 'top');
43479         
43480         // this should use the code from combo really... on('add' ....)
43481         if (this.adder) {
43482             
43483         
43484             this.adder = this.outerWrap.createChild(
43485                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-adder', style: 'margin-left:2px'});  
43486             var _t = this;
43487             this.adder.on('click', function(e) {
43488                 _t.fireEvent('adderclick', this, e);
43489             }, _t);
43490         }
43491         //var _t = this;
43492         //this.adder.on('click', this.onAddClick, _t);
43493         
43494         
43495         this.combo.on('select', function(cb, rec, ix) {
43496             this.addItem(rec.data);
43497             
43498             cb.setValue('');
43499             cb.el.dom.value = '';
43500             //cb.lastData = rec.data;
43501             // add to list
43502             
43503         }, this);
43504         
43505         
43506     },
43507     
43508     
43509     getName: function()
43510     {
43511         // returns hidden if it's set..
43512         if (!this.rendered) {return ''};
43513         return  this.hiddenName ? this.hiddenName : this.name;
43514         
43515     },
43516     
43517     
43518     onResize: function(w, h){
43519         
43520         return;
43521         // not sure if this is needed..
43522         //this.combo.onResize(w,h);
43523         
43524         if(typeof w != 'number'){
43525             // we do not handle it!?!?
43526             return;
43527         }
43528         var tw = this.combo.trigger.getWidth();
43529         tw += this.addicon ? this.addicon.getWidth() : 0;
43530         tw += this.editicon ? this.editicon.getWidth() : 0;
43531         var x = w - tw;
43532         this.combo.el.setWidth( this.combo.adjustWidth('input', x));
43533             
43534         this.combo.trigger.setStyle('left', '0px');
43535         
43536         if(this.list && this.listWidth === undefined){
43537             var lw = Math.max(x + this.combo.trigger.getWidth(), this.combo.minListWidth);
43538             this.list.setWidth(lw);
43539             this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
43540         }
43541         
43542     
43543         
43544     },
43545     
43546     addItem: function(rec)
43547     {
43548         var valueField = this.combo.valueField;
43549         var displayField = this.combo.displayField;
43550         
43551         if (this.items.indexOfKey(rec[valueField]) > -1) {
43552             //console.log("GOT " + rec.data.id);
43553             return;
43554         }
43555         
43556         var x = new Roo.form.ComboBoxArray.Item({
43557             //id : rec[this.idField],
43558             data : rec,
43559             displayField : displayField ,
43560             tipField : displayField ,
43561             cb : this
43562         });
43563         // use the 
43564         this.items.add(rec[valueField],x);
43565         // add it before the element..
43566         this.updateHiddenEl();
43567         x.render(this.outerWrap, this.wrap.dom);
43568         // add the image handler..
43569     },
43570     
43571     updateHiddenEl : function()
43572     {
43573         this.validate();
43574         if (!this.hiddenEl) {
43575             return;
43576         }
43577         var ar = [];
43578         var idField = this.combo.valueField;
43579         
43580         this.items.each(function(f) {
43581             ar.push(f.data[idField]);
43582         });
43583         this.hiddenEl.dom.value = ar.join(this.seperator);
43584         this.validate();
43585     },
43586     
43587     reset : function()
43588     {
43589         this.items.clear();
43590         
43591         Roo.each(this.outerWrap.select('.x-cbarray-item', true).elements, function(el){
43592            el.remove();
43593         });
43594         
43595         this.el.dom.value = '';
43596         if (this.hiddenEl) {
43597             this.hiddenEl.dom.value = '';
43598         }
43599         
43600     },
43601     getValue: function()
43602     {
43603         return this.hiddenEl ? this.hiddenEl.dom.value : '';
43604     },
43605     setValue: function(v) // not a valid action - must use addItems..
43606     {
43607         
43608         this.reset();
43609          
43610         if (this.store.isLocal && (typeof(v) == 'string')) {
43611             // then we can use the store to find the values..
43612             // comma seperated at present.. this needs to allow JSON based encoding..
43613             this.hiddenEl.value  = v;
43614             var v_ar = [];
43615             Roo.each(v.split(this.seperator), function(k) {
43616                 Roo.log("CHECK " + this.valueField + ',' + k);
43617                 var li = this.store.query(this.valueField, k);
43618                 if (!li.length) {
43619                     return;
43620                 }
43621                 var add = {};
43622                 add[this.valueField] = k;
43623                 add[this.displayField] = li.item(0).data[this.displayField];
43624                 
43625                 this.addItem(add);
43626             }, this) 
43627              
43628         }
43629         if (typeof(v) == 'object' ) {
43630             // then let's assume it's an array of objects..
43631             Roo.each(v, function(l) {
43632                 var add = l;
43633                 if (typeof(l) == 'string') {
43634                     add = {};
43635                     add[this.valueField] = l;
43636                     add[this.displayField] = l
43637                 }
43638                 this.addItem(add);
43639             }, this);
43640              
43641         }
43642         
43643         
43644     },
43645     setFromData: function(v)
43646     {
43647         // this recieves an object, if setValues is called.
43648         this.reset();
43649         this.el.dom.value = v[this.displayField];
43650         this.hiddenEl.dom.value = v[this.valueField];
43651         if (typeof(v[this.valueField]) != 'string' || !v[this.valueField].length) {
43652             return;
43653         }
43654         var kv = v[this.valueField];
43655         var dv = v[this.displayField];
43656         kv = typeof(kv) != 'string' ? '' : kv;
43657         dv = typeof(dv) != 'string' ? '' : dv;
43658         
43659         
43660         var keys = kv.split(this.seperator);
43661         var display = dv.split(this.seperator);
43662         for (var i = 0 ; i < keys.length; i++) {
43663             add = {};
43664             add[this.valueField] = keys[i];
43665             add[this.displayField] = display[i];
43666             this.addItem(add);
43667         }
43668       
43669         
43670     },
43671     
43672     /**
43673      * Validates the combox array value
43674      * @return {Boolean} True if the value is valid, else false
43675      */
43676     validate : function(){
43677         if(this.disabled || this.validateValue(this.processValue(this.getValue()))){
43678             this.clearInvalid();
43679             return true;
43680         }
43681         return false;
43682     },
43683     
43684     validateValue : function(value){
43685         return Roo.form.ComboBoxArray.superclass.validateValue.call(this, this.getValue());
43686         
43687     },
43688     
43689     /*@
43690      * overide
43691      * 
43692      */
43693     isDirty : function() {
43694         if(this.disabled) {
43695             return false;
43696         }
43697         
43698         try {
43699             var d = Roo.decode(String(this.originalValue));
43700         } catch (e) {
43701             return String(this.getValue()) !== String(this.originalValue);
43702         }
43703         
43704         var originalValue = [];
43705         
43706         for (var i = 0; i < d.length; i++){
43707             originalValue.push(d[i][this.valueField]);
43708         }
43709         
43710         return String(this.getValue()) !== String(originalValue.join(this.seperator));
43711         
43712     }
43713     
43714 });
43715
43716
43717
43718 /**
43719  * @class Roo.form.ComboBoxArray.Item
43720  * @extends Roo.BoxComponent
43721  * A selected item in the list
43722  *  Fred [x]  Brian [x]  [Pick another |v]
43723  * 
43724  * @constructor
43725  * Create a new item.
43726  * @param {Object} config Configuration options
43727  */
43728  
43729 Roo.form.ComboBoxArray.Item = function(config) {
43730     config.id = Roo.id();
43731     Roo.form.ComboBoxArray.Item.superclass.constructor.call(this, config);
43732 }
43733
43734 Roo.extend(Roo.form.ComboBoxArray.Item, Roo.BoxComponent, {
43735     data : {},
43736     cb: false,
43737     displayField : false,
43738     tipField : false,
43739     
43740     
43741     defaultAutoCreate : {
43742         tag: 'div',
43743         cls: 'x-cbarray-item',
43744         cn : [ 
43745             { tag: 'div' },
43746             {
43747                 tag: 'img',
43748                 width:16,
43749                 height : 16,
43750                 src : Roo.BLANK_IMAGE_URL ,
43751                 align: 'center'
43752             }
43753         ]
43754         
43755     },
43756     
43757  
43758     onRender : function(ct, position)
43759     {
43760         Roo.form.Field.superclass.onRender.call(this, ct, position);
43761         
43762         if(!this.el){
43763             var cfg = this.getAutoCreate();
43764             this.el = ct.createChild(cfg, position);
43765         }
43766         
43767         this.el.child('img').dom.setAttribute('src', Roo.BLANK_IMAGE_URL);
43768         
43769         this.el.child('div').dom.innerHTML = this.cb.renderer ? 
43770             this.cb.renderer(this.data) :
43771             String.format('{0}',this.data[this.displayField]);
43772         
43773             
43774         this.el.child('div').dom.setAttribute('qtip',
43775                         String.format('{0}',this.data[this.tipField])
43776         );
43777         
43778         this.el.child('img').on('click', this.remove, this);
43779         
43780     },
43781    
43782     remove : function()
43783     {
43784         if(this.cb.disabled){
43785             return;
43786         }
43787         
43788         if(false !== this.cb.fireEvent('beforeremove', this.cb, this)){
43789             this.cb.items.remove(this);
43790             this.el.child('img').un('click', this.remove, this);
43791             this.el.remove();
43792             this.cb.updateHiddenEl();
43793
43794             this.cb.fireEvent('remove', this.cb, this);
43795         }
43796         
43797     }
43798 });/*
43799  * RooJS Library 1.1.1
43800  * Copyright(c) 2008-2011  Alan Knowles
43801  *
43802  * License - LGPL
43803  */
43804  
43805
43806 /**
43807  * @class Roo.form.ComboNested
43808  * @extends Roo.form.ComboBox
43809  * A combobox for that allows selection of nested items in a list,
43810  * eg.
43811  *
43812  *  Book
43813  *    -> red
43814  *    -> green
43815  *  Table
43816  *    -> square
43817  *      ->red
43818  *      ->green
43819  *    -> rectangle
43820  *      ->green
43821  *      
43822  * 
43823  * @constructor
43824  * Create a new ComboNested
43825  * @param {Object} config Configuration options
43826  */
43827 Roo.form.ComboNested = function(config){
43828     Roo.form.ComboCheck.superclass.constructor.call(this, config);
43829     // should verify some data...
43830     // like
43831     // hiddenName = required..
43832     // displayField = required
43833     // valudField == required
43834     var req= [ 'hiddenName', 'displayField', 'valueField' ];
43835     var _t = this;
43836     Roo.each(req, function(e) {
43837         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
43838             throw "Roo.form.ComboNested : missing value for: " + e;
43839         }
43840     });
43841      
43842     
43843 };
43844
43845 Roo.extend(Roo.form.ComboNested, Roo.form.ComboBox, {
43846    
43847     /*
43848      * @config {Number} max Number of columns to show
43849      */
43850     
43851     maxColumns : 3,
43852    
43853     list : null, // the outermost div..
43854     innerLists : null, // the
43855     views : null,
43856     stores : null,
43857     // private
43858     loadingChildren : false,
43859     
43860     onRender : function(ct, position)
43861     {
43862         Roo.form.ComboBox.superclass.onRender.call(this, ct, position); // skip parent call - got to above..
43863         
43864         if(this.hiddenName){
43865             this.hiddenField = this.el.insertSibling({tag:'input', type:'hidden', name: this.hiddenName, id:  (this.hiddenId||this.hiddenName)},
43866                     'before', true);
43867             this.hiddenField.value =
43868                 this.hiddenValue !== undefined ? this.hiddenValue :
43869                 this.value !== undefined ? this.value : '';
43870
43871             // prevent input submission
43872             this.el.dom.removeAttribute('name');
43873              
43874              
43875         }
43876         
43877         if(Roo.isGecko){
43878             this.el.dom.setAttribute('autocomplete', 'off');
43879         }
43880
43881         var cls = 'x-combo-list';
43882
43883         this.list = new Roo.Layer({
43884             shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
43885         });
43886
43887         var lw = this.listWidth || Math.max(this.wrap.getWidth(), this.minListWidth);
43888         this.list.setWidth(lw);
43889         this.list.swallowEvent('mousewheel');
43890         this.assetHeight = 0;
43891
43892         if(this.title){
43893             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
43894             this.assetHeight += this.header.getHeight();
43895         }
43896         this.innerLists = [];
43897         this.views = [];
43898         this.stores = [];
43899         for (var i =0 ; i < this.maxColumns; i++) {
43900             this.onRenderList( cls, i);
43901         }
43902         
43903         // always needs footer, as we are going to have an 'OK' button.
43904         this.footer = this.list.createChild({cls:cls+'-ft'});
43905         this.pageTb = new Roo.Toolbar(this.footer);  
43906         var _this = this;
43907         this.pageTb.add(  {
43908             
43909             text: 'Done',
43910             handler: function()
43911             {
43912                 _this.collapse();
43913             }
43914         });
43915         
43916         if ( this.allowBlank && !this.disableClear) {
43917             
43918             this.pageTb.add(new Roo.Toolbar.Fill(), {
43919                 cls: 'x-btn-icon x-btn-clear',
43920                 text: '&#160;',
43921                 handler: function()
43922                 {
43923                     _this.collapse();
43924                     _this.clearValue();
43925                     _this.onSelect(false, -1);
43926                 }
43927             });
43928         }
43929         if (this.footer) {
43930             this.assetHeight += this.footer.getHeight();
43931         }
43932         
43933     },
43934     onRenderList : function (  cls, i)
43935     {
43936         
43937         var lw = Math.floor(
43938                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
43939         );
43940         
43941         this.list.setWidth(lw); // default to '1'
43942
43943         var il = this.innerLists[i] = this.list.createChild({cls:cls+'-inner'});
43944         //il.on('mouseover', this.onViewOver, this, { list:  i });
43945         //il.on('mousemove', this.onViewMove, this, { list:  i });
43946         il.setWidth(lw);
43947         il.setStyle({ 'overflow-x' : 'hidden'});
43948
43949         if(!this.tpl){
43950             this.tpl = new Roo.Template({
43951                 html :  '<div class="'+cls+'-item '+cls+'-item-{cn:this.isEmpty}">{' + this.displayField + '}</div>',
43952                 isEmpty: function (value, allValues) {
43953                     //Roo.log(value);
43954                     var dl = typeof(value.data) != 'undefined' ? value.data.length : value.length; ///json is a nested response..
43955                     return dl ? 'has-children' : 'no-children'
43956                 }
43957             });
43958         }
43959         
43960         var store  = this.store;
43961         if (i > 0) {
43962             store  = new Roo.data.SimpleStore({
43963                 //fields : this.store.reader.meta.fields,
43964                 reader : this.store.reader,
43965                 data : [ ]
43966             });
43967         }
43968         this.stores[i]  = store;
43969                   
43970         var view = this.views[i] = new Roo.View(
43971             il,
43972             this.tpl,
43973             {
43974                 singleSelect:true,
43975                 store: store,
43976                 selectedClass: this.selectedClass
43977             }
43978         );
43979         view.getEl().setWidth(lw);
43980         view.getEl().setStyle({
43981             position: i < 1 ? 'relative' : 'absolute',
43982             top: 0,
43983             left: (i * lw ) + 'px',
43984             display : i > 0 ? 'none' : 'block'
43985         });
43986         view.on('selectionchange', this.onSelectChange.createDelegate(this, {list : i }, true));
43987         view.on('dblclick', this.onDoubleClick.createDelegate(this, {list : i }, true));
43988         //view.on('click', this.onViewClick, this, { list : i });
43989
43990         store.on('beforeload', this.onBeforeLoad, this);
43991         store.on('load',  this.onLoad, this, { list  : i});
43992         store.on('loadexception', this.onLoadException, this);
43993
43994         // hide the other vies..
43995         
43996         
43997         
43998     },
43999       
44000     restrictHeight : function()
44001     {
44002         var mh = 0;
44003         Roo.each(this.innerLists, function(il,i) {
44004             var el = this.views[i].getEl();
44005             el.dom.style.height = '';
44006             var inner = el.dom;
44007             var h = Math.max(il.clientHeight, il.offsetHeight, il.scrollHeight);
44008             // only adjust heights on other ones..
44009             mh = Math.max(h, mh);
44010             if (i < 1) {
44011                 
44012                 el.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44013                 il.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
44014                
44015             }
44016             
44017             
44018         }, this);
44019         
44020         this.list.beginUpdate();
44021         this.list.setHeight(mh+this.list.getFrameWidth('tb')+this.assetHeight);
44022         this.list.alignTo(this.el, this.listAlign);
44023         this.list.endUpdate();
44024         
44025     },
44026      
44027     
44028     // -- store handlers..
44029     // private
44030     onBeforeLoad : function()
44031     {
44032         if(!this.hasFocus){
44033             return;
44034         }
44035         this.innerLists[0].update(this.loadingText ?
44036                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
44037         this.restrictHeight();
44038         this.selectedIndex = -1;
44039     },
44040     // private
44041     onLoad : function(a,b,c,d)
44042     {
44043         if (!this.loadingChildren) {
44044             // then we are loading the top level. - hide the children
44045             for (var i = 1;i < this.views.length; i++) {
44046                 this.views[i].getEl().setStyle({ display : 'none' });
44047             }
44048             var lw = Math.floor(
44049                 ((this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')) / this.maxColumns
44050             );
44051         
44052              this.list.setWidth(lw); // default to '1'
44053
44054             
44055         }
44056         if(!this.hasFocus){
44057             return;
44058         }
44059         
44060         if(this.store.getCount() > 0) {
44061             this.expand();
44062             this.restrictHeight();   
44063         } else {
44064             this.onEmptyResults();
44065         }
44066         
44067         if (!this.loadingChildren) {
44068             this.selectActive();
44069         }
44070         /*
44071         this.stores[1].loadData([]);
44072         this.stores[2].loadData([]);
44073         this.views
44074         */    
44075     
44076         //this.el.focus();
44077     },
44078     
44079     
44080     // private
44081     onLoadException : function()
44082     {
44083         this.collapse();
44084         Roo.log(this.store.reader.jsonData);
44085         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
44086             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
44087         }
44088         
44089         
44090     },
44091     // no cleaning of leading spaces on blur here.
44092     cleanLeadingSpace : function(e) { },
44093     
44094
44095     onSelectChange : function (view, sels, opts )
44096     {
44097         var ix = view.getSelectedIndexes();
44098          
44099         if (opts.list > this.maxColumns - 2) {
44100             if (view.store.getCount()<  1) {
44101                 this.views[opts.list ].getEl().setStyle({ display :   'none' });
44102
44103             } else  {
44104                 if (ix.length) {
44105                     // used to clear ?? but if we are loading unselected 
44106                     this.setFromData(view.store.getAt(ix[0]).data);
44107                 }
44108                 
44109             }
44110             
44111             return;
44112         }
44113         
44114         if (!ix.length) {
44115             // this get's fired when trigger opens..
44116            // this.setFromData({});
44117             var str = this.stores[opts.list+1];
44118             str.data.clear(); // removeall wihtout the fire events..
44119             return;
44120         }
44121         
44122         var rec = view.store.getAt(ix[0]);
44123          
44124         this.setFromData(rec.data);
44125         this.fireEvent('select', this, rec, ix[0]);
44126         
44127         var lw = Math.floor(
44128              (
44129                 (this.listWidth * this.maxColumns || Math.max(this.wrap.getWidth(), this.minListWidth)) - this.list.getFrameWidth('lr')
44130              ) / this.maxColumns
44131         );
44132         this.loadingChildren = true;
44133         this.stores[opts.list+1].loadDataFromChildren( rec );
44134         this.loadingChildren = false;
44135         var dl = this.stores[opts.list+1]. getTotalCount();
44136         
44137         this.views[opts.list+1].getEl().setHeight( this.innerLists[0].getHeight());
44138         
44139         this.views[opts.list+1].getEl().setStyle({ display : dl ? 'block' : 'none' });
44140         for (var i = opts.list+2; i < this.views.length;i++) {
44141             this.views[i].getEl().setStyle({ display : 'none' });
44142         }
44143         
44144         this.innerLists[opts.list+1].setHeight( this.innerLists[0].getHeight());
44145         this.list.setWidth(lw * (opts.list + (dl ? 2 : 1)));
44146         
44147         if (this.isLoading) {
44148            // this.selectActive(opts.list);
44149         }
44150          
44151     },
44152     
44153     
44154     
44155     
44156     onDoubleClick : function()
44157     {
44158         this.collapse(); //??
44159     },
44160     
44161      
44162     
44163     
44164     
44165     // private
44166     recordToStack : function(store, prop, value, stack)
44167     {
44168         var cstore = new Roo.data.SimpleStore({
44169             //fields : this.store.reader.meta.fields, // we need array reader.. for
44170             reader : this.store.reader,
44171             data : [ ]
44172         });
44173         var _this = this;
44174         var record  = false;
44175         var srec = false;
44176         if(store.getCount() < 1){
44177             return false;
44178         }
44179         store.each(function(r){
44180             if(r.data[prop] == value){
44181                 record = r;
44182             srec = r;
44183                 return false;
44184             }
44185             if (r.data.cn && r.data.cn.length) {
44186                 cstore.loadDataFromChildren( r);
44187                 var cret = _this.recordToStack(cstore, prop, value, stack);
44188                 if (cret !== false) {
44189                     record = cret;
44190                     srec = r;
44191                     return false;
44192                 }
44193             }
44194              
44195             return true;
44196         });
44197         if (record == false) {
44198             return false
44199         }
44200         stack.unshift(srec);
44201         return record;
44202     },
44203     
44204     /*
44205      * find the stack of stores that match our value.
44206      *
44207      * 
44208      */
44209     
44210     selectActive : function ()
44211     {
44212         // if store is not loaded, then we will need to wait for that to happen first.
44213         var stack = [];
44214         this.recordToStack(this.store, this.valueField, this.getValue(), stack);
44215         for (var i = 0; i < stack.length; i++ ) {
44216             this.views[i].select(stack[i].store.indexOf(stack[i]), false, false );
44217         }
44218         
44219     }
44220         
44221          
44222     
44223     
44224     
44225     
44226 });/*
44227  * Based on:
44228  * Ext JS Library 1.1.1
44229  * Copyright(c) 2006-2007, Ext JS, LLC.
44230  *
44231  * Originally Released Under LGPL - original licence link has changed is not relivant.
44232  *
44233  * Fork - LGPL
44234  * <script type="text/javascript">
44235  */
44236 /**
44237  * @class Roo.form.Checkbox
44238  * @extends Roo.form.Field
44239  * Single checkbox field.  Can be used as a direct replacement for traditional checkbox fields.
44240  * @constructor
44241  * Creates a new Checkbox
44242  * @param {Object} config Configuration options
44243  */
44244 Roo.form.Checkbox = function(config){
44245     Roo.form.Checkbox.superclass.constructor.call(this, config);
44246     this.addEvents({
44247         /**
44248          * @event check
44249          * Fires when the checkbox is checked or unchecked.
44250              * @param {Roo.form.Checkbox} this This checkbox
44251              * @param {Boolean} checked The new checked value
44252              */
44253         check : true
44254     });
44255 };
44256
44257 Roo.extend(Roo.form.Checkbox, Roo.form.Field,  {
44258     /**
44259      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
44260      */
44261     focusClass : undefined,
44262     /**
44263      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
44264      */
44265     fieldClass: "x-form-field",
44266     /**
44267      * @cfg {Boolean} checked True if the the checkbox should render already checked (defaults to false)
44268      */
44269     checked: false,
44270     /**
44271      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
44272      * {tag: "input", type: "checkbox", autocomplete: "off"})
44273      */
44274     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "off"},
44275     /**
44276      * @cfg {String} boxLabel The text that appears beside the checkbox
44277      */
44278     boxLabel : "",
44279     /**
44280      * @cfg {String} inputValue The value that should go into the generated input element's value attribute
44281      */  
44282     inputValue : '1',
44283     /**
44284      * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
44285      */
44286      valueOff: '0', // value when not checked..
44287
44288     actionMode : 'viewEl', 
44289     //
44290     // private
44291     itemCls : 'x-menu-check-item x-form-item',
44292     groupClass : 'x-menu-group-item',
44293     inputType : 'hidden',
44294     
44295     
44296     inSetChecked: false, // check that we are not calling self...
44297     
44298     inputElement: false, // real input element?
44299     basedOn: false, // ????
44300     
44301     isFormField: true, // not sure where this is needed!!!!
44302
44303     onResize : function(){
44304         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
44305         if(!this.boxLabel){
44306             this.el.alignTo(this.wrap, 'c-c');
44307         }
44308     },
44309
44310     initEvents : function(){
44311         Roo.form.Checkbox.superclass.initEvents.call(this);
44312         this.el.on("click", this.onClick,  this);
44313         this.el.on("change", this.onClick,  this);
44314     },
44315
44316
44317     getResizeEl : function(){
44318         return this.wrap;
44319     },
44320
44321     getPositionEl : function(){
44322         return this.wrap;
44323     },
44324
44325     // private
44326     onRender : function(ct, position){
44327         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44328         /*
44329         if(this.inputValue !== undefined){
44330             this.el.dom.value = this.inputValue;
44331         }
44332         */
44333         //this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44334         this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44335         var viewEl = this.wrap.createChild({ 
44336             tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44337         this.viewEl = viewEl;   
44338         this.wrap.on('click', this.onClick,  this); 
44339         
44340         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44341         this.el.on('propertychange', this.setFromHidden,  this);  //ie
44342         
44343         
44344         
44345         if(this.boxLabel){
44346             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44347         //    viewEl.on('click', this.onClick,  this); 
44348         }
44349         //if(this.checked){
44350             this.setChecked(this.checked);
44351         //}else{
44352             //this.checked = this.el.dom;
44353         //}
44354
44355     },
44356
44357     // private
44358     initValue : Roo.emptyFn,
44359
44360     /**
44361      * Returns the checked state of the checkbox.
44362      * @return {Boolean} True if checked, else false
44363      */
44364     getValue : function(){
44365         if(this.el){
44366             return String(this.el.dom.value) == String(this.inputValue ) ? this.inputValue : this.valueOff;
44367         }
44368         return this.valueOff;
44369         
44370     },
44371
44372         // private
44373     onClick : function(){ 
44374         if (this.disabled) {
44375             return;
44376         }
44377         this.setChecked(!this.checked);
44378
44379         //if(this.el.dom.checked != this.checked){
44380         //    this.setValue(this.el.dom.checked);
44381        // }
44382     },
44383
44384     /**
44385      * Sets the checked state of the checkbox.
44386      * On is always based on a string comparison between inputValue and the param.
44387      * @param {Boolean/String} value - the value to set 
44388      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
44389      */
44390     setValue : function(v,suppressEvent){
44391         
44392         
44393         //this.checked = (v === true || v === 'true' || v == '1' || String(v).toLowerCase() == 'on');
44394         //if(this.el && this.el.dom){
44395         //    this.el.dom.checked = this.checked;
44396         //    this.el.dom.defaultChecked = this.checked;
44397         //}
44398         this.setChecked(String(v) === String(this.inputValue), suppressEvent);
44399         //this.fireEvent("check", this, this.checked);
44400     },
44401     // private..
44402     setChecked : function(state,suppressEvent)
44403     {
44404         if (this.inSetChecked) {
44405             this.checked = state;
44406             return;
44407         }
44408         
44409     
44410         if(this.wrap){
44411             this.wrap[state ? 'addClass' : 'removeClass']('x-menu-item-checked');
44412         }
44413         this.checked = state;
44414         if(suppressEvent !== true){
44415             this.fireEvent('check', this, state);
44416         }
44417         this.inSetChecked = true;
44418         this.el.dom.value = state ? this.inputValue : this.valueOff;
44419         this.inSetChecked = false;
44420         
44421     },
44422     // handle setting of hidden value by some other method!!?!?
44423     setFromHidden: function()
44424     {
44425         if(!this.el){
44426             return;
44427         }
44428         //console.log("SET FROM HIDDEN");
44429         //alert('setFrom hidden');
44430         this.setValue(this.el.dom.value);
44431     },
44432     
44433     onDestroy : function()
44434     {
44435         if(this.viewEl){
44436             Roo.get(this.viewEl).remove();
44437         }
44438          
44439         Roo.form.Checkbox.superclass.onDestroy.call(this);
44440     },
44441     
44442     setBoxLabel : function(str)
44443     {
44444         this.wrap.select('.x-form-cb-label', true).first().dom.innerHTML = str;
44445     }
44446
44447 });/*
44448  * Based on:
44449  * Ext JS Library 1.1.1
44450  * Copyright(c) 2006-2007, Ext JS, LLC.
44451  *
44452  * Originally Released Under LGPL - original licence link has changed is not relivant.
44453  *
44454  * Fork - LGPL
44455  * <script type="text/javascript">
44456  */
44457  
44458 /**
44459  * @class Roo.form.Radio
44460  * @extends Roo.form.Checkbox
44461  * Single radio field.  Same as Checkbox, but provided as a convenience for automatically setting the input type.
44462  * Radio grouping is handled automatically by the browser if you give each radio in a group the same name.
44463  * @constructor
44464  * Creates a new Radio
44465  * @param {Object} config Configuration options
44466  */
44467 Roo.form.Radio = function(){
44468     Roo.form.Radio.superclass.constructor.apply(this, arguments);
44469 };
44470 Roo.extend(Roo.form.Radio, Roo.form.Checkbox, {
44471     inputType: 'radio',
44472
44473     /**
44474      * If this radio is part of a group, it will return the selected value
44475      * @return {String}
44476      */
44477     getGroupValue : function(){
44478         return this.el.up('form').child('input[name='+this.el.dom.name+']:checked', true).value;
44479     },
44480     
44481     
44482     onRender : function(ct, position){
44483         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
44484         
44485         if(this.inputValue !== undefined){
44486             this.el.dom.value = this.inputValue;
44487         }
44488          
44489         this.wrap = this.el.wrap({cls: "x-form-check-wrap"});
44490         //this.wrap = this.el.wrap({cls: 'x-menu-check-item '});
44491         //var viewEl = this.wrap.createChild({ 
44492         //    tag: 'img', cls: 'x-menu-item-icon', style: 'margin: 0px;' ,src : Roo.BLANK_IMAGE_URL });
44493         //this.viewEl = viewEl;   
44494         //this.wrap.on('click', this.onClick,  this); 
44495         
44496         //this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
44497         //this.el.on('propertychange', this.setFromHidden,  this);  //ie
44498         
44499         
44500         
44501         if(this.boxLabel){
44502             this.wrap.createChild({tag: 'label', htmlFor: this.el.id, cls: 'x-form-cb-label', html: this.boxLabel});
44503         //    viewEl.on('click', this.onClick,  this); 
44504         }
44505          if(this.checked){
44506             this.el.dom.checked =   'checked' ;
44507         }
44508          
44509     } 
44510     
44511     
44512 });Roo.htmleditor = {}; 
44513 /**
44514  * @class Roo.htmleditor.Filter
44515  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
44516  * @cfg {DomElement} node The node to iterate and filter
44517  * @cfg {boolean|String|Array} tag Tags to replace 
44518  * @constructor
44519  * Create a new Filter.
44520  * @param {Object} config Configuration options
44521  */
44522
44523
44524
44525 Roo.htmleditor.Filter = function(cfg) {
44526     Roo.apply(this.cfg);
44527     // this does not actually call walk as it's really just a abstract class
44528 }
44529
44530
44531 Roo.htmleditor.Filter.prototype = {
44532     
44533     node: false,
44534     
44535     tag: false,
44536
44537     // overrride to do replace comments.
44538     replaceComment : false,
44539     
44540     // overrride to do replace or do stuff with tags..
44541     replaceTag : false,
44542     
44543     walk : function(dom)
44544     {
44545         Roo.each( Array.from(dom.childNodes), function( e ) {
44546             switch(true) {
44547                 
44548                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
44549                     this.replaceComment(e);
44550                     return;
44551                 
44552                 case e.nodeType != 1: //not a node.
44553                     return;
44554                 
44555                 case this.tag === true: // everything
44556                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
44557                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
44558                     if (this.replaceTag && false === this.replaceTag(e)) {
44559                         return;
44560                     }
44561                     if (e.hasChildNodes()) {
44562                         this.walk(e);
44563                     }
44564                     return;
44565                 
44566                 default:    // tags .. that do not match.
44567                     if (e.hasChildNodes()) {
44568                         this.walk(e);
44569                     }
44570             }
44571             
44572         }, this);
44573         
44574     }
44575 }; 
44576
44577 /**
44578  * @class Roo.htmleditor.FilterAttributes
44579  * clean attributes and  styles including http:// etc.. in attribute
44580  * @constructor
44581 * Run a new Attribute Filter
44582 * @param {Object} config Configuration options
44583  */
44584 Roo.htmleditor.FilterAttributes = function(cfg)
44585 {
44586     Roo.apply(this, cfg);
44587     this.attrib_black = this.attrib_black || [];
44588     this.attrib_white = this.attrib_white || [];
44589
44590     this.attrib_clean = this.attrib_clean || [];
44591     this.style_white = this.style_white || [];
44592     this.style_black = this.style_black || [];
44593     this.walk(cfg.node);
44594 }
44595
44596 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
44597 {
44598     tag: true, // all tags
44599     
44600     attrib_black : false, // array
44601     attrib_clean : false,
44602     attrib_white : false,
44603
44604     style_white : false,
44605     style_black : false,
44606      
44607      
44608     replaceTag : function(node)
44609     {
44610         if (!node.attributes || !node.attributes.length) {
44611             return true;
44612         }
44613         
44614         for (var i = node.attributes.length-1; i > -1 ; i--) {
44615             var a = node.attributes[i];
44616             //console.log(a);
44617             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
44618                 node.removeAttribute(a.name);
44619                 continue;
44620             }
44621             
44622             
44623             
44624             if (a.name.toLowerCase().substr(0,2)=='on')  {
44625                 node.removeAttribute(a.name);
44626                 continue;
44627             }
44628             
44629             
44630             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
44631                 node.removeAttribute(a.name);
44632                 continue;
44633             }
44634             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
44635                 this.cleanAttr(node,a.name,a.value); // fixme..
44636                 continue;
44637             }
44638             if (a.name == 'style') {
44639                 this.cleanStyle(node,a.name,a.value);
44640                 continue;
44641             }
44642             /// clean up MS crap..
44643             // tecnically this should be a list of valid class'es..
44644             
44645             
44646             if (a.name == 'class') {
44647                 if (a.value.match(/^Mso/)) {
44648                     node.removeAttribute('class');
44649                 }
44650                 
44651                 if (a.value.match(/^body$/)) {
44652                     node.removeAttribute('class');
44653                 }
44654                 continue;
44655             }
44656             
44657             
44658             // style cleanup!?
44659             // class cleanup?
44660             
44661         }
44662         return true; // clean children
44663     },
44664         
44665     cleanAttr: function(node, n,v)
44666     {
44667         
44668         if (v.match(/^\./) || v.match(/^\//)) {
44669             return;
44670         }
44671         if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
44672             return;
44673         }
44674         if (v.match(/^#/)) {
44675             return;
44676         }
44677         if (v.match(/^\{/)) { // allow template editing.
44678             return;
44679         }
44680 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
44681         node.removeAttribute(n);
44682         
44683     },
44684     cleanStyle : function(node,  n,v)
44685     {
44686         if (v.match(/expression/)) { //XSS?? should we even bother..
44687             node.removeAttribute(n);
44688             return;
44689         }
44690         
44691         var parts = v.split(/;/);
44692         var clean = [];
44693         
44694         Roo.each(parts, function(p) {
44695             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
44696             if (!p.length) {
44697                 return true;
44698             }
44699             var l = p.split(':').shift().replace(/\s+/g,'');
44700             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
44701             
44702             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
44703                 return true;
44704             }
44705             //Roo.log()
44706             // only allow 'c whitelisted system attributes'
44707             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
44708                 return true;
44709             }
44710             
44711             
44712             clean.push(p);
44713             return true;
44714         },this);
44715         if (clean.length) { 
44716             node.setAttribute(n, clean.join(';'));
44717         } else {
44718             node.removeAttribute(n);
44719         }
44720         
44721     }
44722         
44723         
44724         
44725     
44726 });/**
44727  * @class Roo.htmleditor.FilterBlack
44728  * remove blacklisted elements.
44729  * @constructor
44730  * Run a new Blacklisted Filter
44731  * @param {Object} config Configuration options
44732  */
44733
44734 Roo.htmleditor.FilterBlack = function(cfg)
44735 {
44736     Roo.apply(this, cfg);
44737     this.walk(cfg.node);
44738 }
44739
44740 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
44741 {
44742     tag : true, // all elements.
44743    
44744     replace : function(n)
44745     {
44746         n.parentNode.removeChild(n);
44747     }
44748 });
44749 /**
44750  * @class Roo.htmleditor.FilterComment
44751  * remove comments.
44752  * @constructor
44753 * Run a new Comments Filter
44754 * @param {Object} config Configuration options
44755  */
44756 Roo.htmleditor.FilterComment = function(cfg)
44757 {
44758     this.walk(cfg.node);
44759 }
44760
44761 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
44762 {
44763   
44764     replaceComment : function(n)
44765     {
44766         n.parentNode.removeChild(n);
44767     }
44768 });/**
44769  * @class Roo.htmleditor.FilterKeepChildren
44770  * remove tags but keep children
44771  * @constructor
44772  * Run a new Keep Children Filter
44773  * @param {Object} config Configuration options
44774  */
44775
44776 Roo.htmleditor.FilterKeepChildren = function(cfg)
44777 {
44778     Roo.apply(this, cfg);
44779     if (this.tag === false) {
44780         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
44781     }
44782     this.walk(cfg.node);
44783 }
44784
44785 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
44786 {
44787     
44788   
44789     replaceTag : function(node)
44790     {
44791         // walk children...
44792         var ar = Array.from(node.childNodes);
44793         for (var i = 0; i < ar.length; i++) {
44794             node.removeChild(ar[i]);
44795             // what if we need to walk these???
44796             node.parentNode.insertBefore(ar[i], node);
44797             if (this.tag !== false) {
44798                 this.walk(ar[i]);
44799             }
44800         }
44801         node.parentNode.removeChild(node);
44802         return false; // don't walk children
44803         
44804         
44805     }
44806 });/**
44807  * @class Roo.htmleditor.FilterParagraph
44808  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
44809  * like on 'push' to remove the <p> tags and replace them with line breaks.
44810  * @constructor
44811  * Run a new Paragraph Filter
44812  * @param {Object} config Configuration options
44813  */
44814
44815 Roo.htmleditor.FilterParagraph = function(cfg)
44816 {
44817     // no need to apply config.
44818     this.walk(cfg.node);
44819 }
44820
44821 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
44822 {
44823     
44824      
44825     tag : 'P',
44826     
44827      
44828     replaceTag : function(node)
44829     {
44830         
44831         if (node.childNodes.length == 1 &&
44832             node.childNodes[0].nodeType == 3 &&
44833             node.childNodes[0].textContent.trim().length < 1
44834             ) {
44835             // remove and replace with '<BR>';
44836             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
44837             return false; // no need to walk..
44838         }
44839         var ar = Array.from(node.childNodes);
44840         for (var i = 0; i < ar.length; i++) {
44841             node.removeChild(ar[i]);
44842             // what if we need to walk these???
44843             node.parentNode.insertBefore(ar[i], node);
44844         }
44845         // now what about this?
44846         // <p> &nbsp; </p>
44847         
44848         // double BR.
44849         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
44850         node.parentNode.removeChild(node);
44851         
44852         return false;
44853
44854     }
44855     
44856 });/**
44857  * @class Roo.htmleditor.FilterSpan
44858  * filter span's with no attributes out..
44859  * @constructor
44860  * Run a new Span Filter
44861  * @param {Object} config Configuration options
44862  */
44863
44864 Roo.htmleditor.FilterSpan = function(cfg)
44865 {
44866     // no need to apply config.
44867     this.walk(cfg.node);
44868 }
44869
44870 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
44871 {
44872      
44873     tag : 'SPAN',
44874      
44875  
44876     replaceTag : function(node)
44877     {
44878         if (node.attributes && node.attributes.length > 0) {
44879             return true; // walk if there are any.
44880         }
44881         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
44882         return false;
44883      
44884     }
44885     
44886 });/**
44887  * @class Roo.htmleditor.FilterTableWidth
44888   try and remove table width data - as that frequently messes up other stuff.
44889  * 
44890  *      was cleanTableWidths.
44891  *
44892  * Quite often pasting from word etc.. results in tables with column and widths.
44893  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
44894  *
44895  * @constructor
44896  * Run a new Table Filter
44897  * @param {Object} config Configuration options
44898  */
44899
44900 Roo.htmleditor.FilterTableWidth = function(cfg)
44901 {
44902     // no need to apply config.
44903     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
44904     this.walk(cfg.node);
44905 }
44906
44907 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
44908 {
44909      
44910      
44911     
44912     replaceTag: function(node) {
44913         
44914         
44915       
44916         if (node.hasAttribute('width')) {
44917             node.removeAttribute('width');
44918         }
44919         
44920          
44921         if (node.hasAttribute("style")) {
44922             // pretty basic...
44923             
44924             var styles = node.getAttribute("style").split(";");
44925             var nstyle = [];
44926             Roo.each(styles, function(s) {
44927                 if (!s.match(/:/)) {
44928                     return;
44929                 }
44930                 var kv = s.split(":");
44931                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
44932                     return;
44933                 }
44934                 // what ever is left... we allow.
44935                 nstyle.push(s);
44936             });
44937             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
44938             if (!nstyle.length) {
44939                 node.removeAttribute('style');
44940             }
44941         }
44942         
44943         return true; // continue doing children..
44944     }
44945 });/**
44946  * @class Roo.htmleditor.FilterWord
44947  * try and clean up all the mess that Word generates.
44948  * 
44949  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
44950  
44951  * @constructor
44952  * Run a new Span Filter
44953  * @param {Object} config Configuration options
44954  */
44955
44956 Roo.htmleditor.FilterWord = function(cfg)
44957 {
44958     // no need to apply config.
44959     this.walk(cfg.node);
44960 }
44961
44962 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
44963 {
44964     tag: true,
44965      
44966     
44967     /**
44968      * Clean up MS wordisms...
44969      */
44970     replaceTag : function(node)
44971     {
44972          
44973         // no idea what this does - span with text, replaceds with just text.
44974         if(
44975                 node.nodeName == 'SPAN' &&
44976                 !node.hasAttributes() &&
44977                 node.childNodes.length == 1 &&
44978                 node.firstChild.nodeName == "#text"  
44979         ) {
44980             var textNode = node.firstChild;
44981             node.removeChild(textNode);
44982             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44983                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
44984             }
44985             node.parentNode.insertBefore(textNode, node);
44986             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
44987                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
44988             }
44989             
44990             node.parentNode.removeChild(node);
44991             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
44992         }
44993         
44994    
44995         
44996         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
44997             node.parentNode.removeChild(node);
44998             return false; // dont do chidlren
44999         }
45000         //Roo.log(node.tagName);
45001         // remove - but keep children..
45002         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45003             //Roo.log('-- removed');
45004             while (node.childNodes.length) {
45005                 var cn = node.childNodes[0];
45006                 node.removeChild(cn);
45007                 node.parentNode.insertBefore(cn, node);
45008                 // move node to parent - and clean it..
45009                 this.replaceTag(cn);
45010             }
45011             node.parentNode.removeChild(node);
45012             /// no need to iterate chidlren = it's got none..
45013             //this.iterateChildren(node, this.cleanWord);
45014             return false; // no need to iterate children.
45015         }
45016         // clean styles
45017         if (node.className.length) {
45018             
45019             var cn = node.className.split(/\W+/);
45020             var cna = [];
45021             Roo.each(cn, function(cls) {
45022                 if (cls.match(/Mso[a-zA-Z]+/)) {
45023                     return;
45024                 }
45025                 cna.push(cls);
45026             });
45027             node.className = cna.length ? cna.join(' ') : '';
45028             if (!cna.length) {
45029                 node.removeAttribute("class");
45030             }
45031         }
45032         
45033         if (node.hasAttribute("lang")) {
45034             node.removeAttribute("lang");
45035         }
45036         
45037         if (node.hasAttribute("style")) {
45038             
45039             var styles = node.getAttribute("style").split(";");
45040             var nstyle = [];
45041             Roo.each(styles, function(s) {
45042                 if (!s.match(/:/)) {
45043                     return;
45044                 }
45045                 var kv = s.split(":");
45046                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45047                     return;
45048                 }
45049                 // what ever is left... we allow.
45050                 nstyle.push(s);
45051             });
45052             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45053             if (!nstyle.length) {
45054                 node.removeAttribute('style');
45055             }
45056         }
45057         return true; // do children
45058         
45059         
45060         
45061     }
45062 });
45063 /**
45064  * @class Roo.htmleditor.FilterStyleToTag
45065  * part of the word stuff... - certain 'styles' should be converted to tags.
45066  * eg.
45067  *   font-weight: bold -> bold
45068  *   ?? super / subscrit etc..
45069  * 
45070  * @constructor
45071 * Run a new style to tag filter.
45072 * @param {Object} config Configuration options
45073  */
45074 Roo.htmleditor.FilterStyleToTag = function(cfg)
45075 {
45076     
45077     this.tags = {
45078         B  : [ 'fontWeight' , 'bold'],
45079         I :  [ 'fontStyle' , 'italic'],
45080         //pre :  [ 'font-style' , 'italic'],
45081         // h1.. h6 ?? font-size?
45082         SUP : [ 'verticalAlign' , 'super' ],
45083         SUB : [ 'verticalAlign' , 'sub' ]
45084         
45085         
45086     };
45087     
45088     Roo.apply(this, cfg);
45089      
45090     
45091     this.walk(cfg.node);
45092     
45093     
45094     
45095 }
45096
45097
45098 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45099 {
45100     tag: true, // all tags
45101     
45102     tags : false,
45103     
45104     
45105     replaceTag : function(node)
45106     {
45107         if (node.getAttribute("style") === null) {
45108             return true;
45109         }
45110         var inject = [];
45111         for (var k in this.tags) {
45112             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45113                 inject.push(k);
45114                 node.style.removeProperty(this.tags[k][0]);
45115             }
45116         }
45117         if (!inject.length) {
45118             return true; 
45119         }
45120         var cn = Array.from(node.childNodes);
45121         var nn = node;
45122         Roo.each(inject, function(t) {
45123             var nc = node.ownerDocument.createelement(t);
45124             nn.appendChild(nc);
45125             nn = nc;
45126         });
45127         for(var i = 0;i < cn.length;cn++) {
45128             node.removeChild(cn[i]);
45129             nn.appendChild(cn[i]);
45130         }
45131         return true /// iterate thru
45132     }
45133     
45134 })/**
45135  * @class Roo.htmleditor.FilterLongBr
45136  * BR/BR/BR - keep a maximum of 2...
45137  * @constructor
45138  * Run a new Long BR Filter
45139  * @param {Object} config Configuration options
45140  */
45141
45142 Roo.htmleditor.FilterLongBr = function(cfg)
45143 {
45144     // no need to apply config.
45145     this.walk(cfg.node);
45146 }
45147
45148 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
45149 {
45150     
45151      
45152     tag : 'BR',
45153     
45154      
45155     replaceTag : function(node)
45156     {
45157         
45158         if (!node.previousSibling || node.previousSibling.tagName != 'BR') {
45159             return false;
45160         }
45161         if (!node.nextSibling || node.nextSibling.tagName != 'BR') {
45162             return false;
45163         }
45164         node.parentNode.removeChild(node); // remove me...
45165         
45166         return false; // no need to do children
45167
45168     }
45169     
45170 });
45171 /**
45172  * @class Roo.htmleditor.Tidy
45173  * Tidy HTML 
45174  * @cfg {Roo.HtmlEditorCore} core the editor.
45175  * @constructor
45176  * Create a new Filter.
45177  * @param {Object} config Configuration options
45178  */
45179
45180
45181 Roo.htmleditor.Tidy = function(cfg) {
45182     Roo.apply(this, cfg);
45183     
45184     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45185      
45186 }
45187
45188 Roo.htmleditor.Tidy.toString = function(node)
45189 {
45190     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45191 }
45192
45193 Roo.htmleditor.Tidy.prototype = {
45194     
45195     
45196     wrap : function(s) {
45197         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
45198     },
45199
45200     
45201     tidy : function(node, indent) {
45202      
45203         if  (node.nodeType == 3) {
45204             // text.
45205             
45206             
45207             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45208                 
45209             
45210         }
45211         
45212         if  (node.nodeType != 1) {
45213             return '';
45214         }
45215         
45216         
45217         
45218         if (node.tagName == 'BODY') {
45219             
45220             return this.cn(node, '');
45221         }
45222              
45223              // Prints the node tagName, such as <A>, <IMG>, etc
45224         var ret = "<" + node.tagName +  this.attr(node) ;
45225         
45226         // elements with no children..
45227         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45228                 return ret + '/>';
45229         }
45230         ret += '>';
45231         
45232         
45233         var cindent = indent === false ? '' : (indent + '  ');
45234         // tags where we will not pad the children.. (inline text tags etc..)
45235         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45236             cindent = false;
45237             
45238             
45239         }
45240         
45241         var cn = this.cn(node, cindent );
45242         
45243         return ret + cn  + '</' + node.tagName + '>';
45244         
45245     },
45246     cn: function(node, indent)
45247     {
45248         var ret = [];
45249         
45250         var ar = Array.from(node.childNodes);
45251         for (var i = 0 ; i < ar.length ; i++) {
45252             
45253             
45254             
45255             if (indent !== false   // indent==false preservies everything
45256                 && i > 0
45257                 && ar[i].nodeType == 3 
45258                 && ar[i].nodeValue.length > 0
45259                 && ar[i].nodeValue.match(/^\s+/)
45260             ) {
45261                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45262                     ret.pop(); // remove line break from last?
45263                 }
45264                 
45265                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45266             }
45267             if (indent !== false
45268                 && ar[i].nodeType == 1 // element - and indent is not set... 
45269             ) {
45270                 ret.push("\n" + indent); 
45271             }
45272             
45273             ret.push(this.tidy(ar[i], indent));
45274             // text + trailing indent 
45275             if (indent !== false
45276                 && ar[i].nodeType == 3
45277                 && ar[i].nodeValue.length > 0
45278                 && ar[i].nodeValue.match(/\s+$/)
45279             ){
45280                 ret.push("\n" + indent); 
45281             }
45282             
45283             
45284             
45285             
45286         }
45287         // what if all text?
45288         
45289         
45290         return ret.join('');
45291     },
45292     
45293          
45294         
45295     attr : function(node)
45296     {
45297         var attr = [];
45298         for(i = 0; i < node.attributes.length;i++) {
45299             
45300             // skip empty values?
45301             if (!node.attributes.item(i).value.length) {
45302                 continue;
45303             }
45304             attr.push(  node.attributes.item(i).name + '="' +
45305                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45306             );
45307         }
45308         return attr.length ? (' ' + attr.join(' ') ) : '';
45309         
45310     }
45311     
45312     
45313     
45314 }
45315 /**
45316  * @class Roo.htmleditor.KeyEnter
45317  * Handle Enter press..
45318  * @cfg {Roo.HtmlEditorCore} core the editor.
45319  * @constructor
45320  * Create a new Filter.
45321  * @param {Object} config Configuration options
45322  */
45323
45324
45325
45326 Roo.htmleditor.KeyEnter = function(cfg) {
45327     Roo.apply(this, cfg);
45328     // this does not actually call walk as it's really just a abstract class
45329  
45330     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45331 }
45332
45333
45334 Roo.htmleditor.KeyEnter.prototype = {
45335     
45336     core : false,
45337     
45338     keypress : function(e) {
45339         if (e.charCode != 13) {
45340             return true;
45341         }
45342         e.preventDefault();
45343         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45344         var doc = this.core.doc;
45345         
45346         var docFragment = doc.createDocumentFragment();
45347     
45348         //add a new line
45349         var newEle = doc.createTextNode('\n');
45350         docFragment.appendChild(newEle);
45351     
45352         //add the br, or p, or something else
45353         newEle = doc.createElement('br');
45354         docFragment.appendChild(newEle);
45355     
45356         //make the br replace selection
45357         var range = this.core.win.getSelection().getRangeAt(0);
45358         range.deleteContents();
45359         range.insertNode(docFragment);
45360     
45361         //create a new range
45362         range = doc.createRange();
45363         range.setStartAfter(newEle);
45364         range.collapse(true);
45365     
45366         //make the cursor there
45367         var sel = this.core.win.getSelection();
45368         sel.removeAllRanges();
45369         sel.addRange(range);
45370     
45371         return false;
45372          
45373     }
45374 };
45375      
45376 /**
45377  * @class Roo.htmleditor.Block
45378  * Base class for html editor blocks - do not use it directly .. extend it..
45379  * @cfg {DomElement} node The node to apply stuff to.
45380  * @cfg {String} friendly_name the name that appears in the context bar about this block
45381  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
45382  
45383  * @constructor
45384  * Create a new Filter.
45385  * @param {Object} config Configuration options
45386  */
45387
45388 Roo.htmleditor.Block  = function(cfg)
45389 {
45390     // do nothing .. should not be called really.
45391 }
45392
45393 Roo.htmleditor.Block.factory = function(node)
45394 {
45395     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
45396     if (typeof(cls) == 'undefined') {
45397         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
45398         return false;
45399     }
45400     return new cls({ node: node });  /// should trigger update element
45401 }
45402
45403
45404 Roo.htmleditor.Block.prototype = {
45405     
45406      // used by context menu
45407     friendly_name : 'Image with caption',
45408     
45409     context : false,
45410     /**
45411      * Update a node with values from this object
45412      * @param {DomElement} node
45413      */
45414     updateElement : function(node)
45415     {
45416         Roo.DomHelper.update(node, this.toObject());
45417     },
45418      /**
45419      * convert to plain HTML for calling insertAtCursor..
45420      */
45421     toHTML : function()
45422     {
45423         return Roo.DomHelper.markup(this.toObject());
45424     },
45425     /**
45426      * used by readEleemnt to extract data from a node
45427      * may need improving as it's pretty basic
45428      
45429      * @param {DomElement} node
45430      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
45431      * @param {String} attribute (use html - for contents, or style for using next param as style)
45432      * @param {String} style the style property - eg. text-align
45433      */
45434     getVal : function(node, tag, attr, style)
45435     {
45436         var n = node;
45437         if (n.tagName != tag.toUpperCase()) {
45438             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
45439             // but kiss for now.
45440             n = node.getElementsByTagName(tag).item(0);
45441         }
45442         if (attr == 'html') {
45443             return n.innerHTML;
45444         }
45445         if (attr == 'style') {
45446             return Roo.get(n).getStyle(style);
45447         }
45448         
45449         return Roo.get(n).attr(attr);
45450             
45451     },
45452     /**
45453      * create a DomHelper friendly object - for use with 
45454      * Roo.DomHelper.markup / overwrite / etc..
45455      * (override this)
45456      */
45457     toObject : function()
45458     {
45459         return {};
45460     },
45461       /**
45462      * Read a node that has a 'data-block' property - and extract the values from it.
45463      * @param {DomElement} node - the node
45464      */
45465     readElement : function(node)
45466     {
45467         
45468     } 
45469     
45470     
45471 }
45472
45473  
45474
45475 /**
45476  * @class Roo.htmleditor.BlockFigure
45477  * Block that has an image and a figcaption
45478  * @cfg {String} image_src the url for the image
45479  * @cfg {String} align (left|right) alignment for the block default left
45480  * @cfg {String} text_align (left|right) alignment for the text caption default left.
45481  * @cfg {String} caption the text to appear below  (and in the alt tag)
45482  * @cfg {String|number} image_width the width of the image number or %?
45483  * @cfg {String|number} image_height the height of the image number or %?
45484  * 
45485  * @constructor
45486  * Create a new Filter.
45487  * @param {Object} config Configuration options
45488  */
45489
45490 Roo.htmleditor.BlockFigure = function(cfg)
45491 {
45492     if (cfg.node) {
45493         this.readElement(cfg.node);
45494         this.updateElement(cfg.node);
45495     }
45496     Roo.apply(this, cfg);
45497 }
45498 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
45499  
45500     
45501     // setable values.
45502     image_src: '',
45503     
45504     align: 'left',
45505     caption : '',
45506     text_align: 'left',
45507     
45508     width : '46%',
45509     margin: '2%',
45510     
45511     // used by context menu
45512     friendly_name : 'Image with caption',
45513     
45514     context : { // ?? static really
45515         width : {
45516             title: "Width",
45517             width: 40
45518             // ?? number
45519         },
45520         margin : {
45521             title: "Margin",
45522             width: 40
45523             // ?? number
45524         },
45525         align: {
45526             title: "Align",
45527             opts : [[ "left"],[ "right"]],
45528             width : 80
45529             
45530         },
45531         text_align: {
45532             title: "Caption Align",
45533             opts : [ [ "left"],[ "right"],[ "center"]],
45534             width : 80
45535         },
45536         
45537        
45538         image_src : {
45539             title: "Src",
45540             width: 220
45541         }
45542     },
45543     /**
45544      * create a DomHelper friendly object - for use with
45545      * Roo.DomHelper.markup / overwrite / etc..
45546      */
45547     toObject : function()
45548     {
45549         var d = document.createElement('div');
45550         d.innerHTML = this.caption;
45551         
45552         return {
45553             tag: 'figure',
45554             'data-block' : 'Figure',
45555             contenteditable : 'false',
45556             style : {
45557                 display: 'table',
45558                 float :  this.align ,
45559                 width :  this.width,
45560                 margin:  this.margin
45561             },
45562             cn : [
45563                 {
45564                     tag : 'img',
45565                     src : this.image_src,
45566                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
45567                     style: {
45568                         width: '100%'
45569                     }
45570                 },
45571                 {
45572                     tag: 'figcaption',
45573                     contenteditable : true,
45574                     style : {
45575                         'text-align': this.text_align
45576                     },
45577                     html : this.caption
45578                     
45579                 }
45580             ]
45581         };
45582     },
45583     
45584     readElement : function(node)
45585     {
45586         this.image_src = this.getVal(node, 'img', 'src');
45587         this.align = this.getVal(node, 'figure', 'style', 'float');
45588         this.caption = this.getVal(node, 'figcaption', 'html');
45589         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
45590         this.width = this.getVal(node, 'figure', 'style', 'width');
45591         this.margin = this.getVal(node, 'figure', 'style', 'margin');
45592         
45593     } 
45594     
45595   
45596    
45597      
45598     
45599     
45600     
45601     
45602 })
45603
45604 //<script type="text/javascript">
45605
45606 /*
45607  * Based  Ext JS Library 1.1.1
45608  * Copyright(c) 2006-2007, Ext JS, LLC.
45609  * LGPL
45610  *
45611  */
45612  
45613 /**
45614  * @class Roo.HtmlEditorCore
45615  * @extends Roo.Component
45616  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
45617  *
45618  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
45619  */
45620
45621 Roo.HtmlEditorCore = function(config){
45622     
45623     
45624     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
45625     
45626     
45627     this.addEvents({
45628         /**
45629          * @event initialize
45630          * Fires when the editor is fully initialized (including the iframe)
45631          * @param {Roo.HtmlEditorCore} this
45632          */
45633         initialize: true,
45634         /**
45635          * @event activate
45636          * Fires when the editor is first receives the focus. Any insertion must wait
45637          * until after this event.
45638          * @param {Roo.HtmlEditorCore} this
45639          */
45640         activate: true,
45641          /**
45642          * @event beforesync
45643          * Fires before the textarea is updated with content from the editor iframe. Return false
45644          * to cancel the sync.
45645          * @param {Roo.HtmlEditorCore} this
45646          * @param {String} html
45647          */
45648         beforesync: true,
45649          /**
45650          * @event beforepush
45651          * Fires before the iframe editor is updated with content from the textarea. Return false
45652          * to cancel the push.
45653          * @param {Roo.HtmlEditorCore} this
45654          * @param {String} html
45655          */
45656         beforepush: true,
45657          /**
45658          * @event sync
45659          * Fires when the textarea is updated with content from the editor iframe.
45660          * @param {Roo.HtmlEditorCore} this
45661          * @param {String} html
45662          */
45663         sync: true,
45664          /**
45665          * @event push
45666          * Fires when the iframe editor is updated with content from the textarea.
45667          * @param {Roo.HtmlEditorCore} this
45668          * @param {String} html
45669          */
45670         push: true,
45671         
45672         /**
45673          * @event editorevent
45674          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
45675          * @param {Roo.HtmlEditorCore} this
45676          */
45677         editorevent: true
45678         
45679     });
45680     
45681     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
45682     
45683     // defaults : white / black...
45684     this.applyBlacklists();
45685     
45686     
45687     
45688 };
45689
45690
45691 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
45692
45693
45694      /**
45695      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
45696      */
45697     
45698     owner : false,
45699     
45700      /**
45701      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
45702      *                        Roo.resizable.
45703      */
45704     resizable : false,
45705      /**
45706      * @cfg {Number} height (in pixels)
45707      */   
45708     height: 300,
45709    /**
45710      * @cfg {Number} width (in pixels)
45711      */   
45712     width: 500,
45713     
45714     /**
45715      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
45716      * 
45717      */
45718     stylesheets: false,
45719     
45720     /**
45721      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
45722      */
45723     allowComments: false,
45724     // id of frame..
45725     frameId: false,
45726     
45727     // private properties
45728     validationEvent : false,
45729     deferHeight: true,
45730     initialized : false,
45731     activated : false,
45732     sourceEditMode : false,
45733     onFocus : Roo.emptyFn,
45734     iframePad:3,
45735     hideMode:'offsets',
45736     
45737     clearUp: true,
45738     
45739     // blacklist + whitelisted elements..
45740     black: false,
45741     white: false,
45742      
45743     bodyCls : '',
45744
45745     /**
45746      * Protected method that will not generally be called directly. It
45747      * is called when the editor initializes the iframe with HTML contents. Override this method if you
45748      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
45749      */
45750     getDocMarkup : function(){
45751         // body styles..
45752         var st = '';
45753         
45754         // inherit styels from page...?? 
45755         if (this.stylesheets === false) {
45756             
45757             Roo.get(document.head).select('style').each(function(node) {
45758                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45759             });
45760             
45761             Roo.get(document.head).select('link').each(function(node) { 
45762                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
45763             });
45764             
45765         } else if (!this.stylesheets.length) {
45766                 // simple..
45767                 st = '<style type="text/css">' +
45768                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45769                    '</style>';
45770         } else {
45771             for (var i in this.stylesheets) {
45772                 if (typeof(this.stylesheets[i]) != 'string') {
45773                     continue;
45774                 }
45775                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
45776             }
45777             
45778         }
45779         
45780         st +=  '<style type="text/css">' +
45781             'IMG { cursor: pointer } ' +
45782         '</style>';
45783
45784         var cls = 'roo-htmleditor-body';
45785         
45786         if(this.bodyCls.length){
45787             cls += ' ' + this.bodyCls;
45788         }
45789         
45790         return '<html><head>' + st  +
45791             //<style type="text/css">' +
45792             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
45793             //'</style>' +
45794             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
45795     },
45796
45797     // private
45798     onRender : function(ct, position)
45799     {
45800         var _t = this;
45801         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
45802         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
45803         
45804         
45805         this.el.dom.style.border = '0 none';
45806         this.el.dom.setAttribute('tabIndex', -1);
45807         this.el.addClass('x-hidden hide');
45808         
45809         
45810         
45811         if(Roo.isIE){ // fix IE 1px bogus margin
45812             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
45813         }
45814        
45815         
45816         this.frameId = Roo.id();
45817         
45818          
45819         
45820         var iframe = this.owner.wrap.createChild({
45821             tag: 'iframe',
45822             cls: 'form-control', // bootstrap..
45823             id: this.frameId,
45824             name: this.frameId,
45825             frameBorder : 'no',
45826             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
45827         }, this.el
45828         );
45829         
45830         
45831         this.iframe = iframe.dom;
45832
45833         this.assignDocWin();
45834         
45835         this.doc.designMode = 'on';
45836        
45837         this.doc.open();
45838         this.doc.write(this.getDocMarkup());
45839         this.doc.close();
45840
45841         
45842         var task = { // must defer to wait for browser to be ready
45843             run : function(){
45844                 //console.log("run task?" + this.doc.readyState);
45845                 this.assignDocWin();
45846                 if(this.doc.body || this.doc.readyState == 'complete'){
45847                     try {
45848                         this.doc.designMode="on";
45849                     } catch (e) {
45850                         return;
45851                     }
45852                     Roo.TaskMgr.stop(task);
45853                     this.initEditor.defer(10, this);
45854                 }
45855             },
45856             interval : 10,
45857             duration: 10000,
45858             scope: this
45859         };
45860         Roo.TaskMgr.start(task);
45861
45862     },
45863
45864     // private
45865     onResize : function(w, h)
45866     {
45867          Roo.log('resize: ' +w + ',' + h );
45868         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
45869         if(!this.iframe){
45870             return;
45871         }
45872         if(typeof w == 'number'){
45873             
45874             this.iframe.style.width = w + 'px';
45875         }
45876         if(typeof h == 'number'){
45877             
45878             this.iframe.style.height = h + 'px';
45879             if(this.doc){
45880                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
45881             }
45882         }
45883         
45884     },
45885
45886     /**
45887      * Toggles the editor between standard and source edit mode.
45888      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
45889      */
45890     toggleSourceEdit : function(sourceEditMode){
45891         
45892         this.sourceEditMode = sourceEditMode === true;
45893         
45894         if(this.sourceEditMode){
45895  
45896             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
45897             
45898         }else{
45899             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
45900             //this.iframe.className = '';
45901             this.deferFocus();
45902         }
45903         //this.setSize(this.owner.wrap.getSize());
45904         //this.fireEvent('editmodechange', this, this.sourceEditMode);
45905     },
45906
45907     
45908   
45909
45910     /**
45911      * Protected method that will not generally be called directly. If you need/want
45912      * custom HTML cleanup, this is the method you should override.
45913      * @param {String} html The HTML to be cleaned
45914      * return {String} The cleaned HTML
45915      */
45916     cleanHtml : function(html){
45917         html = String(html);
45918         if(html.length > 5){
45919             if(Roo.isSafari){ // strip safari nonsense
45920                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
45921             }
45922         }
45923         if(html == '&nbsp;'){
45924             html = '';
45925         }
45926         return html;
45927     },
45928
45929     /**
45930      * HTML Editor -> Textarea
45931      * Protected method that will not generally be called directly. Syncs the contents
45932      * of the editor iframe with the textarea.
45933      */
45934     syncValue : function()
45935     {
45936         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
45937         if(this.initialized){
45938             var bd = (this.doc.body || this.doc.documentElement);
45939             //this.cleanUpPaste(); -- this is done else where and causes havoc..
45940             
45941             // not sure if this is really the place for this
45942             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
45943             // this has to update attributes that get duped.. like alt and caption..
45944             
45945             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
45946                  Roo.htmleditor.Block.factory(e);
45947             },this);
45948             
45949             
45950             var div = document.createElement('div');
45951             div.innerHTML = bd.innerHTML;
45952             // remove content editable. (blocks)
45953             
45954            
45955             
45956             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
45957             //?? tidy?
45958             var html = div.innerHTML;
45959             if(Roo.isSafari){
45960                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
45961                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
45962                 if(m && m[1]){
45963                     html = '<div style="'+m[0]+'">' + html + '</div>';
45964                 }
45965             }
45966             html = this.cleanHtml(html);
45967             // fix up the special chars.. normaly like back quotes in word...
45968             // however we do not want to do this with chinese..
45969             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
45970                 
45971                 var cc = match.charCodeAt();
45972
45973                 // Get the character value, handling surrogate pairs
45974                 if (match.length == 2) {
45975                     // It's a surrogate pair, calculate the Unicode code point
45976                     var high = match.charCodeAt(0) - 0xD800;
45977                     var low  = match.charCodeAt(1) - 0xDC00;
45978                     cc = (high * 0x400) + low + 0x10000;
45979                 }  else if (
45980                     (cc >= 0x4E00 && cc < 0xA000 ) ||
45981                     (cc >= 0x3400 && cc < 0x4E00 ) ||
45982                     (cc >= 0xf900 && cc < 0xfb00 )
45983                 ) {
45984                         return match;
45985                 }  
45986          
45987                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
45988                 return "&#" + cc + ";";
45989                 
45990                 
45991             });
45992             
45993             
45994              
45995             if(this.owner.fireEvent('beforesync', this, html) !== false){
45996                 this.el.dom.value = html;
45997                 this.owner.fireEvent('sync', this, html);
45998             }
45999         }
46000     },
46001
46002     /**
46003      * TEXTAREA -> EDITABLE
46004      * Protected method that will not generally be called directly. Pushes the value of the textarea
46005      * into the iframe editor.
46006      */
46007     pushValue : function()
46008     {
46009         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46010         if(this.initialized){
46011             var v = this.el.dom.value.trim();
46012             
46013             
46014             if(this.owner.fireEvent('beforepush', this, v) !== false){
46015                 var d = (this.doc.body || this.doc.documentElement);
46016                 d.innerHTML = v;
46017                  
46018                 this.el.dom.value = d.innerHTML;
46019                 this.owner.fireEvent('push', this, v);
46020             }
46021             
46022             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46023                 
46024                 Roo.htmleditor.Block.factory(e);
46025                 
46026             },this);
46027             var lc = this.doc.body.lastChild;
46028             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46029                 // add an extra line at the end.
46030                 this.doc.body.appendChild(this.doc.createElement('br'));
46031             }
46032             
46033             
46034         }
46035     },
46036
46037     // private
46038     deferFocus : function(){
46039         this.focus.defer(10, this);
46040     },
46041
46042     // doc'ed in Field
46043     focus : function(){
46044         if(this.win && !this.sourceEditMode){
46045             this.win.focus();
46046         }else{
46047             this.el.focus();
46048         }
46049     },
46050     
46051     assignDocWin: function()
46052     {
46053         var iframe = this.iframe;
46054         
46055          if(Roo.isIE){
46056             this.doc = iframe.contentWindow.document;
46057             this.win = iframe.contentWindow;
46058         } else {
46059 //            if (!Roo.get(this.frameId)) {
46060 //                return;
46061 //            }
46062 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46063 //            this.win = Roo.get(this.frameId).dom.contentWindow;
46064             
46065             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
46066                 return;
46067             }
46068             
46069             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46070             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
46071         }
46072     },
46073     
46074     // private
46075     initEditor : function(){
46076         //console.log("INIT EDITOR");
46077         this.assignDocWin();
46078         
46079         
46080         
46081         this.doc.designMode="on";
46082         this.doc.open();
46083         this.doc.write(this.getDocMarkup());
46084         this.doc.close();
46085         
46086         var dbody = (this.doc.body || this.doc.documentElement);
46087         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
46088         // this copies styles from the containing element into thsi one..
46089         // not sure why we need all of this..
46090         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
46091         
46092         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
46093         //ss['background-attachment'] = 'fixed'; // w3c
46094         dbody.bgProperties = 'fixed'; // ie
46095         //Roo.DomHelper.applyStyles(dbody, ss);
46096         Roo.EventManager.on(this.doc, {
46097             //'mousedown': this.onEditorEvent,
46098             'mouseup': this.onEditorEvent,
46099             'dblclick': this.onEditorEvent,
46100             'click': this.onEditorEvent,
46101             'keyup': this.onEditorEvent,
46102             
46103             buffer:100,
46104             scope: this
46105         });
46106         Roo.EventManager.on(this.doc, {
46107             'paste': this.onPasteEvent,
46108             scope : this
46109         });
46110         if(Roo.isGecko){
46111             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
46112         }
46113         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
46114             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
46115         }
46116         this.initialized = true;
46117
46118         
46119         // initialize special key events - enter
46120         new Roo.htmleditor.KeyEnter({core : this});
46121         
46122          
46123         
46124         this.owner.fireEvent('initialize', this);
46125         this.pushValue();
46126     },
46127     
46128     onPasteEvent : function(e,v)
46129     {
46130         // I think we better assume paste is going to be a dirty load of rubish from word..
46131         
46132         // even pasting into a 'email version' of this widget will have to clean up that mess.
46133         
46134         var html = (e.browserEvent.clipboardData || window.clipboardData).getData('text/html'); // clipboard event
46135         
46136         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
46137         
46138         
46139         new Roo.htmleditor.FilterStyleToTag({ node : d });
46140         new Roo.htmleditor.FilterAttributes({
46141             node : d,
46142             attrib_white : ['href'],
46143             attrib_clean : ['href'] 
46144         });
46145         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
46146         new Roo.htmleditor.FilterKeepChildren({node : d, tag : this.tag_remove} );
46147         new Roo.htmleditor.FilterParagraph({ node : d });
46148         new Roo.htmleditor.FilterLongBr({ node : d });
46149         this.insertAtCursor(d.innerHTML);
46150         
46151         e.preventDefault();
46152         return false;
46153         // default behaveiour should be our local cleanup paste? (optional?)
46154         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
46155         //this.owner.fireEvent('paste', e, v);
46156     },
46157     // private
46158     onDestroy : function(){
46159         
46160         
46161         
46162         if(this.rendered){
46163             
46164             //for (var i =0; i < this.toolbars.length;i++) {
46165             //    // fixme - ask toolbars for heights?
46166             //    this.toolbars[i].onDestroy();
46167            // }
46168             
46169             //this.wrap.dom.innerHTML = '';
46170             //this.wrap.remove();
46171         }
46172     },
46173
46174     // private
46175     onFirstFocus : function(){
46176         
46177         this.assignDocWin();
46178         
46179         
46180         this.activated = true;
46181          
46182     
46183         if(Roo.isGecko){ // prevent silly gecko errors
46184             this.win.focus();
46185             var s = this.win.getSelection();
46186             if(!s.focusNode || s.focusNode.nodeType != 3){
46187                 var r = s.getRangeAt(0);
46188                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
46189                 r.collapse(true);
46190                 this.deferFocus();
46191             }
46192             try{
46193                 this.execCmd('useCSS', true);
46194                 this.execCmd('styleWithCSS', false);
46195             }catch(e){}
46196         }
46197         this.owner.fireEvent('activate', this);
46198     },
46199
46200     // private
46201     adjustFont: function(btn){
46202         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
46203         //if(Roo.isSafari){ // safari
46204         //    adjust *= 2;
46205        // }
46206         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
46207         if(Roo.isSafari){ // safari
46208             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
46209             v =  (v < 10) ? 10 : v;
46210             v =  (v > 48) ? 48 : v;
46211             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
46212             
46213         }
46214         
46215         
46216         v = Math.max(1, v+adjust);
46217         
46218         this.execCmd('FontSize', v  );
46219     },
46220
46221     onEditorEvent : function(e)
46222     {
46223         this.owner.fireEvent('editorevent', this, e);
46224       //  this.updateToolbar();
46225         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
46226     },
46227
46228     insertTag : function(tg)
46229     {
46230         // could be a bit smarter... -> wrap the current selected tRoo..
46231         if (tg.toLowerCase() == 'span' ||
46232             tg.toLowerCase() == 'code' ||
46233             tg.toLowerCase() == 'sup' ||
46234             tg.toLowerCase() == 'sub' 
46235             ) {
46236             
46237             range = this.createRange(this.getSelection());
46238             var wrappingNode = this.doc.createElement(tg.toLowerCase());
46239             wrappingNode.appendChild(range.extractContents());
46240             range.insertNode(wrappingNode);
46241
46242             return;
46243             
46244             
46245             
46246         }
46247         this.execCmd("formatblock",   tg);
46248         
46249     },
46250     
46251     insertText : function(txt)
46252     {
46253         
46254         
46255         var range = this.createRange();
46256         range.deleteContents();
46257                //alert(Sender.getAttribute('label'));
46258                
46259         range.insertNode(this.doc.createTextNode(txt));
46260     } ,
46261     
46262      
46263
46264     /**
46265      * Executes a Midas editor command on the editor document and performs necessary focus and
46266      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
46267      * @param {String} cmd The Midas command
46268      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46269      */
46270     relayCmd : function(cmd, value){
46271         this.win.focus();
46272         this.execCmd(cmd, value);
46273         this.owner.fireEvent('editorevent', this);
46274         //this.updateToolbar();
46275         this.owner.deferFocus();
46276     },
46277
46278     /**
46279      * Executes a Midas editor command directly on the editor document.
46280      * For visual commands, you should use {@link #relayCmd} instead.
46281      * <b>This should only be called after the editor is initialized.</b>
46282      * @param {String} cmd The Midas command
46283      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46284      */
46285     execCmd : function(cmd, value){
46286         this.doc.execCommand(cmd, false, value === undefined ? null : value);
46287         this.syncValue();
46288     },
46289  
46290  
46291    
46292     /**
46293      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
46294      * to insert tRoo.
46295      * @param {String} text | dom node.. 
46296      */
46297     insertAtCursor : function(text)
46298     {
46299         
46300         if(!this.activated){
46301             return;
46302         }
46303         /*
46304         if(Roo.isIE){
46305             this.win.focus();
46306             var r = this.doc.selection.createRange();
46307             if(r){
46308                 r.collapse(true);
46309                 r.pasteHTML(text);
46310                 this.syncValue();
46311                 this.deferFocus();
46312             
46313             }
46314             return;
46315         }
46316         */
46317         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
46318             this.win.focus();
46319             
46320             
46321             // from jquery ui (MIT licenced)
46322             var range, node;
46323             var win = this.win;
46324             
46325             if (win.getSelection && win.getSelection().getRangeAt) {
46326                 range = win.getSelection().getRangeAt(0);
46327                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
46328                 range.insertNode(node);
46329             } else if (win.document.selection && win.document.selection.createRange) {
46330                 // no firefox support
46331                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46332                 win.document.selection.createRange().pasteHTML(txt);
46333             } else {
46334                 // no firefox support
46335                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46336                 this.execCmd('InsertHTML', txt);
46337             } 
46338             
46339             this.syncValue();
46340             
46341             this.deferFocus();
46342         }
46343     },
46344  // private
46345     mozKeyPress : function(e){
46346         if(e.ctrlKey){
46347             var c = e.getCharCode(), cmd;
46348           
46349             if(c > 0){
46350                 c = String.fromCharCode(c).toLowerCase();
46351                 switch(c){
46352                     case 'b':
46353                         cmd = 'bold';
46354                         break;
46355                     case 'i':
46356                         cmd = 'italic';
46357                         break;
46358                     
46359                     case 'u':
46360                         cmd = 'underline';
46361                         break;
46362                     
46363                     //case 'v':
46364                       //  this.cleanUpPaste.defer(100, this);
46365                       //  return;
46366                         
46367                 }
46368                 if(cmd){
46369                     this.win.focus();
46370                     this.execCmd(cmd);
46371                     this.deferFocus();
46372                     e.preventDefault();
46373                 }
46374                 
46375             }
46376         }
46377     },
46378
46379     // private
46380     fixKeys : function(){ // load time branching for fastest keydown performance
46381         if(Roo.isIE){
46382             return function(e){
46383                 var k = e.getKey(), r;
46384                 if(k == e.TAB){
46385                     e.stopEvent();
46386                     r = this.doc.selection.createRange();
46387                     if(r){
46388                         r.collapse(true);
46389                         r.pasteHTML('&#160;&#160;&#160;&#160;');
46390                         this.deferFocus();
46391                     }
46392                     return;
46393                 }
46394                 
46395                 if(k == e.ENTER){
46396                     r = this.doc.selection.createRange();
46397                     if(r){
46398                         var target = r.parentElement();
46399                         if(!target || target.tagName.toLowerCase() != 'li'){
46400                             e.stopEvent();
46401                             r.pasteHTML('<br/>');
46402                             r.collapse(false);
46403                             r.select();
46404                         }
46405                     }
46406                 }
46407                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46408                 //    this.cleanUpPaste.defer(100, this);
46409                 //    return;
46410                 //}
46411                 
46412                 
46413             };
46414         }else if(Roo.isOpera){
46415             return function(e){
46416                 var k = e.getKey();
46417                 if(k == e.TAB){
46418                     e.stopEvent();
46419                     this.win.focus();
46420                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
46421                     this.deferFocus();
46422                 }
46423                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46424                 //    this.cleanUpPaste.defer(100, this);
46425                  //   return;
46426                 //}
46427                 
46428             };
46429         }else if(Roo.isSafari){
46430             return function(e){
46431                 var k = e.getKey();
46432                 
46433                 if(k == e.TAB){
46434                     e.stopEvent();
46435                     this.execCmd('InsertText','\t');
46436                     this.deferFocus();
46437                     return;
46438                 }
46439                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
46440                  //   this.cleanUpPaste.defer(100, this);
46441                  //   return;
46442                // }
46443                 
46444              };
46445         }
46446     }(),
46447     
46448     getAllAncestors: function()
46449     {
46450         var p = this.getSelectedNode();
46451         var a = [];
46452         if (!p) {
46453             a.push(p); // push blank onto stack..
46454             p = this.getParentElement();
46455         }
46456         
46457         
46458         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
46459             a.push(p);
46460             p = p.parentNode;
46461         }
46462         a.push(this.doc.body);
46463         return a;
46464     },
46465     lastSel : false,
46466     lastSelNode : false,
46467     
46468     
46469     getSelection : function() 
46470     {
46471         this.assignDocWin();
46472         return Roo.isIE ? this.doc.selection : this.win.getSelection();
46473     },
46474     /**
46475      * Select a dom node
46476      * @param {DomElement} node the node to select
46477      */
46478     selectNode : function(node)
46479     {
46480         
46481             var nodeRange = node.ownerDocument.createRange();
46482             try {
46483                 nodeRange.selectNode(node);
46484             } catch (e) {
46485                 nodeRange.selectNodeContents(node);
46486             }
46487             //nodeRange.collapse(true);
46488             var s = this.win.getSelection();
46489             s.removeAllRanges();
46490             s.addRange(nodeRange);
46491     },
46492     
46493     getSelectedNode: function() 
46494     {
46495         // this may only work on Gecko!!!
46496         
46497         // should we cache this!!!!
46498         
46499         
46500         
46501          
46502         var range = this.createRange(this.getSelection()).cloneRange();
46503         
46504         if (Roo.isIE) {
46505             var parent = range.parentElement();
46506             while (true) {
46507                 var testRange = range.duplicate();
46508                 testRange.moveToElementText(parent);
46509                 if (testRange.inRange(range)) {
46510                     break;
46511                 }
46512                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
46513                     break;
46514                 }
46515                 parent = parent.parentElement;
46516             }
46517             return parent;
46518         }
46519         
46520         // is ancestor a text element.
46521         var ac =  range.commonAncestorContainer;
46522         if (ac.nodeType == 3) {
46523             ac = ac.parentNode;
46524         }
46525         
46526         var ar = ac.childNodes;
46527          
46528         var nodes = [];
46529         var other_nodes = [];
46530         var has_other_nodes = false;
46531         for (var i=0;i<ar.length;i++) {
46532             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
46533                 continue;
46534             }
46535             // fullly contained node.
46536             
46537             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
46538                 nodes.push(ar[i]);
46539                 continue;
46540             }
46541             
46542             // probably selected..
46543             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
46544                 other_nodes.push(ar[i]);
46545                 continue;
46546             }
46547             // outer..
46548             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
46549                 continue;
46550             }
46551             
46552             
46553             has_other_nodes = true;
46554         }
46555         if (!nodes.length && other_nodes.length) {
46556             nodes= other_nodes;
46557         }
46558         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
46559             return false;
46560         }
46561         
46562         return nodes[0];
46563     },
46564     createRange: function(sel)
46565     {
46566         // this has strange effects when using with 
46567         // top toolbar - not sure if it's a great idea.
46568         //this.editor.contentWindow.focus();
46569         if (typeof sel != "undefined") {
46570             try {
46571                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
46572             } catch(e) {
46573                 return this.doc.createRange();
46574             }
46575         } else {
46576             return this.doc.createRange();
46577         }
46578     },
46579     getParentElement: function()
46580     {
46581         
46582         this.assignDocWin();
46583         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
46584         
46585         var range = this.createRange(sel);
46586          
46587         try {
46588             var p = range.commonAncestorContainer;
46589             while (p.nodeType == 3) { // text node
46590                 p = p.parentNode;
46591             }
46592             return p;
46593         } catch (e) {
46594             return null;
46595         }
46596     
46597     },
46598     /***
46599      *
46600      * Range intersection.. the hard stuff...
46601      *  '-1' = before
46602      *  '0' = hits..
46603      *  '1' = after.
46604      *         [ -- selected range --- ]
46605      *   [fail]                        [fail]
46606      *
46607      *    basically..
46608      *      if end is before start or  hits it. fail.
46609      *      if start is after end or hits it fail.
46610      *
46611      *   if either hits (but other is outside. - then it's not 
46612      *   
46613      *    
46614      **/
46615     
46616     
46617     // @see http://www.thismuchiknow.co.uk/?p=64.
46618     rangeIntersectsNode : function(range, node)
46619     {
46620         var nodeRange = node.ownerDocument.createRange();
46621         try {
46622             nodeRange.selectNode(node);
46623         } catch (e) {
46624             nodeRange.selectNodeContents(node);
46625         }
46626     
46627         var rangeStartRange = range.cloneRange();
46628         rangeStartRange.collapse(true);
46629     
46630         var rangeEndRange = range.cloneRange();
46631         rangeEndRange.collapse(false);
46632     
46633         var nodeStartRange = nodeRange.cloneRange();
46634         nodeStartRange.collapse(true);
46635     
46636         var nodeEndRange = nodeRange.cloneRange();
46637         nodeEndRange.collapse(false);
46638     
46639         return rangeStartRange.compareBoundaryPoints(
46640                  Range.START_TO_START, nodeEndRange) == -1 &&
46641                rangeEndRange.compareBoundaryPoints(
46642                  Range.START_TO_START, nodeStartRange) == 1;
46643         
46644          
46645     },
46646     rangeCompareNode : function(range, node)
46647     {
46648         var nodeRange = node.ownerDocument.createRange();
46649         try {
46650             nodeRange.selectNode(node);
46651         } catch (e) {
46652             nodeRange.selectNodeContents(node);
46653         }
46654         
46655         
46656         range.collapse(true);
46657     
46658         nodeRange.collapse(true);
46659      
46660         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
46661         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
46662          
46663         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
46664         
46665         var nodeIsBefore   =  ss == 1;
46666         var nodeIsAfter    = ee == -1;
46667         
46668         if (nodeIsBefore && nodeIsAfter) {
46669             return 0; // outer
46670         }
46671         if (!nodeIsBefore && nodeIsAfter) {
46672             return 1; //right trailed.
46673         }
46674         
46675         if (nodeIsBefore && !nodeIsAfter) {
46676             return 2;  // left trailed.
46677         }
46678         // fully contined.
46679         return 3;
46680     },
46681  
46682     cleanWordChars : function(input) {// change the chars to hex code
46683         var he = Roo.HtmlEditorCore;
46684         
46685         var output = input;
46686         Roo.each(he.swapCodes, function(sw) { 
46687             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
46688             
46689             output = output.replace(swapper, sw[1]);
46690         });
46691         
46692         return output;
46693     },
46694     
46695      
46696     
46697         
46698     
46699     cleanUpChild : function (node)
46700     {
46701         
46702         new Roo.htmleditor.FilterComment({node : node});
46703         new Roo.htmleditor.FilterAttributes({
46704                 node : node,
46705                 attrib_black : this.ablack,
46706                 attrib_clean : this.aclean,
46707                 style_white : this.cwhite,
46708                 style_black : this.cblack
46709         });
46710         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
46711         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
46712          
46713         
46714     },
46715     
46716     /**
46717      * Clean up MS wordisms...
46718      * @deprecated - use filter directly
46719      */
46720     cleanWord : function(node)
46721     {
46722         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
46723         
46724     },
46725    
46726     
46727     /**
46728
46729      * @deprecated - use filters
46730      */
46731     cleanTableWidths : function(node)
46732     {
46733         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
46734         
46735  
46736     },
46737     
46738      
46739         
46740     applyBlacklists : function()
46741     {
46742         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
46743         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
46744         
46745         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
46746         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
46747         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
46748         
46749         this.white = [];
46750         this.black = [];
46751         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
46752             if (b.indexOf(tag) > -1) {
46753                 return;
46754             }
46755             this.white.push(tag);
46756             
46757         }, this);
46758         
46759         Roo.each(w, function(tag) {
46760             if (b.indexOf(tag) > -1) {
46761                 return;
46762             }
46763             if (this.white.indexOf(tag) > -1) {
46764                 return;
46765             }
46766             this.white.push(tag);
46767             
46768         }, this);
46769         
46770         
46771         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
46772             if (w.indexOf(tag) > -1) {
46773                 return;
46774             }
46775             this.black.push(tag);
46776             
46777         }, this);
46778         
46779         Roo.each(b, function(tag) {
46780             if (w.indexOf(tag) > -1) {
46781                 return;
46782             }
46783             if (this.black.indexOf(tag) > -1) {
46784                 return;
46785             }
46786             this.black.push(tag);
46787             
46788         }, this);
46789         
46790         
46791         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
46792         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
46793         
46794         this.cwhite = [];
46795         this.cblack = [];
46796         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
46797             if (b.indexOf(tag) > -1) {
46798                 return;
46799             }
46800             this.cwhite.push(tag);
46801             
46802         }, this);
46803         
46804         Roo.each(w, function(tag) {
46805             if (b.indexOf(tag) > -1) {
46806                 return;
46807             }
46808             if (this.cwhite.indexOf(tag) > -1) {
46809                 return;
46810             }
46811             this.cwhite.push(tag);
46812             
46813         }, this);
46814         
46815         
46816         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
46817             if (w.indexOf(tag) > -1) {
46818                 return;
46819             }
46820             this.cblack.push(tag);
46821             
46822         }, this);
46823         
46824         Roo.each(b, function(tag) {
46825             if (w.indexOf(tag) > -1) {
46826                 return;
46827             }
46828             if (this.cblack.indexOf(tag) > -1) {
46829                 return;
46830             }
46831             this.cblack.push(tag);
46832             
46833         }, this);
46834     },
46835     
46836     setStylesheets : function(stylesheets)
46837     {
46838         if(typeof(stylesheets) == 'string'){
46839             Roo.get(this.iframe.contentDocument.head).createChild({
46840                 tag : 'link',
46841                 rel : 'stylesheet',
46842                 type : 'text/css',
46843                 href : stylesheets
46844             });
46845             
46846             return;
46847         }
46848         var _this = this;
46849      
46850         Roo.each(stylesheets, function(s) {
46851             if(!s.length){
46852                 return;
46853             }
46854             
46855             Roo.get(_this.iframe.contentDocument.head).createChild({
46856                 tag : 'link',
46857                 rel : 'stylesheet',
46858                 type : 'text/css',
46859                 href : s
46860             });
46861         });
46862
46863         
46864     },
46865     
46866     removeStylesheets : function()
46867     {
46868         var _this = this;
46869         
46870         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
46871             s.remove();
46872         });
46873     },
46874     
46875     setStyle : function(style)
46876     {
46877         Roo.get(this.iframe.contentDocument.head).createChild({
46878             tag : 'style',
46879             type : 'text/css',
46880             html : style
46881         });
46882
46883         return;
46884     }
46885     
46886     // hide stuff that is not compatible
46887     /**
46888      * @event blur
46889      * @hide
46890      */
46891     /**
46892      * @event change
46893      * @hide
46894      */
46895     /**
46896      * @event focus
46897      * @hide
46898      */
46899     /**
46900      * @event specialkey
46901      * @hide
46902      */
46903     /**
46904      * @cfg {String} fieldClass @hide
46905      */
46906     /**
46907      * @cfg {String} focusClass @hide
46908      */
46909     /**
46910      * @cfg {String} autoCreate @hide
46911      */
46912     /**
46913      * @cfg {String} inputType @hide
46914      */
46915     /**
46916      * @cfg {String} invalidClass @hide
46917      */
46918     /**
46919      * @cfg {String} invalidText @hide
46920      */
46921     /**
46922      * @cfg {String} msgFx @hide
46923      */
46924     /**
46925      * @cfg {String} validateOnBlur @hide
46926      */
46927 });
46928
46929 Roo.HtmlEditorCore.white = [
46930         'area', 'br', 'img', 'input', 'hr', 'wbr',
46931         
46932        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
46933        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
46934        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
46935        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
46936        'table',   'ul',         'xmp', 
46937        
46938        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
46939       'thead',   'tr', 
46940      
46941       'dir', 'menu', 'ol', 'ul', 'dl',
46942        
46943       'embed',  'object'
46944 ];
46945
46946
46947 Roo.HtmlEditorCore.black = [
46948     //    'embed',  'object', // enable - backend responsiblity to clean thiese
46949         'applet', // 
46950         'base',   'basefont', 'bgsound', 'blink',  'body', 
46951         'frame',  'frameset', 'head',    'html',   'ilayer', 
46952         'iframe', 'layer',  'link',     'meta',    'object',   
46953         'script', 'style' ,'title',  'xml' // clean later..
46954 ];
46955 Roo.HtmlEditorCore.clean = [
46956     'script', 'style', 'title', 'xml'
46957 ];
46958 Roo.HtmlEditorCore.tag_remove = [
46959     'font'
46960 ];
46961 // attributes..
46962
46963 Roo.HtmlEditorCore.ablack = [
46964     'on'
46965 ];
46966     
46967 Roo.HtmlEditorCore.aclean = [ 
46968     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
46969 ];
46970
46971 // protocols..
46972 Roo.HtmlEditorCore.pwhite= [
46973         'http',  'https',  'mailto'
46974 ];
46975
46976 // white listed style attributes.
46977 Roo.HtmlEditorCore.cwhite= [
46978       //  'text-align', /// default is to allow most things..
46979       
46980          
46981 //        'font-size'//??
46982 ];
46983
46984 // black listed style attributes.
46985 Roo.HtmlEditorCore.cblack= [
46986       //  'font-size' -- this can be set by the project 
46987 ];
46988
46989
46990 Roo.HtmlEditorCore.swapCodes   =[ 
46991     [    8211, "&#8211;" ], 
46992     [    8212, "&#8212;" ], 
46993     [    8216,  "'" ],  
46994     [    8217, "'" ],  
46995     [    8220, '"' ],  
46996     [    8221, '"' ],  
46997     [    8226, "*" ],  
46998     [    8230, "..." ]
46999 ]; 
47000
47001     //<script type="text/javascript">
47002
47003 /*
47004  * Ext JS Library 1.1.1
47005  * Copyright(c) 2006-2007, Ext JS, LLC.
47006  * Licence LGPL
47007  * 
47008  */
47009  
47010  
47011 Roo.form.HtmlEditor = function(config){
47012     
47013     
47014     
47015     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
47016     
47017     if (!this.toolbars) {
47018         this.toolbars = [];
47019     }
47020     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
47021     
47022     
47023 };
47024
47025 /**
47026  * @class Roo.form.HtmlEditor
47027  * @extends Roo.form.Field
47028  * Provides a lightweight HTML Editor component.
47029  *
47030  * This has been tested on Fireforx / Chrome.. IE may not be so great..
47031  * 
47032  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
47033  * supported by this editor.</b><br/><br/>
47034  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
47035  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47036  */
47037 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
47038     /**
47039      * @cfg {Boolean} clearUp
47040      */
47041     clearUp : true,
47042       /**
47043      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
47044      */
47045     toolbars : false,
47046    
47047      /**
47048      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47049      *                        Roo.resizable.
47050      */
47051     resizable : false,
47052      /**
47053      * @cfg {Number} height (in pixels)
47054      */   
47055     height: 300,
47056    /**
47057      * @cfg {Number} width (in pixels)
47058      */   
47059     width: 500,
47060     
47061     /**
47062      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
47063      * 
47064      */
47065     stylesheets: false,
47066     
47067     
47068      /**
47069      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
47070      * 
47071      */
47072     cblack: false,
47073     /**
47074      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
47075      * 
47076      */
47077     cwhite: false,
47078     
47079      /**
47080      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
47081      * 
47082      */
47083     black: false,
47084     /**
47085      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
47086      * 
47087      */
47088     white: false,
47089     /**
47090      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47091      */
47092     allowComments: false,
47093     /**
47094      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
47095      */
47096     
47097     
47098      bodyCls : '',
47099     
47100     // id of frame..
47101     frameId: false,
47102     
47103     // private properties
47104     validationEvent : false,
47105     deferHeight: true,
47106     initialized : false,
47107     activated : false,
47108     
47109     onFocus : Roo.emptyFn,
47110     iframePad:3,
47111     hideMode:'offsets',
47112     
47113     actionMode : 'container', // defaults to hiding it...
47114     
47115     defaultAutoCreate : { // modified by initCompnoent..
47116         tag: "textarea",
47117         style:"width:500px;height:300px;",
47118         autocomplete: "new-password"
47119     },
47120
47121     // private
47122     initComponent : function(){
47123         this.addEvents({
47124             /**
47125              * @event initialize
47126              * Fires when the editor is fully initialized (including the iframe)
47127              * @param {HtmlEditor} this
47128              */
47129             initialize: true,
47130             /**
47131              * @event activate
47132              * Fires when the editor is first receives the focus. Any insertion must wait
47133              * until after this event.
47134              * @param {HtmlEditor} this
47135              */
47136             activate: true,
47137              /**
47138              * @event beforesync
47139              * Fires before the textarea is updated with content from the editor iframe. Return false
47140              * to cancel the sync.
47141              * @param {HtmlEditor} this
47142              * @param {String} html
47143              */
47144             beforesync: true,
47145              /**
47146              * @event beforepush
47147              * Fires before the iframe editor is updated with content from the textarea. Return false
47148              * to cancel the push.
47149              * @param {HtmlEditor} this
47150              * @param {String} html
47151              */
47152             beforepush: true,
47153              /**
47154              * @event sync
47155              * Fires when the textarea is updated with content from the editor iframe.
47156              * @param {HtmlEditor} this
47157              * @param {String} html
47158              */
47159             sync: true,
47160              /**
47161              * @event push
47162              * Fires when the iframe editor is updated with content from the textarea.
47163              * @param {HtmlEditor} this
47164              * @param {String} html
47165              */
47166             push: true,
47167              /**
47168              * @event editmodechange
47169              * Fires when the editor switches edit modes
47170              * @param {HtmlEditor} this
47171              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
47172              */
47173             editmodechange: true,
47174             /**
47175              * @event editorevent
47176              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47177              * @param {HtmlEditor} this
47178              */
47179             editorevent: true,
47180             /**
47181              * @event firstfocus
47182              * Fires when on first focus - needed by toolbars..
47183              * @param {HtmlEditor} this
47184              */
47185             firstfocus: true,
47186             /**
47187              * @event autosave
47188              * Auto save the htmlEditor value as a file into Events
47189              * @param {HtmlEditor} this
47190              */
47191             autosave: true,
47192             /**
47193              * @event savedpreview
47194              * preview the saved version of htmlEditor
47195              * @param {HtmlEditor} this
47196              */
47197             savedpreview: true,
47198             
47199             /**
47200             * @event stylesheetsclick
47201             * Fires when press the Sytlesheets button
47202             * @param {Roo.HtmlEditorCore} this
47203             */
47204             stylesheetsclick: true,
47205             /**
47206             * @event paste
47207             * Fires when press user pastes into the editor
47208             * @param {Roo.HtmlEditorCore} this
47209             */
47210             paste: true 
47211         });
47212         this.defaultAutoCreate =  {
47213             tag: "textarea",
47214             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
47215             autocomplete: "new-password"
47216         };
47217     },
47218
47219     /**
47220      * Protected method that will not generally be called directly. It
47221      * is called when the editor creates its toolbar. Override this method if you need to
47222      * add custom toolbar buttons.
47223      * @param {HtmlEditor} editor
47224      */
47225     createToolbar : function(editor){
47226         Roo.log("create toolbars");
47227         if (!editor.toolbars || !editor.toolbars.length) {
47228             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
47229         }
47230         
47231         for (var i =0 ; i < editor.toolbars.length;i++) {
47232             editor.toolbars[i] = Roo.factory(
47233                     typeof(editor.toolbars[i]) == 'string' ?
47234                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
47235                 Roo.form.HtmlEditor);
47236             editor.toolbars[i].init(editor);
47237         }
47238          
47239         
47240     },
47241
47242      
47243     // private
47244     onRender : function(ct, position)
47245     {
47246         var _t = this;
47247         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
47248         
47249         this.wrap = this.el.wrap({
47250             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
47251         });
47252         
47253         this.editorcore.onRender(ct, position);
47254          
47255         if (this.resizable) {
47256             this.resizeEl = new Roo.Resizable(this.wrap, {
47257                 pinned : true,
47258                 wrap: true,
47259                 dynamic : true,
47260                 minHeight : this.height,
47261                 height: this.height,
47262                 handles : this.resizable,
47263                 width: this.width,
47264                 listeners : {
47265                     resize : function(r, w, h) {
47266                         _t.onResize(w,h); // -something
47267                     }
47268                 }
47269             });
47270             
47271         }
47272         this.createToolbar(this);
47273        
47274         
47275         if(!this.width){
47276             this.setSize(this.wrap.getSize());
47277         }
47278         if (this.resizeEl) {
47279             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
47280             // should trigger onReize..
47281         }
47282         
47283         this.keyNav = new Roo.KeyNav(this.el, {
47284             
47285             "tab" : function(e){
47286                 e.preventDefault();
47287                 
47288                 var value = this.getValue();
47289                 
47290                 var start = this.el.dom.selectionStart;
47291                 var end = this.el.dom.selectionEnd;
47292                 
47293                 if(!e.shiftKey){
47294                     
47295                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
47296                     this.el.dom.setSelectionRange(end + 1, end + 1);
47297                     return;
47298                 }
47299                 
47300                 var f = value.substring(0, start).split("\t");
47301                 
47302                 if(f.pop().length != 0){
47303                     return;
47304                 }
47305                 
47306                 this.setValue(f.join("\t") + value.substring(end));
47307                 this.el.dom.setSelectionRange(start - 1, start - 1);
47308                 
47309             },
47310             
47311             "home" : function(e){
47312                 e.preventDefault();
47313                 
47314                 var curr = this.el.dom.selectionStart;
47315                 var lines = this.getValue().split("\n");
47316                 
47317                 if(!lines.length){
47318                     return;
47319                 }
47320                 
47321                 if(e.ctrlKey){
47322                     this.el.dom.setSelectionRange(0, 0);
47323                     return;
47324                 }
47325                 
47326                 var pos = 0;
47327                 
47328                 for (var i = 0; i < lines.length;i++) {
47329                     pos += lines[i].length;
47330                     
47331                     if(i != 0){
47332                         pos += 1;
47333                     }
47334                     
47335                     if(pos < curr){
47336                         continue;
47337                     }
47338                     
47339                     pos -= lines[i].length;
47340                     
47341                     break;
47342                 }
47343                 
47344                 if(!e.shiftKey){
47345                     this.el.dom.setSelectionRange(pos, pos);
47346                     return;
47347                 }
47348                 
47349                 this.el.dom.selectionStart = pos;
47350                 this.el.dom.selectionEnd = curr;
47351             },
47352             
47353             "end" : function(e){
47354                 e.preventDefault();
47355                 
47356                 var curr = this.el.dom.selectionStart;
47357                 var lines = this.getValue().split("\n");
47358                 
47359                 if(!lines.length){
47360                     return;
47361                 }
47362                 
47363                 if(e.ctrlKey){
47364                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
47365                     return;
47366                 }
47367                 
47368                 var pos = 0;
47369                 
47370                 for (var i = 0; i < lines.length;i++) {
47371                     
47372                     pos += lines[i].length;
47373                     
47374                     if(i != 0){
47375                         pos += 1;
47376                     }
47377                     
47378                     if(pos < curr){
47379                         continue;
47380                     }
47381                     
47382                     break;
47383                 }
47384                 
47385                 if(!e.shiftKey){
47386                     this.el.dom.setSelectionRange(pos, pos);
47387                     return;
47388                 }
47389                 
47390                 this.el.dom.selectionStart = curr;
47391                 this.el.dom.selectionEnd = pos;
47392             },
47393
47394             scope : this,
47395
47396             doRelay : function(foo, bar, hname){
47397                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
47398             },
47399
47400             forceKeyDown: true
47401         });
47402         
47403 //        if(this.autosave && this.w){
47404 //            this.autoSaveFn = setInterval(this.autosave, 1000);
47405 //        }
47406     },
47407
47408     // private
47409     onResize : function(w, h)
47410     {
47411         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
47412         var ew = false;
47413         var eh = false;
47414         
47415         if(this.el ){
47416             if(typeof w == 'number'){
47417                 var aw = w - this.wrap.getFrameWidth('lr');
47418                 this.el.setWidth(this.adjustWidth('textarea', aw));
47419                 ew = aw;
47420             }
47421             if(typeof h == 'number'){
47422                 var tbh = 0;
47423                 for (var i =0; i < this.toolbars.length;i++) {
47424                     // fixme - ask toolbars for heights?
47425                     tbh += this.toolbars[i].tb.el.getHeight();
47426                     if (this.toolbars[i].footer) {
47427                         tbh += this.toolbars[i].footer.el.getHeight();
47428                     }
47429                 }
47430                 
47431                 
47432                 
47433                 
47434                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
47435                 ah -= 5; // knock a few pixes off for look..
47436 //                Roo.log(ah);
47437                 this.el.setHeight(this.adjustWidth('textarea', ah));
47438                 var eh = ah;
47439             }
47440         }
47441         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
47442         this.editorcore.onResize(ew,eh);
47443         
47444     },
47445
47446     /**
47447      * Toggles the editor between standard and source edit mode.
47448      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
47449      */
47450     toggleSourceEdit : function(sourceEditMode)
47451     {
47452         this.editorcore.toggleSourceEdit(sourceEditMode);
47453         
47454         if(this.editorcore.sourceEditMode){
47455             Roo.log('editor - showing textarea');
47456             
47457 //            Roo.log('in');
47458 //            Roo.log(this.syncValue());
47459             this.editorcore.syncValue();
47460             this.el.removeClass('x-hidden');
47461             this.el.dom.removeAttribute('tabIndex');
47462             this.el.focus();
47463             this.el.dom.scrollTop = 0;
47464             
47465             
47466             for (var i = 0; i < this.toolbars.length; i++) {
47467                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
47468                     this.toolbars[i].tb.hide();
47469                     this.toolbars[i].footer.hide();
47470                 }
47471             }
47472             
47473         }else{
47474             Roo.log('editor - hiding textarea');
47475 //            Roo.log('out')
47476 //            Roo.log(this.pushValue()); 
47477             this.editorcore.pushValue();
47478             
47479             this.el.addClass('x-hidden');
47480             this.el.dom.setAttribute('tabIndex', -1);
47481             
47482             for (var i = 0; i < this.toolbars.length; i++) {
47483                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
47484                     this.toolbars[i].tb.show();
47485                     this.toolbars[i].footer.show();
47486                 }
47487             }
47488             
47489             //this.deferFocus();
47490         }
47491         
47492         this.setSize(this.wrap.getSize());
47493         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
47494         
47495         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
47496     },
47497  
47498     // private (for BoxComponent)
47499     adjustSize : Roo.BoxComponent.prototype.adjustSize,
47500
47501     // private (for BoxComponent)
47502     getResizeEl : function(){
47503         return this.wrap;
47504     },
47505
47506     // private (for BoxComponent)
47507     getPositionEl : function(){
47508         return this.wrap;
47509     },
47510
47511     // private
47512     initEvents : function(){
47513         this.originalValue = this.getValue();
47514     },
47515
47516     /**
47517      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47518      * @method
47519      */
47520     markInvalid : Roo.emptyFn,
47521     /**
47522      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
47523      * @method
47524      */
47525     clearInvalid : Roo.emptyFn,
47526
47527     setValue : function(v){
47528         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
47529         this.editorcore.pushValue();
47530     },
47531
47532      
47533     // private
47534     deferFocus : function(){
47535         this.focus.defer(10, this);
47536     },
47537
47538     // doc'ed in Field
47539     focus : function(){
47540         this.editorcore.focus();
47541         
47542     },
47543       
47544
47545     // private
47546     onDestroy : function(){
47547         
47548         
47549         
47550         if(this.rendered){
47551             
47552             for (var i =0; i < this.toolbars.length;i++) {
47553                 // fixme - ask toolbars for heights?
47554                 this.toolbars[i].onDestroy();
47555             }
47556             
47557             this.wrap.dom.innerHTML = '';
47558             this.wrap.remove();
47559         }
47560     },
47561
47562     // private
47563     onFirstFocus : function(){
47564         //Roo.log("onFirstFocus");
47565         this.editorcore.onFirstFocus();
47566          for (var i =0; i < this.toolbars.length;i++) {
47567             this.toolbars[i].onFirstFocus();
47568         }
47569         
47570     },
47571     
47572     // private
47573     syncValue : function()
47574     {
47575         this.editorcore.syncValue();
47576     },
47577     
47578     pushValue : function()
47579     {
47580         this.editorcore.pushValue();
47581     },
47582     
47583     setStylesheets : function(stylesheets)
47584     {
47585         this.editorcore.setStylesheets(stylesheets);
47586     },
47587     
47588     removeStylesheets : function()
47589     {
47590         this.editorcore.removeStylesheets();
47591     }
47592      
47593     
47594     // hide stuff that is not compatible
47595     /**
47596      * @event blur
47597      * @hide
47598      */
47599     /**
47600      * @event change
47601      * @hide
47602      */
47603     /**
47604      * @event focus
47605      * @hide
47606      */
47607     /**
47608      * @event specialkey
47609      * @hide
47610      */
47611     /**
47612      * @cfg {String} fieldClass @hide
47613      */
47614     /**
47615      * @cfg {String} focusClass @hide
47616      */
47617     /**
47618      * @cfg {String} autoCreate @hide
47619      */
47620     /**
47621      * @cfg {String} inputType @hide
47622      */
47623     /**
47624      * @cfg {String} invalidClass @hide
47625      */
47626     /**
47627      * @cfg {String} invalidText @hide
47628      */
47629     /**
47630      * @cfg {String} msgFx @hide
47631      */
47632     /**
47633      * @cfg {String} validateOnBlur @hide
47634      */
47635 });
47636  
47637     // <script type="text/javascript">
47638 /*
47639  * Based on
47640  * Ext JS Library 1.1.1
47641  * Copyright(c) 2006-2007, Ext JS, LLC.
47642  *  
47643  
47644  */
47645
47646 /**
47647  * @class Roo.form.HtmlEditorToolbar1
47648  * Basic Toolbar
47649  * 
47650  * Usage:
47651  *
47652  new Roo.form.HtmlEditor({
47653     ....
47654     toolbars : [
47655         new Roo.form.HtmlEditorToolbar1({
47656             disable : { fonts: 1 , format: 1, ..., ... , ...],
47657             btns : [ .... ]
47658         })
47659     }
47660      
47661  * 
47662  * @cfg {Object} disable List of elements to disable..
47663  * @cfg {Array} btns List of additional buttons.
47664  * 
47665  * 
47666  * NEEDS Extra CSS? 
47667  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
47668  */
47669  
47670 Roo.form.HtmlEditor.ToolbarStandard = function(config)
47671 {
47672     
47673     Roo.apply(this, config);
47674     
47675     // default disabled, based on 'good practice'..
47676     this.disable = this.disable || {};
47677     Roo.applyIf(this.disable, {
47678         fontSize : true,
47679         colors : true,
47680         specialElements : true
47681     });
47682     
47683     
47684     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
47685     // dont call parent... till later.
47686 }
47687
47688 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
47689     
47690     tb: false,
47691     
47692     rendered: false,
47693     
47694     editor : false,
47695     editorcore : false,
47696     /**
47697      * @cfg {Object} disable  List of toolbar elements to disable
47698          
47699      */
47700     disable : false,
47701     
47702     
47703      /**
47704      * @cfg {String} createLinkText The default text for the create link prompt
47705      */
47706     createLinkText : 'Please enter the URL for the link:',
47707     /**
47708      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
47709      */
47710     defaultLinkValue : 'http:/'+'/',
47711    
47712     
47713       /**
47714      * @cfg {Array} fontFamilies An array of available font families
47715      */
47716     fontFamilies : [
47717         'Arial',
47718         'Courier New',
47719         'Tahoma',
47720         'Times New Roman',
47721         'Verdana'
47722     ],
47723     
47724     specialChars : [
47725            "&#169;",
47726           "&#174;",     
47727           "&#8482;",    
47728           "&#163;" ,    
47729          // "&#8212;",    
47730           "&#8230;",    
47731           "&#247;" ,    
47732         //  "&#225;" ,     ?? a acute?
47733            "&#8364;"    , //Euro
47734        //   "&#8220;"    ,
47735         //  "&#8221;"    ,
47736         //  "&#8226;"    ,
47737           "&#176;"  //   , // degrees
47738
47739          // "&#233;"     , // e ecute
47740          // "&#250;"     , // u ecute?
47741     ],
47742     
47743     specialElements : [
47744         {
47745             text: "Insert Table",
47746             xtype: 'MenuItem',
47747             xns : Roo.Menu,
47748             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
47749                 
47750         },
47751         {    
47752             text: "Insert Image",
47753             xtype: 'MenuItem',
47754             xns : Roo.Menu,
47755             ihtml : '<img src="about:blank"/>'
47756             
47757         }
47758         
47759          
47760     ],
47761     
47762     
47763     inputElements : [ 
47764             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
47765             "input:submit", "input:button", "select", "textarea", "label" ],
47766     formats : [
47767         ["p"] ,  
47768         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
47769         ["pre"],[ "code"], 
47770         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
47771         ['div'],['span'],
47772         ['sup'],['sub']
47773     ],
47774     
47775     cleanStyles : [
47776         "font-size"
47777     ],
47778      /**
47779      * @cfg {String} defaultFont default font to use.
47780      */
47781     defaultFont: 'tahoma',
47782    
47783     fontSelect : false,
47784     
47785     
47786     formatCombo : false,
47787     
47788     init : function(editor)
47789     {
47790         this.editor = editor;
47791         this.editorcore = editor.editorcore ? editor.editorcore : editor;
47792         var editorcore = this.editorcore;
47793         
47794         var _t = this;
47795         
47796         var fid = editorcore.frameId;
47797         var etb = this;
47798         function btn(id, toggle, handler){
47799             var xid = fid + '-'+ id ;
47800             return {
47801                 id : xid,
47802                 cmd : id,
47803                 cls : 'x-btn-icon x-edit-'+id,
47804                 enableToggle:toggle !== false,
47805                 scope: _t, // was editor...
47806                 handler:handler||_t.relayBtnCmd,
47807                 clickEvent:'mousedown',
47808                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
47809                 tabIndex:-1
47810             };
47811         }
47812         
47813         
47814         
47815         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
47816         this.tb = tb;
47817          // stop form submits
47818         tb.el.on('click', function(e){
47819             e.preventDefault(); // what does this do?
47820         });
47821
47822         if(!this.disable.font) { // && !Roo.isSafari){
47823             /* why no safari for fonts 
47824             editor.fontSelect = tb.el.createChild({
47825                 tag:'select',
47826                 tabIndex: -1,
47827                 cls:'x-font-select',
47828                 html: this.createFontOptions()
47829             });
47830             
47831             editor.fontSelect.on('change', function(){
47832                 var font = editor.fontSelect.dom.value;
47833                 editor.relayCmd('fontname', font);
47834                 editor.deferFocus();
47835             }, editor);
47836             
47837             tb.add(
47838                 editor.fontSelect.dom,
47839                 '-'
47840             );
47841             */
47842             
47843         };
47844         if(!this.disable.formats){
47845             this.formatCombo = new Roo.form.ComboBox({
47846                 store: new Roo.data.SimpleStore({
47847                     id : 'tag',
47848                     fields: ['tag'],
47849                     data : this.formats // from states.js
47850                 }),
47851                 blockFocus : true,
47852                 name : '',
47853                 //autoCreate : {tag: "div",  size: "20"},
47854                 displayField:'tag',
47855                 typeAhead: false,
47856                 mode: 'local',
47857                 editable : false,
47858                 triggerAction: 'all',
47859                 emptyText:'Add tag',
47860                 selectOnFocus:true,
47861                 width:135,
47862                 listeners : {
47863                     'select': function(c, r, i) {
47864                         editorcore.insertTag(r.get('tag'));
47865                         editor.focus();
47866                     }
47867                 }
47868
47869             });
47870             tb.addField(this.formatCombo);
47871             
47872         }
47873         
47874         if(!this.disable.format){
47875             tb.add(
47876                 btn('bold'),
47877                 btn('italic'),
47878                 btn('underline'),
47879                 btn('strikethrough')
47880             );
47881         };
47882         if(!this.disable.fontSize){
47883             tb.add(
47884                 '-',
47885                 
47886                 
47887                 btn('increasefontsize', false, editorcore.adjustFont),
47888                 btn('decreasefontsize', false, editorcore.adjustFont)
47889             );
47890         };
47891         
47892         
47893         if(!this.disable.colors){
47894             tb.add(
47895                 '-', {
47896                     id:editorcore.frameId +'-forecolor',
47897                     cls:'x-btn-icon x-edit-forecolor',
47898                     clickEvent:'mousedown',
47899                     tooltip: this.buttonTips['forecolor'] || undefined,
47900                     tabIndex:-1,
47901                     menu : new Roo.menu.ColorMenu({
47902                         allowReselect: true,
47903                         focus: Roo.emptyFn,
47904                         value:'000000',
47905                         plain:true,
47906                         selectHandler: function(cp, color){
47907                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
47908                             editor.deferFocus();
47909                         },
47910                         scope: editorcore,
47911                         clickEvent:'mousedown'
47912                     })
47913                 }, {
47914                     id:editorcore.frameId +'backcolor',
47915                     cls:'x-btn-icon x-edit-backcolor',
47916                     clickEvent:'mousedown',
47917                     tooltip: this.buttonTips['backcolor'] || undefined,
47918                     tabIndex:-1,
47919                     menu : new Roo.menu.ColorMenu({
47920                         focus: Roo.emptyFn,
47921                         value:'FFFFFF',
47922                         plain:true,
47923                         allowReselect: true,
47924                         selectHandler: function(cp, color){
47925                             if(Roo.isGecko){
47926                                 editorcore.execCmd('useCSS', false);
47927                                 editorcore.execCmd('hilitecolor', color);
47928                                 editorcore.execCmd('useCSS', true);
47929                                 editor.deferFocus();
47930                             }else{
47931                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
47932                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
47933                                 editor.deferFocus();
47934                             }
47935                         },
47936                         scope:editorcore,
47937                         clickEvent:'mousedown'
47938                     })
47939                 }
47940             );
47941         };
47942         // now add all the items...
47943         
47944
47945         if(!this.disable.alignments){
47946             tb.add(
47947                 '-',
47948                 btn('justifyleft'),
47949                 btn('justifycenter'),
47950                 btn('justifyright')
47951             );
47952         };
47953
47954         //if(!Roo.isSafari){
47955             if(!this.disable.links){
47956                 tb.add(
47957                     '-',
47958                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
47959                 );
47960             };
47961
47962             if(!this.disable.lists){
47963                 tb.add(
47964                     '-',
47965                     btn('insertorderedlist'),
47966                     btn('insertunorderedlist')
47967                 );
47968             }
47969             if(!this.disable.sourceEdit){
47970                 tb.add(
47971                     '-',
47972                     btn('sourceedit', true, function(btn){
47973                         this.toggleSourceEdit(btn.pressed);
47974                     })
47975                 );
47976             }
47977         //}
47978         
47979         var smenu = { };
47980         // special menu.. - needs to be tidied up..
47981         if (!this.disable.special) {
47982             smenu = {
47983                 text: "&#169;",
47984                 cls: 'x-edit-none',
47985                 
47986                 menu : {
47987                     items : []
47988                 }
47989             };
47990             for (var i =0; i < this.specialChars.length; i++) {
47991                 smenu.menu.items.push({
47992                     
47993                     html: this.specialChars[i],
47994                     handler: function(a,b) {
47995                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
47996                         //editor.insertAtCursor(a.html);
47997                         
47998                     },
47999                     tabIndex:-1
48000                 });
48001             }
48002             
48003             
48004             tb.add(smenu);
48005             
48006             
48007         }
48008         
48009         var cmenu = { };
48010         if (!this.disable.cleanStyles) {
48011             cmenu = {
48012                 cls: 'x-btn-icon x-btn-clear',
48013                 
48014                 menu : {
48015                     items : []
48016                 }
48017             };
48018             for (var i =0; i < this.cleanStyles.length; i++) {
48019                 cmenu.menu.items.push({
48020                     actiontype : this.cleanStyles[i],
48021                     html: 'Remove ' + this.cleanStyles[i],
48022                     handler: function(a,b) {
48023 //                        Roo.log(a);
48024 //                        Roo.log(b);
48025                         var c = Roo.get(editorcore.doc.body);
48026                         c.select('[style]').each(function(s) {
48027                             s.dom.style.removeProperty(a.actiontype);
48028                         });
48029                         editorcore.syncValue();
48030                     },
48031                     tabIndex:-1
48032                 });
48033             }
48034             cmenu.menu.items.push({
48035                 actiontype : 'tablewidths',
48036                 html: 'Remove Table Widths',
48037                 handler: function(a,b) {
48038                     editorcore.cleanTableWidths();
48039                     editorcore.syncValue();
48040                 },
48041                 tabIndex:-1
48042             });
48043             cmenu.menu.items.push({
48044                 actiontype : 'word',
48045                 html: 'Remove MS Word Formating',
48046                 handler: function(a,b) {
48047                     editorcore.cleanWord();
48048                     editorcore.syncValue();
48049                 },
48050                 tabIndex:-1
48051             });
48052             
48053             cmenu.menu.items.push({
48054                 actiontype : 'all',
48055                 html: 'Remove All Styles',
48056                 handler: function(a,b) {
48057                     
48058                     var c = Roo.get(editorcore.doc.body);
48059                     c.select('[style]').each(function(s) {
48060                         s.dom.removeAttribute('style');
48061                     });
48062                     editorcore.syncValue();
48063                 },
48064                 tabIndex:-1
48065             });
48066             
48067             cmenu.menu.items.push({
48068                 actiontype : 'all',
48069                 html: 'Remove All CSS Classes',
48070                 handler: function(a,b) {
48071                     
48072                     var c = Roo.get(editorcore.doc.body);
48073                     c.select('[class]').each(function(s) {
48074                         s.dom.removeAttribute('class');
48075                     });
48076                     editorcore.cleanWord();
48077                     editorcore.syncValue();
48078                 },
48079                 tabIndex:-1
48080             });
48081             
48082              cmenu.menu.items.push({
48083                 actiontype : 'tidy',
48084                 html: 'Tidy HTML Source',
48085                 handler: function(a,b) {
48086                     new Roo.htmleditor.Tidy(editorcore.doc.body);
48087                     editorcore.syncValue();
48088                 },
48089                 tabIndex:-1
48090             });
48091             
48092             
48093             tb.add(cmenu);
48094         }
48095          
48096         if (!this.disable.specialElements) {
48097             var semenu = {
48098                 text: "Other;",
48099                 cls: 'x-edit-none',
48100                 menu : {
48101                     items : []
48102                 }
48103             };
48104             for (var i =0; i < this.specialElements.length; i++) {
48105                 semenu.menu.items.push(
48106                     Roo.apply({ 
48107                         handler: function(a,b) {
48108                             editor.insertAtCursor(this.ihtml);
48109                         }
48110                     }, this.specialElements[i])
48111                 );
48112                     
48113             }
48114             
48115             tb.add(semenu);
48116             
48117             
48118         }
48119          
48120         
48121         if (this.btns) {
48122             for(var i =0; i< this.btns.length;i++) {
48123                 var b = Roo.factory(this.btns[i],Roo.form);
48124                 b.cls =  'x-edit-none';
48125                 
48126                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
48127                     b.cls += ' x-init-enable';
48128                 }
48129                 
48130                 b.scope = editorcore;
48131                 tb.add(b);
48132             }
48133         
48134         }
48135         
48136         
48137         
48138         // disable everything...
48139         
48140         this.tb.items.each(function(item){
48141             
48142            if(
48143                 item.id != editorcore.frameId+ '-sourceedit' && 
48144                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
48145             ){
48146                 
48147                 item.disable();
48148             }
48149         });
48150         this.rendered = true;
48151         
48152         // the all the btns;
48153         editor.on('editorevent', this.updateToolbar, this);
48154         // other toolbars need to implement this..
48155         //editor.on('editmodechange', this.updateToolbar, this);
48156     },
48157     
48158     
48159     relayBtnCmd : function(btn) {
48160         this.editorcore.relayCmd(btn.cmd);
48161     },
48162     // private used internally
48163     createLink : function(){
48164         Roo.log("create link?");
48165         var url = prompt(this.createLinkText, this.defaultLinkValue);
48166         if(url && url != 'http:/'+'/'){
48167             this.editorcore.relayCmd('createlink', url);
48168         }
48169     },
48170
48171     
48172     /**
48173      * Protected method that will not generally be called directly. It triggers
48174      * a toolbar update by reading the markup state of the current selection in the editor.
48175      */
48176     updateToolbar: function(){
48177
48178         if(!this.editorcore.activated){
48179             this.editor.onFirstFocus();
48180             return;
48181         }
48182
48183         var btns = this.tb.items.map, 
48184             doc = this.editorcore.doc,
48185             frameId = this.editorcore.frameId;
48186
48187         if(!this.disable.font && !Roo.isSafari){
48188             /*
48189             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
48190             if(name != this.fontSelect.dom.value){
48191                 this.fontSelect.dom.value = name;
48192             }
48193             */
48194         }
48195         if(!this.disable.format){
48196             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
48197             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
48198             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
48199             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
48200         }
48201         if(!this.disable.alignments){
48202             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
48203             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
48204             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
48205         }
48206         if(!Roo.isSafari && !this.disable.lists){
48207             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
48208             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
48209         }
48210         
48211         var ans = this.editorcore.getAllAncestors();
48212         if (this.formatCombo) {
48213             
48214             
48215             var store = this.formatCombo.store;
48216             this.formatCombo.setValue("");
48217             for (var i =0; i < ans.length;i++) {
48218                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
48219                     // select it..
48220                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
48221                     break;
48222                 }
48223             }
48224         }
48225         
48226         
48227         
48228         // hides menus... - so this cant be on a menu...
48229         Roo.menu.MenuMgr.hideAll();
48230
48231         //this.editorsyncValue();
48232     },
48233    
48234     
48235     createFontOptions : function(){
48236         var buf = [], fs = this.fontFamilies, ff, lc;
48237         
48238         
48239         
48240         for(var i = 0, len = fs.length; i< len; i++){
48241             ff = fs[i];
48242             lc = ff.toLowerCase();
48243             buf.push(
48244                 '<option value="',lc,'" style="font-family:',ff,';"',
48245                     (this.defaultFont == lc ? ' selected="true">' : '>'),
48246                     ff,
48247                 '</option>'
48248             );
48249         }
48250         return buf.join('');
48251     },
48252     
48253     toggleSourceEdit : function(sourceEditMode){
48254         
48255         Roo.log("toolbar toogle");
48256         if(sourceEditMode === undefined){
48257             sourceEditMode = !this.sourceEditMode;
48258         }
48259         this.sourceEditMode = sourceEditMode === true;
48260         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
48261         // just toggle the button?
48262         if(btn.pressed !== this.sourceEditMode){
48263             btn.toggle(this.sourceEditMode);
48264             return;
48265         }
48266         
48267         if(sourceEditMode){
48268             Roo.log("disabling buttons");
48269             this.tb.items.each(function(item){
48270                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
48271                     item.disable();
48272                 }
48273             });
48274           
48275         }else{
48276             Roo.log("enabling buttons");
48277             if(this.editorcore.initialized){
48278                 this.tb.items.each(function(item){
48279                     item.enable();
48280                 });
48281             }
48282             
48283         }
48284         Roo.log("calling toggole on editor");
48285         // tell the editor that it's been pressed..
48286         this.editor.toggleSourceEdit(sourceEditMode);
48287        
48288     },
48289      /**
48290      * Object collection of toolbar tooltips for the buttons in the editor. The key
48291      * is the command id associated with that button and the value is a valid QuickTips object.
48292      * For example:
48293 <pre><code>
48294 {
48295     bold : {
48296         title: 'Bold (Ctrl+B)',
48297         text: 'Make the selected text bold.',
48298         cls: 'x-html-editor-tip'
48299     },
48300     italic : {
48301         title: 'Italic (Ctrl+I)',
48302         text: 'Make the selected text italic.',
48303         cls: 'x-html-editor-tip'
48304     },
48305     ...
48306 </code></pre>
48307     * @type Object
48308      */
48309     buttonTips : {
48310         bold : {
48311             title: 'Bold (Ctrl+B)',
48312             text: 'Make the selected text bold.',
48313             cls: 'x-html-editor-tip'
48314         },
48315         italic : {
48316             title: 'Italic (Ctrl+I)',
48317             text: 'Make the selected text italic.',
48318             cls: 'x-html-editor-tip'
48319         },
48320         underline : {
48321             title: 'Underline (Ctrl+U)',
48322             text: 'Underline the selected text.',
48323             cls: 'x-html-editor-tip'
48324         },
48325         strikethrough : {
48326             title: 'Strikethrough',
48327             text: 'Strikethrough the selected text.',
48328             cls: 'x-html-editor-tip'
48329         },
48330         increasefontsize : {
48331             title: 'Grow Text',
48332             text: 'Increase the font size.',
48333             cls: 'x-html-editor-tip'
48334         },
48335         decreasefontsize : {
48336             title: 'Shrink Text',
48337             text: 'Decrease the font size.',
48338             cls: 'x-html-editor-tip'
48339         },
48340         backcolor : {
48341             title: 'Text Highlight Color',
48342             text: 'Change the background color of the selected text.',
48343             cls: 'x-html-editor-tip'
48344         },
48345         forecolor : {
48346             title: 'Font Color',
48347             text: 'Change the color of the selected text.',
48348             cls: 'x-html-editor-tip'
48349         },
48350         justifyleft : {
48351             title: 'Align Text Left',
48352             text: 'Align text to the left.',
48353             cls: 'x-html-editor-tip'
48354         },
48355         justifycenter : {
48356             title: 'Center Text',
48357             text: 'Center text in the editor.',
48358             cls: 'x-html-editor-tip'
48359         },
48360         justifyright : {
48361             title: 'Align Text Right',
48362             text: 'Align text to the right.',
48363             cls: 'x-html-editor-tip'
48364         },
48365         insertunorderedlist : {
48366             title: 'Bullet List',
48367             text: 'Start a bulleted list.',
48368             cls: 'x-html-editor-tip'
48369         },
48370         insertorderedlist : {
48371             title: 'Numbered List',
48372             text: 'Start a numbered list.',
48373             cls: 'x-html-editor-tip'
48374         },
48375         createlink : {
48376             title: 'Hyperlink',
48377             text: 'Make the selected text a hyperlink.',
48378             cls: 'x-html-editor-tip'
48379         },
48380         sourceedit : {
48381             title: 'Source Edit',
48382             text: 'Switch to source editing mode.',
48383             cls: 'x-html-editor-tip'
48384         }
48385     },
48386     // private
48387     onDestroy : function(){
48388         if(this.rendered){
48389             
48390             this.tb.items.each(function(item){
48391                 if(item.menu){
48392                     item.menu.removeAll();
48393                     if(item.menu.el){
48394                         item.menu.el.destroy();
48395                     }
48396                 }
48397                 item.destroy();
48398             });
48399              
48400         }
48401     },
48402     onFirstFocus: function() {
48403         this.tb.items.each(function(item){
48404            item.enable();
48405         });
48406     }
48407 });
48408
48409
48410
48411
48412 // <script type="text/javascript">
48413 /*
48414  * Based on
48415  * Ext JS Library 1.1.1
48416  * Copyright(c) 2006-2007, Ext JS, LLC.
48417  *  
48418  
48419  */
48420
48421  
48422 /**
48423  * @class Roo.form.HtmlEditor.ToolbarContext
48424  * Context Toolbar
48425  * 
48426  * Usage:
48427  *
48428  new Roo.form.HtmlEditor({
48429     ....
48430     toolbars : [
48431         { xtype: 'ToolbarStandard', styles : {} }
48432         { xtype: 'ToolbarContext', disable : {} }
48433     ]
48434 })
48435
48436      
48437  * 
48438  * @config : {Object} disable List of elements to disable.. (not done yet.)
48439  * @config : {Object} styles  Map of styles available.
48440  * 
48441  */
48442
48443 Roo.form.HtmlEditor.ToolbarContext = function(config)
48444 {
48445     
48446     Roo.apply(this, config);
48447     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48448     // dont call parent... till later.
48449     this.styles = this.styles || {};
48450 }
48451
48452  
48453
48454 Roo.form.HtmlEditor.ToolbarContext.types = {
48455     'IMG' : {
48456         width : {
48457             title: "Width",
48458             width: 40
48459         },
48460         height:  {
48461             title: "Height",
48462             width: 40
48463         },
48464         align: {
48465             title: "Align",
48466             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
48467             width : 80
48468             
48469         },
48470         border: {
48471             title: "Border",
48472             width: 40
48473         },
48474         alt: {
48475             title: "Alt",
48476             width: 120
48477         },
48478         src : {
48479             title: "Src",
48480             width: 220
48481         }
48482         
48483     },
48484     
48485     'FIGURE' : {
48486          align: {
48487             title: "Align",
48488             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
48489             width : 80  
48490         }
48491     },
48492     'A' : {
48493         name : {
48494             title: "Name",
48495             width: 50
48496         },
48497         target:  {
48498             title: "Target",
48499             width: 120
48500         },
48501         href:  {
48502             title: "Href",
48503             width: 220
48504         } // border?
48505         
48506     },
48507     'TABLE' : {
48508         rows : {
48509             title: "Rows",
48510             width: 20
48511         },
48512         cols : {
48513             title: "Cols",
48514             width: 20
48515         },
48516         width : {
48517             title: "Width",
48518             width: 40
48519         },
48520         height : {
48521             title: "Height",
48522             width: 40
48523         },
48524         border : {
48525             title: "Border",
48526             width: 20
48527         }
48528     },
48529     'TD' : {
48530         width : {
48531             title: "Width",
48532             width: 40
48533         },
48534         height : {
48535             title: "Height",
48536             width: 40
48537         },   
48538         align: {
48539             title: "Align",
48540             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
48541             width: 80
48542         },
48543         valign: {
48544             title: "Valign",
48545             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
48546             width: 80
48547         },
48548         colspan: {
48549             title: "Colspan",
48550             width: 20
48551             
48552         },
48553          'font-family'  : {
48554             title : "Font",
48555             style : 'fontFamily',
48556             displayField: 'display',
48557             optname : 'font-family',
48558             width: 140
48559         }
48560     },
48561     'INPUT' : {
48562         name : {
48563             title: "name",
48564             width: 120
48565         },
48566         value : {
48567             title: "Value",
48568             width: 120
48569         },
48570         width : {
48571             title: "Width",
48572             width: 40
48573         }
48574     },
48575     'LABEL' : {
48576         'for' : {
48577             title: "For",
48578             width: 120
48579         }
48580     },
48581     'TEXTAREA' : {
48582         name : {
48583             title: "name",
48584             width: 120
48585         },
48586         rows : {
48587             title: "Rows",
48588             width: 20
48589         },
48590         cols : {
48591             title: "Cols",
48592             width: 20
48593         }
48594     },
48595     'SELECT' : {
48596         name : {
48597             title: "name",
48598             width: 120
48599         },
48600         selectoptions : {
48601             title: "Options",
48602             width: 200
48603         }
48604     },
48605     
48606     // should we really allow this??
48607     // should this just be 
48608     'BODY' : {
48609         title : {
48610             title: "Title",
48611             width: 200,
48612             disabled : true
48613         }
48614     },
48615     /*
48616     'SPAN' : {
48617         'font-family'  : {
48618             title : "Font",
48619             style : 'fontFamily',
48620             displayField: 'display',
48621             optname : 'font-family',
48622             width: 140
48623         }
48624     },
48625     'DIV' : {
48626         'font-family'  : {
48627             title : "Font",
48628             style : 'fontFamily',
48629             displayField: 'display',
48630             optname : 'font-family',
48631             width: 140
48632         }
48633     },
48634      'P' : {
48635         'font-family'  : {
48636             title : "Font",
48637             style : 'fontFamily',
48638             displayField: 'display',
48639             optname : 'font-family',
48640             width: 140
48641         }
48642     },
48643     */
48644     '*' : {
48645         // empty..
48646     }
48647
48648 };
48649
48650 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
48651 Roo.form.HtmlEditor.ToolbarContext.stores = false;
48652
48653 Roo.form.HtmlEditor.ToolbarContext.options = {
48654         'font-family'  : [ 
48655                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
48656                 [ 'Courier New', 'Courier New'],
48657                 [ 'Tahoma', 'Tahoma'],
48658                 [ 'Times New Roman,serif', 'Times'],
48659                 [ 'Verdana','Verdana' ]
48660         ]
48661 };
48662
48663 // fixme - these need to be configurable..
48664  
48665
48666 //Roo.form.HtmlEditor.ToolbarContext.types
48667
48668
48669 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
48670     
48671     tb: false,
48672     
48673     rendered: false,
48674     
48675     editor : false,
48676     editorcore : false,
48677     /**
48678      * @cfg {Object} disable  List of toolbar elements to disable
48679          
48680      */
48681     disable : false,
48682     /**
48683      * @cfg {Object} styles List of styles 
48684      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
48685      *
48686      * These must be defined in the page, so they get rendered correctly..
48687      * .headline { }
48688      * TD.underline { }
48689      * 
48690      */
48691     styles : false,
48692     
48693     options: false,
48694     
48695     toolbars : false,
48696     
48697     init : function(editor)
48698     {
48699         this.editor = editor;
48700         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48701         var editorcore = this.editorcore;
48702         
48703         var fid = editorcore.frameId;
48704         var etb = this;
48705         function btn(id, toggle, handler){
48706             var xid = fid + '-'+ id ;
48707             return {
48708                 id : xid,
48709                 cmd : id,
48710                 cls : 'x-btn-icon x-edit-'+id,
48711                 enableToggle:toggle !== false,
48712                 scope: editorcore, // was editor...
48713                 handler:handler||editorcore.relayBtnCmd,
48714                 clickEvent:'mousedown',
48715                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48716                 tabIndex:-1
48717             };
48718         }
48719         // create a new element.
48720         var wdiv = editor.wrap.createChild({
48721                 tag: 'div'
48722             }, editor.wrap.dom.firstChild.nextSibling, true);
48723         
48724         // can we do this more than once??
48725         
48726          // stop form submits
48727       
48728  
48729         // disable everything...
48730         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
48731         this.toolbars = {};
48732            
48733         for (var i in  ty) {
48734           
48735             this.toolbars[i] = this.buildToolbar(ty[i],i);
48736         }
48737         this.tb = this.toolbars.BODY;
48738         this.tb.el.show();
48739         this.buildFooter();
48740         this.footer.show();
48741         editor.on('hide', function( ) { this.footer.hide() }, this);
48742         editor.on('show', function( ) { this.footer.show() }, this);
48743         
48744          
48745         this.rendered = true;
48746         
48747         // the all the btns;
48748         editor.on('editorevent', this.updateToolbar, this);
48749         // other toolbars need to implement this..
48750         //editor.on('editmodechange', this.updateToolbar, this);
48751     },
48752     
48753     
48754     
48755     /**
48756      * Protected method that will not generally be called directly. It triggers
48757      * a toolbar update by reading the markup state of the current selection in the editor.
48758      *
48759      * Note you can force an update by calling on('editorevent', scope, false)
48760      */
48761     updateToolbar: function(editor ,ev, sel){
48762
48763         //Roo.log(ev);
48764         // capture mouse up - this is handy for selecting images..
48765         // perhaps should go somewhere else...
48766         if(!this.editorcore.activated){
48767              this.editor.onFirstFocus();
48768             return;
48769         }
48770         
48771         
48772         
48773         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
48774         // selectNode - might want to handle IE?
48775         
48776         
48777         
48778         if (ev &&
48779             (ev.type == 'mouseup' || ev.type == 'click' ) &&
48780             ev.target && ev.target.tagName == 'IMG') {
48781             // they have click on an image...
48782             // let's see if we can change the selection...
48783             sel = ev.target;
48784             
48785             
48786             this.editorcore.selectNode(sel);
48787              
48788         }  
48789         
48790       
48791         //var updateFooter = sel ? false : true; 
48792         
48793         
48794         var ans = this.editorcore.getAllAncestors();
48795         
48796         // pick
48797         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
48798         
48799         if (!sel) { 
48800             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
48801             sel = sel ? sel : this.editorcore.doc.body;
48802             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
48803             
48804         }
48805         
48806         var tn = sel.tagName.toUpperCase();
48807         var lastSel = this.tb.selectedNode;
48808         this.tb.selectedNode = sel;
48809         var left_label = tn;
48810         
48811         // ok see if we are editing a block?
48812         
48813         var db = Roo.get(sel).findParent('[data-block]');
48814         var cepar = Roo.get(sel).findParent('[contenteditable=true]');
48815         if (db && cepar && cepar.tagName != 'BODY') {
48816             db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
48817         }
48818         var block = false;
48819         if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
48820             block = Roo.htmleditor.Block.factory(db);
48821             if (block) {
48822                 tn = 'BLOCK.' + db.getAttribute('data-block');
48823                 this.tb.selectedNode = db;
48824                 this.editorcore.selectNode(db);
48825                 if (typeof(this.toolbars[tn]) == 'undefined') {
48826                    this.toolbars[tn] = this.buildToolbar( block.context,tn ,block.friendly_name);
48827                 }
48828                 left_label = block.friendly_name;
48829                 ans = this.editorcore.getAllAncestors();
48830             }
48831             
48832                 
48833             
48834         }
48835         
48836         
48837         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
48838             return; // no change?
48839         }
48840         
48841         
48842           
48843         this.tb.el.hide();
48844         ///console.log("show: " + tn);
48845         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
48846         
48847         this.tb.el.show();
48848         // update name
48849         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
48850         
48851         
48852         // update attributes
48853         if (block) {
48854              
48855             this.tb.fields.each(function(e) {
48856                 e.setValue(block[e.attrname]);
48857             });
48858             
48859             
48860         } else  if (this.tb.fields && this.tb.selectedNode) {
48861             this.tb.fields.each( function(e) {
48862                 if (e.stylename) {
48863                     e.setValue(this.tb.selectedNode.style[e.stylename]);
48864                     return;
48865                 } 
48866                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
48867             }, this);
48868             this.updateToolbarStyles(this.tb.selectedNode);  
48869         }
48870         
48871         
48872        
48873         Roo.menu.MenuMgr.hideAll();
48874
48875         
48876         
48877     
48878         // update the footer
48879         //
48880         this.updateFooter(ans);
48881              
48882     },
48883     
48884     updateToolbarStyles : function(sel)
48885     {
48886          var hasStyles = false;
48887         for(var i in this.styles) {
48888             hasStyles = true;
48889             break;
48890         }
48891         
48892         // update styles
48893         if (hasStyles) { 
48894             var st = this.tb.fields.item(0);
48895             
48896             st.store.removeAll();
48897             var cn = sel.className.split(/\s+/);
48898             
48899             var avs = [];
48900             if (this.styles['*']) {
48901                 
48902                 Roo.each(this.styles['*'], function(v) {
48903                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48904                 });
48905             }
48906             if (this.styles[tn]) { 
48907                 Roo.each(this.styles[tn], function(v) {
48908                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
48909                 });
48910             }
48911             
48912             st.store.loadData(avs);
48913             st.collapse();
48914             st.setValue(cn);
48915         }
48916     },
48917     
48918      
48919     updateFooter : function(ans)
48920     {
48921         var html = '';
48922         if (ans === false) {
48923             this.footDisp.dom.innerHTML = '';
48924             return;
48925         }
48926         
48927         this.footerEls = ans.reverse();
48928         Roo.each(this.footerEls, function(a,i) {
48929             if (!a) { return; }
48930             html += html.length ? ' &gt; '  :  '';
48931             
48932             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
48933             
48934         });
48935        
48936         // 
48937         var sz = this.footDisp.up('td').getSize();
48938         this.footDisp.dom.style.width = (sz.width -10) + 'px';
48939         this.footDisp.dom.style.marginLeft = '5px';
48940         
48941         this.footDisp.dom.style.overflow = 'hidden';
48942         
48943         this.footDisp.dom.innerHTML = html;
48944             
48945         
48946     },
48947    
48948        
48949     // private
48950     onDestroy : function(){
48951         if(this.rendered){
48952             
48953             this.tb.items.each(function(item){
48954                 if(item.menu){
48955                     item.menu.removeAll();
48956                     if(item.menu.el){
48957                         item.menu.el.destroy();
48958                     }
48959                 }
48960                 item.destroy();
48961             });
48962              
48963         }
48964     },
48965     onFirstFocus: function() {
48966         // need to do this for all the toolbars..
48967         this.tb.items.each(function(item){
48968            item.enable();
48969         });
48970     },
48971     buildToolbar: function(tlist, nm, friendly_name)
48972     {
48973         var editor = this.editor;
48974         var editorcore = this.editorcore;
48975          // create a new element.
48976         var wdiv = editor.wrap.createChild({
48977                 tag: 'div'
48978             }, editor.wrap.dom.firstChild.nextSibling, true);
48979         
48980        
48981         var tb = new Roo.Toolbar(wdiv);
48982         tb.name = nm;
48983         
48984         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
48985         
48986         var styles = Array.from(this.styles);
48987         
48988         
48989         // styles...
48990         if (styles && styles.length) {
48991             
48992             // this needs a multi-select checkbox...
48993             tb.addField( new Roo.form.ComboBox({
48994                 store: new Roo.data.SimpleStore({
48995                     id : 'val',
48996                     fields: ['val', 'selected'],
48997                     data : [] 
48998                 }),
48999                 name : '-roo-edit-className',
49000                 attrname : 'className',
49001                 displayField: 'val',
49002                 typeAhead: false,
49003                 mode: 'local',
49004                 editable : false,
49005                 triggerAction: 'all',
49006                 emptyText:'Select Style',
49007                 selectOnFocus:true,
49008                 width: 130,
49009                 listeners : {
49010                     'select': function(c, r, i) {
49011                         // initial support only for on class per el..
49012                         tb.selectedNode.className =  r ? r.get('val') : '';
49013                         editorcore.syncValue();
49014                     }
49015                 }
49016     
49017             }));
49018         }
49019         
49020         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49021         
49022         
49023         for (var i in tlist) {
49024             
49025             var item = tlist[i];
49026             tb.add(item.title + ":&nbsp;");
49027             
49028             
49029             //optname == used so you can configure the options available..
49030             var opts = item.opts ? item.opts : false;
49031             if (item.optname) { // use the b
49032                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49033            
49034             }
49035             
49036             if (opts) {
49037                 // opts == pulldown..
49038                 tb.addField( new Roo.form.ComboBox({
49039                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
49040                         id : 'val',
49041                         fields: ['val', 'display'],
49042                         data : opts  
49043                     }),
49044                     name : '-roo-edit-' + i,
49045                     
49046                     attrname : i,
49047                     stylename : item.style ? item.style : false,
49048                     
49049                     displayField: item.displayField ? item.displayField : 'val',
49050                     valueField :  'val',
49051                     typeAhead: false,
49052                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
49053                     editable : false,
49054                     triggerAction: 'all',
49055                     emptyText:'Select',
49056                     selectOnFocus:true,
49057                     width: item.width ? item.width  : 130,
49058                     listeners : {
49059                         'select': function(c, r, i) {
49060                             if (tb.selectedNode.hasAttribute('data-block')) {
49061                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49062                                 b[c.attrname] = r.get('val');
49063                                 b.updateElement(tb.selectedNode);
49064                                 editorcore.syncValue();
49065                                 return;
49066                             }
49067                             
49068                             if (c.stylename) {
49069                                 tb.selectedNode.style[c.stylename] =  r.get('val');
49070                                 editorcore.syncValue();
49071                                 return;
49072                             }
49073                             if (r === false) {
49074                                 tb.selectedNode.removeAttribute(c.attrname);
49075                                 editorcore.syncValue();
49076                                 return;
49077                             }
49078                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
49079                             editorcore.syncValue();
49080                         }
49081                     }
49082
49083                 }));
49084                 continue;
49085                     
49086                  
49087                 /*
49088                 tb.addField( new Roo.form.TextField({
49089                     name: i,
49090                     width: 100,
49091                     //allowBlank:false,
49092                     value: ''
49093                 }));
49094                 continue;
49095                 */
49096             }
49097             tb.addField( new Roo.form.TextField({
49098                 name: '-roo-edit-' + i,
49099                 attrname : i,
49100                 
49101                 width: item.width,
49102                 //allowBlank:true,
49103                 value: '',
49104                 listeners: {
49105                     'change' : function(f, nv, ov) {
49106                         
49107                         if (tb.selectedNode.hasAttribute('data-block')) {
49108                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49109                             b[f.attrname] = nv;
49110                             b.updateElement(tb.selectedNode);
49111                             editorcore.syncValue();
49112                             return;
49113                         }
49114                         
49115                         tb.selectedNode.setAttribute(f.attrname, nv);
49116                         editorcore.syncValue();
49117                     }
49118                 }
49119             }));
49120              
49121         }
49122         
49123         var _this = this;
49124         
49125         if(nm == 'BODY'){
49126             tb.addSeparator();
49127         
49128             tb.addButton( {
49129                 text: 'Stylesheets',
49130
49131                 listeners : {
49132                     click : function ()
49133                     {
49134                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
49135                     }
49136                 }
49137             });
49138         }
49139         
49140         tb.addFill();
49141         tb.addButton({
49142             text: 'Remove', // remove the tag, and puts the children outside...
49143     
49144             listeners : {
49145                 click : function ()
49146                 {
49147                     // remove
49148                     // undo does not work.
49149                     var sn = tb.selectedNode;
49150                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
49151                     if (sn.hasAttribute('data-block')) {
49152                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
49153                         sn.parentNode.removeChild(sn);
49154                         
49155                     } else {
49156                         // remove and keep parents.
49157                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
49158                         a.removeTag(sn);
49159                     }
49160                     
49161                     
49162                     var range = editorcore.createRange();
49163         
49164                     range.setStart(stn,0);
49165                     range.setEnd(stn,0); 
49166                     var selection = editorcore.getSelection();
49167                     selection.removeAllRanges();
49168                     selection.addRange(range);
49169                     
49170                     
49171                     //_this.updateToolbar(null, null, pn);
49172                     _this.updateToolbar(null, null, null);
49173                     _this.updateFooter(false);
49174                     
49175                 }
49176             }
49177             
49178                     
49179                 
49180             
49181         });
49182         
49183         
49184         tb.el.on('click', function(e){
49185             e.preventDefault(); // what does this do?
49186         });
49187         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
49188         tb.el.hide();
49189         
49190         // dont need to disable them... as they will get hidden
49191         return tb;
49192          
49193         
49194     },
49195     buildFooter : function()
49196     {
49197         
49198         var fel = this.editor.wrap.createChild();
49199         this.footer = new Roo.Toolbar(fel);
49200         // toolbar has scrolly on left / right?
49201         var footDisp= new Roo.Toolbar.Fill();
49202         var _t = this;
49203         this.footer.add(
49204             {
49205                 text : '&lt;',
49206                 xtype: 'Button',
49207                 handler : function() {
49208                     _t.footDisp.scrollTo('left',0,true)
49209                 }
49210             }
49211         );
49212         this.footer.add( footDisp );
49213         this.footer.add( 
49214             {
49215                 text : '&gt;',
49216                 xtype: 'Button',
49217                 handler : function() {
49218                     // no animation..
49219                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
49220                 }
49221             }
49222         );
49223         var fel = Roo.get(footDisp.el);
49224         fel.addClass('x-editor-context');
49225         this.footDispWrap = fel; 
49226         this.footDispWrap.overflow  = 'hidden';
49227         
49228         this.footDisp = fel.createChild();
49229         this.footDispWrap.on('click', this.onContextClick, this)
49230         
49231         
49232     },
49233     onContextClick : function (ev,dom)
49234     {
49235         ev.preventDefault();
49236         var  cn = dom.className;
49237         //Roo.log(cn);
49238         if (!cn.match(/x-ed-loc-/)) {
49239             return;
49240         }
49241         var n = cn.split('-').pop();
49242         var ans = this.footerEls;
49243         var sel = ans[n];
49244         
49245          // pick
49246         var range = this.editorcore.createRange();
49247         
49248         range.selectNodeContents(sel);
49249         //range.selectNode(sel);
49250         
49251         
49252         var selection = this.editorcore.getSelection();
49253         selection.removeAllRanges();
49254         selection.addRange(range);
49255         
49256         
49257         
49258         this.updateToolbar(null, null, sel);
49259         
49260         
49261     }
49262     
49263     
49264     
49265     
49266     
49267 });
49268
49269
49270
49271
49272
49273 /*
49274  * Based on:
49275  * Ext JS Library 1.1.1
49276  * Copyright(c) 2006-2007, Ext JS, LLC.
49277  *
49278  * Originally Released Under LGPL - original licence link has changed is not relivant.
49279  *
49280  * Fork - LGPL
49281  * <script type="text/javascript">
49282  */
49283  
49284 /**
49285  * @class Roo.form.BasicForm
49286  * @extends Roo.util.Observable
49287  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
49288  * @constructor
49289  * @param {String/HTMLElement/Roo.Element} el The form element or its id
49290  * @param {Object} config Configuration options
49291  */
49292 Roo.form.BasicForm = function(el, config){
49293     this.allItems = [];
49294     this.childForms = [];
49295     Roo.apply(this, config);
49296     /*
49297      * The Roo.form.Field items in this form.
49298      * @type MixedCollection
49299      */
49300      
49301      
49302     this.items = new Roo.util.MixedCollection(false, function(o){
49303         return o.id || (o.id = Roo.id());
49304     });
49305     this.addEvents({
49306         /**
49307          * @event beforeaction
49308          * Fires before any action is performed. Return false to cancel the action.
49309          * @param {Form} this
49310          * @param {Action} action The action to be performed
49311          */
49312         beforeaction: true,
49313         /**
49314          * @event actionfailed
49315          * Fires when an action fails.
49316          * @param {Form} this
49317          * @param {Action} action The action that failed
49318          */
49319         actionfailed : true,
49320         /**
49321          * @event actioncomplete
49322          * Fires when an action is completed.
49323          * @param {Form} this
49324          * @param {Action} action The action that completed
49325          */
49326         actioncomplete : true
49327     });
49328     if(el){
49329         this.initEl(el);
49330     }
49331     Roo.form.BasicForm.superclass.constructor.call(this);
49332     
49333     Roo.form.BasicForm.popover.apply();
49334 };
49335
49336 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
49337     /**
49338      * @cfg {String} method
49339      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
49340      */
49341     /**
49342      * @cfg {DataReader} reader
49343      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
49344      * This is optional as there is built-in support for processing JSON.
49345      */
49346     /**
49347      * @cfg {DataReader} errorReader
49348      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
49349      * This is completely optional as there is built-in support for processing JSON.
49350      */
49351     /**
49352      * @cfg {String} url
49353      * The URL to use for form actions if one isn't supplied in the action options.
49354      */
49355     /**
49356      * @cfg {Boolean} fileUpload
49357      * Set to true if this form is a file upload.
49358      */
49359      
49360     /**
49361      * @cfg {Object} baseParams
49362      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
49363      */
49364      /**
49365      
49366     /**
49367      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
49368      */
49369     timeout: 30,
49370
49371     // private
49372     activeAction : null,
49373
49374     /**
49375      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
49376      * or setValues() data instead of when the form was first created.
49377      */
49378     trackResetOnLoad : false,
49379     
49380     
49381     /**
49382      * childForms - used for multi-tab forms
49383      * @type {Array}
49384      */
49385     childForms : false,
49386     
49387     /**
49388      * allItems - full list of fields.
49389      * @type {Array}
49390      */
49391     allItems : false,
49392     
49393     /**
49394      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
49395      * element by passing it or its id or mask the form itself by passing in true.
49396      * @type Mixed
49397      */
49398     waitMsgTarget : false,
49399     
49400     /**
49401      * @type Boolean
49402      */
49403     disableMask : false,
49404     
49405     /**
49406      * @cfg {Boolean} errorMask (true|false) default false
49407      */
49408     errorMask : false,
49409     
49410     /**
49411      * @cfg {Number} maskOffset Default 100
49412      */
49413     maskOffset : 100,
49414
49415     // private
49416     initEl : function(el){
49417         this.el = Roo.get(el);
49418         this.id = this.el.id || Roo.id();
49419         this.el.on('submit', this.onSubmit, this);
49420         this.el.addClass('x-form');
49421     },
49422
49423     // private
49424     onSubmit : function(e){
49425         e.stopEvent();
49426     },
49427
49428     /**
49429      * Returns true if client-side validation on the form is successful.
49430      * @return Boolean
49431      */
49432     isValid : function(){
49433         var valid = true;
49434         var target = false;
49435         this.items.each(function(f){
49436             if(f.validate()){
49437                 return;
49438             }
49439             
49440             valid = false;
49441                 
49442             if(!target && f.el.isVisible(true)){
49443                 target = f;
49444             }
49445         });
49446         
49447         if(this.errorMask && !valid){
49448             Roo.form.BasicForm.popover.mask(this, target);
49449         }
49450         
49451         return valid;
49452     },
49453     /**
49454      * Returns array of invalid form fields.
49455      * @return Array
49456      */
49457     
49458     invalidFields : function()
49459     {
49460         var ret = [];
49461         this.items.each(function(f){
49462             if(f.validate()){
49463                 return;
49464             }
49465             ret.push(f);
49466             
49467         });
49468         
49469         return ret;
49470     },
49471     
49472     
49473     /**
49474      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
49475      * @return Boolean
49476      */
49477     isDirty : function(){
49478         var dirty = false;
49479         this.items.each(function(f){
49480            if(f.isDirty()){
49481                dirty = true;
49482                return false;
49483            }
49484         });
49485         return dirty;
49486     },
49487     
49488     /**
49489      * Returns true if any fields in this form have changed since their original load. (New version)
49490      * @return Boolean
49491      */
49492     
49493     hasChanged : function()
49494     {
49495         var dirty = false;
49496         this.items.each(function(f){
49497            if(f.hasChanged()){
49498                dirty = true;
49499                return false;
49500            }
49501         });
49502         return dirty;
49503         
49504     },
49505     /**
49506      * Resets all hasChanged to 'false' -
49507      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
49508      * So hasChanged storage is only to be used for this purpose
49509      * @return Boolean
49510      */
49511     resetHasChanged : function()
49512     {
49513         this.items.each(function(f){
49514            f.resetHasChanged();
49515         });
49516         
49517     },
49518     
49519     
49520     /**
49521      * Performs a predefined action (submit or load) or custom actions you define on this form.
49522      * @param {String} actionName The name of the action type
49523      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
49524      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
49525      * accept other config options):
49526      * <pre>
49527 Property          Type             Description
49528 ----------------  ---------------  ----------------------------------------------------------------------------------
49529 url               String           The url for the action (defaults to the form's url)
49530 method            String           The form method to use (defaults to the form's method, or POST if not defined)
49531 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
49532 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
49533                                    validate the form on the client (defaults to false)
49534      * </pre>
49535      * @return {BasicForm} this
49536      */
49537     doAction : function(action, options){
49538         if(typeof action == 'string'){
49539             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
49540         }
49541         if(this.fireEvent('beforeaction', this, action) !== false){
49542             this.beforeAction(action);
49543             action.run.defer(100, action);
49544         }
49545         return this;
49546     },
49547
49548     /**
49549      * Shortcut to do a submit action.
49550      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
49551      * @return {BasicForm} this
49552      */
49553     submit : function(options){
49554         this.doAction('submit', options);
49555         return this;
49556     },
49557
49558     /**
49559      * Shortcut to do a load action.
49560      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
49561      * @return {BasicForm} this
49562      */
49563     load : function(options){
49564         this.doAction('load', options);
49565         return this;
49566     },
49567
49568     /**
49569      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
49570      * @param {Record} record The record to edit
49571      * @return {BasicForm} this
49572      */
49573     updateRecord : function(record){
49574         record.beginEdit();
49575         var fs = record.fields;
49576         fs.each(function(f){
49577             var field = this.findField(f.name);
49578             if(field){
49579                 record.set(f.name, field.getValue());
49580             }
49581         }, this);
49582         record.endEdit();
49583         return this;
49584     },
49585
49586     /**
49587      * Loads an Roo.data.Record into this form.
49588      * @param {Record} record The record to load
49589      * @return {BasicForm} this
49590      */
49591     loadRecord : function(record){
49592         this.setValues(record.data);
49593         return this;
49594     },
49595
49596     // private
49597     beforeAction : function(action){
49598         var o = action.options;
49599         
49600         if(!this.disableMask) {
49601             if(this.waitMsgTarget === true){
49602                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
49603             }else if(this.waitMsgTarget){
49604                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
49605                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
49606             }else {
49607                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
49608             }
49609         }
49610         
49611          
49612     },
49613
49614     // private
49615     afterAction : function(action, success){
49616         this.activeAction = null;
49617         var o = action.options;
49618         
49619         if(!this.disableMask) {
49620             if(this.waitMsgTarget === true){
49621                 this.el.unmask();
49622             }else if(this.waitMsgTarget){
49623                 this.waitMsgTarget.unmask();
49624             }else{
49625                 Roo.MessageBox.updateProgress(1);
49626                 Roo.MessageBox.hide();
49627             }
49628         }
49629         
49630         if(success){
49631             if(o.reset){
49632                 this.reset();
49633             }
49634             Roo.callback(o.success, o.scope, [this, action]);
49635             this.fireEvent('actioncomplete', this, action);
49636             
49637         }else{
49638             
49639             // failure condition..
49640             // we have a scenario where updates need confirming.
49641             // eg. if a locking scenario exists..
49642             // we look for { errors : { needs_confirm : true }} in the response.
49643             if (
49644                 (typeof(action.result) != 'undefined')  &&
49645                 (typeof(action.result.errors) != 'undefined')  &&
49646                 (typeof(action.result.errors.needs_confirm) != 'undefined')
49647            ){
49648                 var _t = this;
49649                 Roo.MessageBox.confirm(
49650                     "Change requires confirmation",
49651                     action.result.errorMsg,
49652                     function(r) {
49653                         if (r != 'yes') {
49654                             return;
49655                         }
49656                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
49657                     }
49658                     
49659                 );
49660                 
49661                 
49662                 
49663                 return;
49664             }
49665             
49666             Roo.callback(o.failure, o.scope, [this, action]);
49667             // show an error message if no failed handler is set..
49668             if (!this.hasListener('actionfailed')) {
49669                 Roo.MessageBox.alert("Error",
49670                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
49671                         action.result.errorMsg :
49672                         "Saving Failed, please check your entries or try again"
49673                 );
49674             }
49675             
49676             this.fireEvent('actionfailed', this, action);
49677         }
49678         
49679     },
49680
49681     /**
49682      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
49683      * @param {String} id The value to search for
49684      * @return Field
49685      */
49686     findField : function(id){
49687         var field = this.items.get(id);
49688         if(!field){
49689             this.items.each(function(f){
49690                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
49691                     field = f;
49692                     return false;
49693                 }
49694             });
49695         }
49696         return field || null;
49697     },
49698
49699     /**
49700      * Add a secondary form to this one, 
49701      * Used to provide tabbed forms. One form is primary, with hidden values 
49702      * which mirror the elements from the other forms.
49703      * 
49704      * @param {Roo.form.Form} form to add.
49705      * 
49706      */
49707     addForm : function(form)
49708     {
49709        
49710         if (this.childForms.indexOf(form) > -1) {
49711             // already added..
49712             return;
49713         }
49714         this.childForms.push(form);
49715         var n = '';
49716         Roo.each(form.allItems, function (fe) {
49717             
49718             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
49719             if (this.findField(n)) { // already added..
49720                 return;
49721             }
49722             var add = new Roo.form.Hidden({
49723                 name : n
49724             });
49725             add.render(this.el);
49726             
49727             this.add( add );
49728         }, this);
49729         
49730     },
49731     /**
49732      * Mark fields in this form invalid in bulk.
49733      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
49734      * @return {BasicForm} this
49735      */
49736     markInvalid : function(errors){
49737         if(errors instanceof Array){
49738             for(var i = 0, len = errors.length; i < len; i++){
49739                 var fieldError = errors[i];
49740                 var f = this.findField(fieldError.id);
49741                 if(f){
49742                     f.markInvalid(fieldError.msg);
49743                 }
49744             }
49745         }else{
49746             var field, id;
49747             for(id in errors){
49748                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
49749                     field.markInvalid(errors[id]);
49750                 }
49751             }
49752         }
49753         Roo.each(this.childForms || [], function (f) {
49754             f.markInvalid(errors);
49755         });
49756         
49757         return this;
49758     },
49759
49760     /**
49761      * Set values for fields in this form in bulk.
49762      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
49763      * @return {BasicForm} this
49764      */
49765     setValues : function(values){
49766         if(values instanceof Array){ // array of objects
49767             for(var i = 0, len = values.length; i < len; i++){
49768                 var v = values[i];
49769                 var f = this.findField(v.id);
49770                 if(f){
49771                     f.setValue(v.value);
49772                     if(this.trackResetOnLoad){
49773                         f.originalValue = f.getValue();
49774                     }
49775                 }
49776             }
49777         }else{ // object hash
49778             var field, id;
49779             for(id in values){
49780                 if(typeof values[id] != 'function' && (field = this.findField(id))){
49781                     
49782                     if (field.setFromData && 
49783                         field.valueField && 
49784                         field.displayField &&
49785                         // combos' with local stores can 
49786                         // be queried via setValue()
49787                         // to set their value..
49788                         (field.store && !field.store.isLocal)
49789                         ) {
49790                         // it's a combo
49791                         var sd = { };
49792                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
49793                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
49794                         field.setFromData(sd);
49795                         
49796                     } else {
49797                         field.setValue(values[id]);
49798                     }
49799                     
49800                     
49801                     if(this.trackResetOnLoad){
49802                         field.originalValue = field.getValue();
49803                     }
49804                 }
49805             }
49806         }
49807         this.resetHasChanged();
49808         
49809         
49810         Roo.each(this.childForms || [], function (f) {
49811             f.setValues(values);
49812             f.resetHasChanged();
49813         });
49814                 
49815         return this;
49816     },
49817  
49818     /**
49819      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
49820      * they are returned as an array.
49821      * @param {Boolean} asString
49822      * @return {Object}
49823      */
49824     getValues : function(asString){
49825         if (this.childForms) {
49826             // copy values from the child forms
49827             Roo.each(this.childForms, function (f) {
49828                 this.setValues(f.getValues());
49829             }, this);
49830         }
49831         
49832         // use formdata
49833         if (typeof(FormData) != 'undefined' && asString !== true) {
49834             // this relies on a 'recent' version of chrome apparently...
49835             try {
49836                 var fd = (new FormData(this.el.dom)).entries();
49837                 var ret = {};
49838                 var ent = fd.next();
49839                 while (!ent.done) {
49840                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
49841                     ent = fd.next();
49842                 };
49843                 return ret;
49844             } catch(e) {
49845                 
49846             }
49847             
49848         }
49849         
49850         
49851         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
49852         if(asString === true){
49853             return fs;
49854         }
49855         return Roo.urlDecode(fs);
49856     },
49857     
49858     /**
49859      * Returns the fields in this form as an object with key/value pairs. 
49860      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
49861      * @return {Object}
49862      */
49863     getFieldValues : function(with_hidden)
49864     {
49865         if (this.childForms) {
49866             // copy values from the child forms
49867             // should this call getFieldValues - probably not as we do not currently copy
49868             // hidden fields when we generate..
49869             Roo.each(this.childForms, function (f) {
49870                 this.setValues(f.getValues());
49871             }, this);
49872         }
49873         
49874         var ret = {};
49875         this.items.each(function(f){
49876             if (!f.getName()) {
49877                 return;
49878             }
49879             var v = f.getValue();
49880             if (f.inputType =='radio') {
49881                 if (typeof(ret[f.getName()]) == 'undefined') {
49882                     ret[f.getName()] = ''; // empty..
49883                 }
49884                 
49885                 if (!f.el.dom.checked) {
49886                     return;
49887                     
49888                 }
49889                 v = f.el.dom.value;
49890                 
49891             }
49892             
49893             // not sure if this supported any more..
49894             if ((typeof(v) == 'object') && f.getRawValue) {
49895                 v = f.getRawValue() ; // dates..
49896             }
49897             // combo boxes where name != hiddenName...
49898             if (f.name != f.getName()) {
49899                 ret[f.name] = f.getRawValue();
49900             }
49901             ret[f.getName()] = v;
49902         });
49903         
49904         return ret;
49905     },
49906
49907     /**
49908      * Clears all invalid messages in this form.
49909      * @return {BasicForm} this
49910      */
49911     clearInvalid : function(){
49912         this.items.each(function(f){
49913            f.clearInvalid();
49914         });
49915         
49916         Roo.each(this.childForms || [], function (f) {
49917             f.clearInvalid();
49918         });
49919         
49920         
49921         return this;
49922     },
49923
49924     /**
49925      * Resets this form.
49926      * @return {BasicForm} this
49927      */
49928     reset : function(){
49929         this.items.each(function(f){
49930             f.reset();
49931         });
49932         
49933         Roo.each(this.childForms || [], function (f) {
49934             f.reset();
49935         });
49936         this.resetHasChanged();
49937         
49938         return this;
49939     },
49940
49941     /**
49942      * Add Roo.form components to this form.
49943      * @param {Field} field1
49944      * @param {Field} field2 (optional)
49945      * @param {Field} etc (optional)
49946      * @return {BasicForm} this
49947      */
49948     add : function(){
49949         this.items.addAll(Array.prototype.slice.call(arguments, 0));
49950         return this;
49951     },
49952
49953
49954     /**
49955      * Removes a field from the items collection (does NOT remove its markup).
49956      * @param {Field} field
49957      * @return {BasicForm} this
49958      */
49959     remove : function(field){
49960         this.items.remove(field);
49961         return this;
49962     },
49963
49964     /**
49965      * Looks at the fields in this form, checks them for an id attribute,
49966      * and calls applyTo on the existing dom element with that id.
49967      * @return {BasicForm} this
49968      */
49969     render : function(){
49970         this.items.each(function(f){
49971             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
49972                 f.applyTo(f.id);
49973             }
49974         });
49975         return this;
49976     },
49977
49978     /**
49979      * Calls {@link Ext#apply} for all fields in this form with the passed object.
49980      * @param {Object} values
49981      * @return {BasicForm} this
49982      */
49983     applyToFields : function(o){
49984         this.items.each(function(f){
49985            Roo.apply(f, o);
49986         });
49987         return this;
49988     },
49989
49990     /**
49991      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
49992      * @param {Object} values
49993      * @return {BasicForm} this
49994      */
49995     applyIfToFields : function(o){
49996         this.items.each(function(f){
49997            Roo.applyIf(f, o);
49998         });
49999         return this;
50000     }
50001 });
50002
50003 // back compat
50004 Roo.BasicForm = Roo.form.BasicForm;
50005
50006 Roo.apply(Roo.form.BasicForm, {
50007     
50008     popover : {
50009         
50010         padding : 5,
50011         
50012         isApplied : false,
50013         
50014         isMasked : false,
50015         
50016         form : false,
50017         
50018         target : false,
50019         
50020         intervalID : false,
50021         
50022         maskEl : false,
50023         
50024         apply : function()
50025         {
50026             if(this.isApplied){
50027                 return;
50028             }
50029             
50030             this.maskEl = {
50031                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
50032                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
50033                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
50034                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
50035             };
50036             
50037             this.maskEl.top.enableDisplayMode("block");
50038             this.maskEl.left.enableDisplayMode("block");
50039             this.maskEl.bottom.enableDisplayMode("block");
50040             this.maskEl.right.enableDisplayMode("block");
50041             
50042             Roo.get(document.body).on('click', function(){
50043                 this.unmask();
50044             }, this);
50045             
50046             Roo.get(document.body).on('touchstart', function(){
50047                 this.unmask();
50048             }, this);
50049             
50050             this.isApplied = true
50051         },
50052         
50053         mask : function(form, target)
50054         {
50055             this.form = form;
50056             
50057             this.target = target;
50058             
50059             if(!this.form.errorMask || !target.el){
50060                 return;
50061             }
50062             
50063             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
50064             
50065             var ot = this.target.el.calcOffsetsTo(scrollable);
50066             
50067             var scrollTo = ot[1] - this.form.maskOffset;
50068             
50069             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
50070             
50071             scrollable.scrollTo('top', scrollTo);
50072             
50073             var el = this.target.wrap || this.target.el;
50074             
50075             var box = el.getBox();
50076             
50077             this.maskEl.top.setStyle('position', 'absolute');
50078             this.maskEl.top.setStyle('z-index', 10000);
50079             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
50080             this.maskEl.top.setLeft(0);
50081             this.maskEl.top.setTop(0);
50082             this.maskEl.top.show();
50083             
50084             this.maskEl.left.setStyle('position', 'absolute');
50085             this.maskEl.left.setStyle('z-index', 10000);
50086             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
50087             this.maskEl.left.setLeft(0);
50088             this.maskEl.left.setTop(box.y - this.padding);
50089             this.maskEl.left.show();
50090
50091             this.maskEl.bottom.setStyle('position', 'absolute');
50092             this.maskEl.bottom.setStyle('z-index', 10000);
50093             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
50094             this.maskEl.bottom.setLeft(0);
50095             this.maskEl.bottom.setTop(box.bottom + this.padding);
50096             this.maskEl.bottom.show();
50097
50098             this.maskEl.right.setStyle('position', 'absolute');
50099             this.maskEl.right.setStyle('z-index', 10000);
50100             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
50101             this.maskEl.right.setLeft(box.right + this.padding);
50102             this.maskEl.right.setTop(box.y - this.padding);
50103             this.maskEl.right.show();
50104
50105             this.intervalID = window.setInterval(function() {
50106                 Roo.form.BasicForm.popover.unmask();
50107             }, 10000);
50108
50109             window.onwheel = function(){ return false;};
50110             
50111             (function(){ this.isMasked = true; }).defer(500, this);
50112             
50113         },
50114         
50115         unmask : function()
50116         {
50117             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
50118                 return;
50119             }
50120             
50121             this.maskEl.top.setStyle('position', 'absolute');
50122             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
50123             this.maskEl.top.hide();
50124
50125             this.maskEl.left.setStyle('position', 'absolute');
50126             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
50127             this.maskEl.left.hide();
50128
50129             this.maskEl.bottom.setStyle('position', 'absolute');
50130             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
50131             this.maskEl.bottom.hide();
50132
50133             this.maskEl.right.setStyle('position', 'absolute');
50134             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
50135             this.maskEl.right.hide();
50136             
50137             window.onwheel = function(){ return true;};
50138             
50139             if(this.intervalID){
50140                 window.clearInterval(this.intervalID);
50141                 this.intervalID = false;
50142             }
50143             
50144             this.isMasked = false;
50145             
50146         }
50147         
50148     }
50149     
50150 });/*
50151  * Based on:
50152  * Ext JS Library 1.1.1
50153  * Copyright(c) 2006-2007, Ext JS, LLC.
50154  *
50155  * Originally Released Under LGPL - original licence link has changed is not relivant.
50156  *
50157  * Fork - LGPL
50158  * <script type="text/javascript">
50159  */
50160
50161 /**
50162  * @class Roo.form.Form
50163  * @extends Roo.form.BasicForm
50164  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50165  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
50166  * @constructor
50167  * @param {Object} config Configuration options
50168  */
50169 Roo.form.Form = function(config){
50170     var xitems =  [];
50171     if (config.items) {
50172         xitems = config.items;
50173         delete config.items;
50174     }
50175    
50176     
50177     Roo.form.Form.superclass.constructor.call(this, null, config);
50178     this.url = this.url || this.action;
50179     if(!this.root){
50180         this.root = new Roo.form.Layout(Roo.applyIf({
50181             id: Roo.id()
50182         }, config));
50183     }
50184     this.active = this.root;
50185     /**
50186      * Array of all the buttons that have been added to this form via {@link addButton}
50187      * @type Array
50188      */
50189     this.buttons = [];
50190     this.allItems = [];
50191     this.addEvents({
50192         /**
50193          * @event clientvalidation
50194          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
50195          * @param {Form} this
50196          * @param {Boolean} valid true if the form has passed client-side validation
50197          */
50198         clientvalidation: true,
50199         /**
50200          * @event rendered
50201          * Fires when the form is rendered
50202          * @param {Roo.form.Form} form
50203          */
50204         rendered : true
50205     });
50206     
50207     if (this.progressUrl) {
50208             // push a hidden field onto the list of fields..
50209             this.addxtype( {
50210                     xns: Roo.form, 
50211                     xtype : 'Hidden', 
50212                     name : 'UPLOAD_IDENTIFIER' 
50213             });
50214         }
50215         
50216     
50217     Roo.each(xitems, this.addxtype, this);
50218     
50219 };
50220
50221 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
50222      /**
50223      * @cfg {Roo.Button} buttons[] buttons at bottom of form
50224      */
50225     
50226     /**
50227      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
50228      */
50229     /**
50230      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
50231      */
50232     /**
50233      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
50234      */
50235     buttonAlign:'center',
50236
50237     /**
50238      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
50239      */
50240     minButtonWidth:75,
50241
50242     /**
50243      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
50244      * This property cascades to child containers if not set.
50245      */
50246     labelAlign:'left',
50247
50248     /**
50249      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
50250      * fires a looping event with that state. This is required to bind buttons to the valid
50251      * state using the config value formBind:true on the button.
50252      */
50253     monitorValid : false,
50254
50255     /**
50256      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
50257      */
50258     monitorPoll : 200,
50259     
50260     /**
50261      * @cfg {String} progressUrl - Url to return progress data 
50262      */
50263     
50264     progressUrl : false,
50265     /**
50266      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
50267      * sending a formdata with extra parameters - eg uploaded elements.
50268      */
50269     
50270     formData : false,
50271     
50272     /**
50273      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
50274      * fields are added and the column is closed. If no fields are passed the column remains open
50275      * until end() is called.
50276      * @param {Object} config The config to pass to the column
50277      * @param {Field} field1 (optional)
50278      * @param {Field} field2 (optional)
50279      * @param {Field} etc (optional)
50280      * @return Column The column container object
50281      */
50282     column : function(c){
50283         var col = new Roo.form.Column(c);
50284         this.start(col);
50285         if(arguments.length > 1){ // duplicate code required because of Opera
50286             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50287             this.end();
50288         }
50289         return col;
50290     },
50291
50292     /**
50293      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
50294      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
50295      * until end() is called.
50296      * @param {Object} config The config to pass to the fieldset
50297      * @param {Field} field1 (optional)
50298      * @param {Field} field2 (optional)
50299      * @param {Field} etc (optional)
50300      * @return FieldSet The fieldset container object
50301      */
50302     fieldset : function(c){
50303         var fs = new Roo.form.FieldSet(c);
50304         this.start(fs);
50305         if(arguments.length > 1){ // duplicate code required because of Opera
50306             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50307             this.end();
50308         }
50309         return fs;
50310     },
50311
50312     /**
50313      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
50314      * fields are added and the container is closed. If no fields are passed the container remains open
50315      * until end() is called.
50316      * @param {Object} config The config to pass to the Layout
50317      * @param {Field} field1 (optional)
50318      * @param {Field} field2 (optional)
50319      * @param {Field} etc (optional)
50320      * @return Layout The container object
50321      */
50322     container : function(c){
50323         var l = new Roo.form.Layout(c);
50324         this.start(l);
50325         if(arguments.length > 1){ // duplicate code required because of Opera
50326             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50327             this.end();
50328         }
50329         return l;
50330     },
50331
50332     /**
50333      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
50334      * @param {Object} container A Roo.form.Layout or subclass of Layout
50335      * @return {Form} this
50336      */
50337     start : function(c){
50338         // cascade label info
50339         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
50340         this.active.stack.push(c);
50341         c.ownerCt = this.active;
50342         this.active = c;
50343         return this;
50344     },
50345
50346     /**
50347      * Closes the current open container
50348      * @return {Form} this
50349      */
50350     end : function(){
50351         if(this.active == this.root){
50352             return this;
50353         }
50354         this.active = this.active.ownerCt;
50355         return this;
50356     },
50357
50358     /**
50359      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
50360      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
50361      * as the label of the field.
50362      * @param {Field} field1
50363      * @param {Field} field2 (optional)
50364      * @param {Field} etc. (optional)
50365      * @return {Form} this
50366      */
50367     add : function(){
50368         this.active.stack.push.apply(this.active.stack, arguments);
50369         this.allItems.push.apply(this.allItems,arguments);
50370         var r = [];
50371         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
50372             if(a[i].isFormField){
50373                 r.push(a[i]);
50374             }
50375         }
50376         if(r.length > 0){
50377             Roo.form.Form.superclass.add.apply(this, r);
50378         }
50379         return this;
50380     },
50381     
50382
50383     
50384     
50385     
50386      /**
50387      * Find any element that has been added to a form, using it's ID or name
50388      * This can include framesets, columns etc. along with regular fields..
50389      * @param {String} id - id or name to find.
50390      
50391      * @return {Element} e - or false if nothing found.
50392      */
50393     findbyId : function(id)
50394     {
50395         var ret = false;
50396         if (!id) {
50397             return ret;
50398         }
50399         Roo.each(this.allItems, function(f){
50400             if (f.id == id || f.name == id ){
50401                 ret = f;
50402                 return false;
50403             }
50404         });
50405         return ret;
50406     },
50407
50408     
50409     
50410     /**
50411      * Render this form into the passed container. This should only be called once!
50412      * @param {String/HTMLElement/Element} container The element this component should be rendered into
50413      * @return {Form} this
50414      */
50415     render : function(ct)
50416     {
50417         
50418         
50419         
50420         ct = Roo.get(ct);
50421         var o = this.autoCreate || {
50422             tag: 'form',
50423             method : this.method || 'POST',
50424             id : this.id || Roo.id()
50425         };
50426         this.initEl(ct.createChild(o));
50427
50428         this.root.render(this.el);
50429         
50430        
50431              
50432         this.items.each(function(f){
50433             f.render('x-form-el-'+f.id);
50434         });
50435
50436         if(this.buttons.length > 0){
50437             // tables are required to maintain order and for correct IE layout
50438             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
50439                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
50440                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
50441             }}, null, true);
50442             var tr = tb.getElementsByTagName('tr')[0];
50443             for(var i = 0, len = this.buttons.length; i < len; i++) {
50444                 var b = this.buttons[i];
50445                 var td = document.createElement('td');
50446                 td.className = 'x-form-btn-td';
50447                 b.render(tr.appendChild(td));
50448             }
50449         }
50450         if(this.monitorValid){ // initialize after render
50451             this.startMonitoring();
50452         }
50453         this.fireEvent('rendered', this);
50454         return this;
50455     },
50456
50457     /**
50458      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
50459      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
50460      * object or a valid Roo.DomHelper element config
50461      * @param {Function} handler The function called when the button is clicked
50462      * @param {Object} scope (optional) The scope of the handler function
50463      * @return {Roo.Button}
50464      */
50465     addButton : function(config, handler, scope){
50466         var bc = {
50467             handler: handler,
50468             scope: scope,
50469             minWidth: this.minButtonWidth,
50470             hideParent:true
50471         };
50472         if(typeof config == "string"){
50473             bc.text = config;
50474         }else{
50475             Roo.apply(bc, config);
50476         }
50477         var btn = new Roo.Button(null, bc);
50478         this.buttons.push(btn);
50479         return btn;
50480     },
50481
50482      /**
50483      * Adds a series of form elements (using the xtype property as the factory method.
50484      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
50485      * @param {Object} config 
50486      */
50487     
50488     addxtype : function()
50489     {
50490         var ar = Array.prototype.slice.call(arguments, 0);
50491         var ret = false;
50492         for(var i = 0; i < ar.length; i++) {
50493             if (!ar[i]) {
50494                 continue; // skip -- if this happends something invalid got sent, we 
50495                 // should ignore it, as basically that interface element will not show up
50496                 // and that should be pretty obvious!!
50497             }
50498             
50499             if (Roo.form[ar[i].xtype]) {
50500                 ar[i].form = this;
50501                 var fe = Roo.factory(ar[i], Roo.form);
50502                 if (!ret) {
50503                     ret = fe;
50504                 }
50505                 fe.form = this;
50506                 if (fe.store) {
50507                     fe.store.form = this;
50508                 }
50509                 if (fe.isLayout) {  
50510                          
50511                     this.start(fe);
50512                     this.allItems.push(fe);
50513                     if (fe.items && fe.addxtype) {
50514                         fe.addxtype.apply(fe, fe.items);
50515                         delete fe.items;
50516                     }
50517                      this.end();
50518                     continue;
50519                 }
50520                 
50521                 
50522                  
50523                 this.add(fe);
50524               //  console.log('adding ' + ar[i].xtype);
50525             }
50526             if (ar[i].xtype == 'Button') {  
50527                 //console.log('adding button');
50528                 //console.log(ar[i]);
50529                 this.addButton(ar[i]);
50530                 this.allItems.push(fe);
50531                 continue;
50532             }
50533             
50534             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
50535                 alert('end is not supported on xtype any more, use items');
50536             //    this.end();
50537             //    //console.log('adding end');
50538             }
50539             
50540         }
50541         return ret;
50542     },
50543     
50544     /**
50545      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
50546      * option "monitorValid"
50547      */
50548     startMonitoring : function(){
50549         if(!this.bound){
50550             this.bound = true;
50551             Roo.TaskMgr.start({
50552                 run : this.bindHandler,
50553                 interval : this.monitorPoll || 200,
50554                 scope: this
50555             });
50556         }
50557     },
50558
50559     /**
50560      * Stops monitoring of the valid state of this form
50561      */
50562     stopMonitoring : function(){
50563         this.bound = false;
50564     },
50565
50566     // private
50567     bindHandler : function(){
50568         if(!this.bound){
50569             return false; // stops binding
50570         }
50571         var valid = true;
50572         this.items.each(function(f){
50573             if(!f.isValid(true)){
50574                 valid = false;
50575                 return false;
50576             }
50577         });
50578         for(var i = 0, len = this.buttons.length; i < len; i++){
50579             var btn = this.buttons[i];
50580             if(btn.formBind === true && btn.disabled === valid){
50581                 btn.setDisabled(!valid);
50582             }
50583         }
50584         this.fireEvent('clientvalidation', this, valid);
50585     }
50586     
50587     
50588     
50589     
50590     
50591     
50592     
50593     
50594 });
50595
50596
50597 // back compat
50598 Roo.Form = Roo.form.Form;
50599 /*
50600  * Based on:
50601  * Ext JS Library 1.1.1
50602  * Copyright(c) 2006-2007, Ext JS, LLC.
50603  *
50604  * Originally Released Under LGPL - original licence link has changed is not relivant.
50605  *
50606  * Fork - LGPL
50607  * <script type="text/javascript">
50608  */
50609
50610 // as we use this in bootstrap.
50611 Roo.namespace('Roo.form');
50612  /**
50613  * @class Roo.form.Action
50614  * Internal Class used to handle form actions
50615  * @constructor
50616  * @param {Roo.form.BasicForm} el The form element or its id
50617  * @param {Object} config Configuration options
50618  */
50619
50620  
50621  
50622 // define the action interface
50623 Roo.form.Action = function(form, options){
50624     this.form = form;
50625     this.options = options || {};
50626 };
50627 /**
50628  * Client Validation Failed
50629  * @const 
50630  */
50631 Roo.form.Action.CLIENT_INVALID = 'client';
50632 /**
50633  * Server Validation Failed
50634  * @const 
50635  */
50636 Roo.form.Action.SERVER_INVALID = 'server';
50637  /**
50638  * Connect to Server Failed
50639  * @const 
50640  */
50641 Roo.form.Action.CONNECT_FAILURE = 'connect';
50642 /**
50643  * Reading Data from Server Failed
50644  * @const 
50645  */
50646 Roo.form.Action.LOAD_FAILURE = 'load';
50647
50648 Roo.form.Action.prototype = {
50649     type : 'default',
50650     failureType : undefined,
50651     response : undefined,
50652     result : undefined,
50653
50654     // interface method
50655     run : function(options){
50656
50657     },
50658
50659     // interface method
50660     success : function(response){
50661
50662     },
50663
50664     // interface method
50665     handleResponse : function(response){
50666
50667     },
50668
50669     // default connection failure
50670     failure : function(response){
50671         
50672         this.response = response;
50673         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50674         this.form.afterAction(this, false);
50675     },
50676
50677     processResponse : function(response){
50678         this.response = response;
50679         if(!response.responseText){
50680             return true;
50681         }
50682         this.result = this.handleResponse(response);
50683         return this.result;
50684     },
50685
50686     // utility functions used internally
50687     getUrl : function(appendParams){
50688         var url = this.options.url || this.form.url || this.form.el.dom.action;
50689         if(appendParams){
50690             var p = this.getParams();
50691             if(p){
50692                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
50693             }
50694         }
50695         return url;
50696     },
50697
50698     getMethod : function(){
50699         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
50700     },
50701
50702     getParams : function(){
50703         var bp = this.form.baseParams;
50704         var p = this.options.params;
50705         if(p){
50706             if(typeof p == "object"){
50707                 p = Roo.urlEncode(Roo.applyIf(p, bp));
50708             }else if(typeof p == 'string' && bp){
50709                 p += '&' + Roo.urlEncode(bp);
50710             }
50711         }else if(bp){
50712             p = Roo.urlEncode(bp);
50713         }
50714         return p;
50715     },
50716
50717     createCallback : function(){
50718         return {
50719             success: this.success,
50720             failure: this.failure,
50721             scope: this,
50722             timeout: (this.form.timeout*1000),
50723             upload: this.form.fileUpload ? this.success : undefined
50724         };
50725     }
50726 };
50727
50728 Roo.form.Action.Submit = function(form, options){
50729     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
50730 };
50731
50732 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
50733     type : 'submit',
50734
50735     haveProgress : false,
50736     uploadComplete : false,
50737     
50738     // uploadProgress indicator.
50739     uploadProgress : function()
50740     {
50741         if (!this.form.progressUrl) {
50742             return;
50743         }
50744         
50745         if (!this.haveProgress) {
50746             Roo.MessageBox.progress("Uploading", "Uploading");
50747         }
50748         if (this.uploadComplete) {
50749            Roo.MessageBox.hide();
50750            return;
50751         }
50752         
50753         this.haveProgress = true;
50754    
50755         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
50756         
50757         var c = new Roo.data.Connection();
50758         c.request({
50759             url : this.form.progressUrl,
50760             params: {
50761                 id : uid
50762             },
50763             method: 'GET',
50764             success : function(req){
50765                //console.log(data);
50766                 var rdata = false;
50767                 var edata;
50768                 try  {
50769                    rdata = Roo.decode(req.responseText)
50770                 } catch (e) {
50771                     Roo.log("Invalid data from server..");
50772                     Roo.log(edata);
50773                     return;
50774                 }
50775                 if (!rdata || !rdata.success) {
50776                     Roo.log(rdata);
50777                     Roo.MessageBox.alert(Roo.encode(rdata));
50778                     return;
50779                 }
50780                 var data = rdata.data;
50781                 
50782                 if (this.uploadComplete) {
50783                    Roo.MessageBox.hide();
50784                    return;
50785                 }
50786                    
50787                 if (data){
50788                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
50789                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
50790                     );
50791                 }
50792                 this.uploadProgress.defer(2000,this);
50793             },
50794        
50795             failure: function(data) {
50796                 Roo.log('progress url failed ');
50797                 Roo.log(data);
50798             },
50799             scope : this
50800         });
50801            
50802     },
50803     
50804     
50805     run : function()
50806     {
50807         // run get Values on the form, so it syncs any secondary forms.
50808         this.form.getValues();
50809         
50810         var o = this.options;
50811         var method = this.getMethod();
50812         var isPost = method == 'POST';
50813         if(o.clientValidation === false || this.form.isValid()){
50814             
50815             if (this.form.progressUrl) {
50816                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
50817                     (new Date() * 1) + '' + Math.random());
50818                     
50819             } 
50820             
50821             
50822             Roo.Ajax.request(Roo.apply(this.createCallback(), {
50823                 form:this.form.el.dom,
50824                 url:this.getUrl(!isPost),
50825                 method: method,
50826                 params:isPost ? this.getParams() : null,
50827                 isUpload: this.form.fileUpload,
50828                 formData : this.form.formData
50829             }));
50830             
50831             this.uploadProgress();
50832
50833         }else if (o.clientValidation !== false){ // client validation failed
50834             this.failureType = Roo.form.Action.CLIENT_INVALID;
50835             this.form.afterAction(this, false);
50836         }
50837     },
50838
50839     success : function(response)
50840     {
50841         this.uploadComplete= true;
50842         if (this.haveProgress) {
50843             Roo.MessageBox.hide();
50844         }
50845         
50846         
50847         var result = this.processResponse(response);
50848         if(result === true || result.success){
50849             this.form.afterAction(this, true);
50850             return;
50851         }
50852         if(result.errors){
50853             this.form.markInvalid(result.errors);
50854             this.failureType = Roo.form.Action.SERVER_INVALID;
50855         }
50856         this.form.afterAction(this, false);
50857     },
50858     failure : function(response)
50859     {
50860         this.uploadComplete= true;
50861         if (this.haveProgress) {
50862             Roo.MessageBox.hide();
50863         }
50864         
50865         this.response = response;
50866         this.failureType = Roo.form.Action.CONNECT_FAILURE;
50867         this.form.afterAction(this, false);
50868     },
50869     
50870     handleResponse : function(response){
50871         if(this.form.errorReader){
50872             var rs = this.form.errorReader.read(response);
50873             var errors = [];
50874             if(rs.records){
50875                 for(var i = 0, len = rs.records.length; i < len; i++) {
50876                     var r = rs.records[i];
50877                     errors[i] = r.data;
50878                 }
50879             }
50880             if(errors.length < 1){
50881                 errors = null;
50882             }
50883             return {
50884                 success : rs.success,
50885                 errors : errors
50886             };
50887         }
50888         var ret = false;
50889         try {
50890             ret = Roo.decode(response.responseText);
50891         } catch (e) {
50892             ret = {
50893                 success: false,
50894                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
50895                 errors : []
50896             };
50897         }
50898         return ret;
50899         
50900     }
50901 });
50902
50903
50904 Roo.form.Action.Load = function(form, options){
50905     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
50906     this.reader = this.form.reader;
50907 };
50908
50909 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
50910     type : 'load',
50911
50912     run : function(){
50913         
50914         Roo.Ajax.request(Roo.apply(
50915                 this.createCallback(), {
50916                     method:this.getMethod(),
50917                     url:this.getUrl(false),
50918                     params:this.getParams()
50919         }));
50920     },
50921
50922     success : function(response){
50923         
50924         var result = this.processResponse(response);
50925         if(result === true || !result.success || !result.data){
50926             this.failureType = Roo.form.Action.LOAD_FAILURE;
50927             this.form.afterAction(this, false);
50928             return;
50929         }
50930         this.form.clearInvalid();
50931         this.form.setValues(result.data);
50932         this.form.afterAction(this, true);
50933     },
50934
50935     handleResponse : function(response){
50936         if(this.form.reader){
50937             var rs = this.form.reader.read(response);
50938             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
50939             return {
50940                 success : rs.success,
50941                 data : data
50942             };
50943         }
50944         return Roo.decode(response.responseText);
50945     }
50946 });
50947
50948 Roo.form.Action.ACTION_TYPES = {
50949     'load' : Roo.form.Action.Load,
50950     'submit' : Roo.form.Action.Submit
50951 };/*
50952  * Based on:
50953  * Ext JS Library 1.1.1
50954  * Copyright(c) 2006-2007, Ext JS, LLC.
50955  *
50956  * Originally Released Under LGPL - original licence link has changed is not relivant.
50957  *
50958  * Fork - LGPL
50959  * <script type="text/javascript">
50960  */
50961  
50962 /**
50963  * @class Roo.form.Layout
50964  * @extends Roo.Component
50965  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50966  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
50967  * @constructor
50968  * @param {Object} config Configuration options
50969  */
50970 Roo.form.Layout = function(config){
50971     var xitems = [];
50972     if (config.items) {
50973         xitems = config.items;
50974         delete config.items;
50975     }
50976     Roo.form.Layout.superclass.constructor.call(this, config);
50977     this.stack = [];
50978     Roo.each(xitems, this.addxtype, this);
50979      
50980 };
50981
50982 Roo.extend(Roo.form.Layout, Roo.Component, {
50983     /**
50984      * @cfg {String/Object} autoCreate
50985      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
50986      */
50987     /**
50988      * @cfg {String/Object/Function} style
50989      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
50990      * a function which returns such a specification.
50991      */
50992     /**
50993      * @cfg {String} labelAlign
50994      * Valid values are "left," "top" and "right" (defaults to "left")
50995      */
50996     /**
50997      * @cfg {Number} labelWidth
50998      * Fixed width in pixels of all field labels (defaults to undefined)
50999      */
51000     /**
51001      * @cfg {Boolean} clear
51002      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51003      */
51004     clear : true,
51005     /**
51006      * @cfg {String} labelSeparator
51007      * The separator to use after field labels (defaults to ':')
51008      */
51009     labelSeparator : ':',
51010     /**
51011      * @cfg {Boolean} hideLabels
51012      * True to suppress the display of field labels in this layout (defaults to false)
51013      */
51014     hideLabels : false,
51015
51016     // private
51017     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51018     
51019     isLayout : true,
51020     
51021     // private
51022     onRender : function(ct, position){
51023         if(this.el){ // from markup
51024             this.el = Roo.get(this.el);
51025         }else {  // generate
51026             var cfg = this.getAutoCreate();
51027             this.el = ct.createChild(cfg, position);
51028         }
51029         if(this.style){
51030             this.el.applyStyles(this.style);
51031         }
51032         if(this.labelAlign){
51033             this.el.addClass('x-form-label-'+this.labelAlign);
51034         }
51035         if(this.hideLabels){
51036             this.labelStyle = "display:none";
51037             this.elementStyle = "padding-left:0;";
51038         }else{
51039             if(typeof this.labelWidth == 'number'){
51040                 this.labelStyle = "width:"+this.labelWidth+"px;";
51041                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
51042             }
51043             if(this.labelAlign == 'top'){
51044                 this.labelStyle = "width:auto;";
51045                 this.elementStyle = "padding-left:0;";
51046             }
51047         }
51048         var stack = this.stack;
51049         var slen = stack.length;
51050         if(slen > 0){
51051             if(!this.fieldTpl){
51052                 var t = new Roo.Template(
51053                     '<div class="x-form-item {5}">',
51054                         '<label for="{0}" style="{2}">{1}{4}</label>',
51055                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51056                         '</div>',
51057                     '</div><div class="x-form-clear-left"></div>'
51058                 );
51059                 t.disableFormats = true;
51060                 t.compile();
51061                 Roo.form.Layout.prototype.fieldTpl = t;
51062             }
51063             for(var i = 0; i < slen; i++) {
51064                 if(stack[i].isFormField){
51065                     this.renderField(stack[i]);
51066                 }else{
51067                     this.renderComponent(stack[i]);
51068                 }
51069             }
51070         }
51071         if(this.clear){
51072             this.el.createChild({cls:'x-form-clear'});
51073         }
51074     },
51075
51076     // private
51077     renderField : function(f){
51078         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
51079                f.id, //0
51080                f.fieldLabel, //1
51081                f.labelStyle||this.labelStyle||'', //2
51082                this.elementStyle||'', //3
51083                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
51084                f.itemCls||this.itemCls||''  //5
51085        ], true).getPrevSibling());
51086     },
51087
51088     // private
51089     renderComponent : function(c){
51090         c.render(c.isLayout ? this.el : this.el.createChild());    
51091     },
51092     /**
51093      * Adds a object form elements (using the xtype property as the factory method.)
51094      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
51095      * @param {Object} config 
51096      */
51097     addxtype : function(o)
51098     {
51099         // create the lement.
51100         o.form = this.form;
51101         var fe = Roo.factory(o, Roo.form);
51102         this.form.allItems.push(fe);
51103         this.stack.push(fe);
51104         
51105         if (fe.isFormField) {
51106             this.form.items.add(fe);
51107         }
51108          
51109         return fe;
51110     }
51111 });
51112
51113 /**
51114  * @class Roo.form.Column
51115  * @extends Roo.form.Layout
51116  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
51117  * @constructor
51118  * @param {Object} config Configuration options
51119  */
51120 Roo.form.Column = function(config){
51121     Roo.form.Column.superclass.constructor.call(this, config);
51122 };
51123
51124 Roo.extend(Roo.form.Column, Roo.form.Layout, {
51125     /**
51126      * @cfg {Number/String} width
51127      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51128      */
51129     /**
51130      * @cfg {String/Object} autoCreate
51131      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
51132      */
51133
51134     // private
51135     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
51136
51137     // private
51138     onRender : function(ct, position){
51139         Roo.form.Column.superclass.onRender.call(this, ct, position);
51140         if(this.width){
51141             this.el.setWidth(this.width);
51142         }
51143     }
51144 });
51145
51146
51147 /**
51148  * @class Roo.form.Row
51149  * @extends Roo.form.Layout
51150  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51151  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
51152  * @constructor
51153  * @param {Object} config Configuration options
51154  */
51155
51156  
51157 Roo.form.Row = function(config){
51158     Roo.form.Row.superclass.constructor.call(this, config);
51159 };
51160  
51161 Roo.extend(Roo.form.Row, Roo.form.Layout, {
51162       /**
51163      * @cfg {Number/String} width
51164      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51165      */
51166     /**
51167      * @cfg {Number/String} height
51168      * The fixed height of the column in pixels or CSS value (defaults to "auto")
51169      */
51170     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
51171     
51172     padWidth : 20,
51173     // private
51174     onRender : function(ct, position){
51175         //console.log('row render');
51176         if(!this.rowTpl){
51177             var t = new Roo.Template(
51178                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
51179                     '<label for="{0}" style="{2}">{1}{4}</label>',
51180                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51181                     '</div>',
51182                 '</div>'
51183             );
51184             t.disableFormats = true;
51185             t.compile();
51186             Roo.form.Layout.prototype.rowTpl = t;
51187         }
51188         this.fieldTpl = this.rowTpl;
51189         
51190         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
51191         var labelWidth = 100;
51192         
51193         if ((this.labelAlign != 'top')) {
51194             if (typeof this.labelWidth == 'number') {
51195                 labelWidth = this.labelWidth
51196             }
51197             this.padWidth =  20 + labelWidth;
51198             
51199         }
51200         
51201         Roo.form.Column.superclass.onRender.call(this, ct, position);
51202         if(this.width){
51203             this.el.setWidth(this.width);
51204         }
51205         if(this.height){
51206             this.el.setHeight(this.height);
51207         }
51208     },
51209     
51210     // private
51211     renderField : function(f){
51212         f.fieldEl = this.fieldTpl.append(this.el, [
51213                f.id, f.fieldLabel,
51214                f.labelStyle||this.labelStyle||'',
51215                this.elementStyle||'',
51216                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
51217                f.itemCls||this.itemCls||'',
51218                f.width ? f.width + this.padWidth : 160 + this.padWidth
51219        ],true);
51220     }
51221 });
51222  
51223
51224 /**
51225  * @class Roo.form.FieldSet
51226  * @extends Roo.form.Layout
51227  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51228  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
51229  * @constructor
51230  * @param {Object} config Configuration options
51231  */
51232 Roo.form.FieldSet = function(config){
51233     Roo.form.FieldSet.superclass.constructor.call(this, config);
51234 };
51235
51236 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
51237     /**
51238      * @cfg {String} legend
51239      * The text to display as the legend for the FieldSet (defaults to '')
51240      */
51241     /**
51242      * @cfg {String/Object} autoCreate
51243      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
51244      */
51245
51246     // private
51247     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
51248
51249     // private
51250     onRender : function(ct, position){
51251         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
51252         if(this.legend){
51253             this.setLegend(this.legend);
51254         }
51255     },
51256
51257     // private
51258     setLegend : function(text){
51259         if(this.rendered){
51260             this.el.child('legend').update(text);
51261         }
51262     }
51263 });/*
51264  * Based on:
51265  * Ext JS Library 1.1.1
51266  * Copyright(c) 2006-2007, Ext JS, LLC.
51267  *
51268  * Originally Released Under LGPL - original licence link has changed is not relivant.
51269  *
51270  * Fork - LGPL
51271  * <script type="text/javascript">
51272  */
51273 /**
51274  * @class Roo.form.VTypes
51275  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
51276  * @static
51277  */
51278 Roo.form.VTypes = function(){
51279     // closure these in so they are only created once.
51280     var alpha = /^[a-zA-Z_]+$/;
51281     var alphanum = /^[a-zA-Z0-9_]+$/;
51282     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
51283     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
51284
51285     // All these messages and functions are configurable
51286     return {
51287         /**
51288          * The function used to validate email addresses
51289          * @param {String} value The email address
51290          */
51291         'email' : function(v){
51292             return email.test(v);
51293         },
51294         /**
51295          * The error text to display when the email validation function returns false
51296          * @type String
51297          */
51298         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
51299         /**
51300          * The keystroke filter mask to be applied on email input
51301          * @type RegExp
51302          */
51303         'emailMask' : /[a-z0-9_\.\-@]/i,
51304
51305         /**
51306          * The function used to validate URLs
51307          * @param {String} value The URL
51308          */
51309         'url' : function(v){
51310             return url.test(v);
51311         },
51312         /**
51313          * The error text to display when the url validation function returns false
51314          * @type String
51315          */
51316         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
51317         
51318         /**
51319          * The function used to validate alpha values
51320          * @param {String} value The value
51321          */
51322         'alpha' : function(v){
51323             return alpha.test(v);
51324         },
51325         /**
51326          * The error text to display when the alpha validation function returns false
51327          * @type String
51328          */
51329         'alphaText' : 'This field should only contain letters and _',
51330         /**
51331          * The keystroke filter mask to be applied on alpha input
51332          * @type RegExp
51333          */
51334         'alphaMask' : /[a-z_]/i,
51335
51336         /**
51337          * The function used to validate alphanumeric values
51338          * @param {String} value The value
51339          */
51340         'alphanum' : function(v){
51341             return alphanum.test(v);
51342         },
51343         /**
51344          * The error text to display when the alphanumeric validation function returns false
51345          * @type String
51346          */
51347         'alphanumText' : 'This field should only contain letters, numbers and _',
51348         /**
51349          * The keystroke filter mask to be applied on alphanumeric input
51350          * @type RegExp
51351          */
51352         'alphanumMask' : /[a-z0-9_]/i
51353     };
51354 }();//<script type="text/javascript">
51355
51356 /**
51357  * @class Roo.form.FCKeditor
51358  * @extends Roo.form.TextArea
51359  * Wrapper around the FCKEditor http://www.fckeditor.net
51360  * @constructor
51361  * Creates a new FCKeditor
51362  * @param {Object} config Configuration options
51363  */
51364 Roo.form.FCKeditor = function(config){
51365     Roo.form.FCKeditor.superclass.constructor.call(this, config);
51366     this.addEvents({
51367          /**
51368          * @event editorinit
51369          * Fired when the editor is initialized - you can add extra handlers here..
51370          * @param {FCKeditor} this
51371          * @param {Object} the FCK object.
51372          */
51373         editorinit : true
51374     });
51375     
51376     
51377 };
51378 Roo.form.FCKeditor.editors = { };
51379 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
51380 {
51381     //defaultAutoCreate : {
51382     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
51383     //},
51384     // private
51385     /**
51386      * @cfg {Object} fck options - see fck manual for details.
51387      */
51388     fckconfig : false,
51389     
51390     /**
51391      * @cfg {Object} fck toolbar set (Basic or Default)
51392      */
51393     toolbarSet : 'Basic',
51394     /**
51395      * @cfg {Object} fck BasePath
51396      */ 
51397     basePath : '/fckeditor/',
51398     
51399     
51400     frame : false,
51401     
51402     value : '',
51403     
51404    
51405     onRender : function(ct, position)
51406     {
51407         if(!this.el){
51408             this.defaultAutoCreate = {
51409                 tag: "textarea",
51410                 style:"width:300px;height:60px;",
51411                 autocomplete: "new-password"
51412             };
51413         }
51414         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
51415         /*
51416         if(this.grow){
51417             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
51418             if(this.preventScrollbars){
51419                 this.el.setStyle("overflow", "hidden");
51420             }
51421             this.el.setHeight(this.growMin);
51422         }
51423         */
51424         //console.log('onrender' + this.getId() );
51425         Roo.form.FCKeditor.editors[this.getId()] = this;
51426          
51427
51428         this.replaceTextarea() ;
51429         
51430     },
51431     
51432     getEditor : function() {
51433         return this.fckEditor;
51434     },
51435     /**
51436      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
51437      * @param {Mixed} value The value to set
51438      */
51439     
51440     
51441     setValue : function(value)
51442     {
51443         //console.log('setValue: ' + value);
51444         
51445         if(typeof(value) == 'undefined') { // not sure why this is happending...
51446             return;
51447         }
51448         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
51449         
51450         //if(!this.el || !this.getEditor()) {
51451         //    this.value = value;
51452             //this.setValue.defer(100,this,[value]);    
51453         //    return;
51454         //} 
51455         
51456         if(!this.getEditor()) {
51457             return;
51458         }
51459         
51460         this.getEditor().SetData(value);
51461         
51462         //
51463
51464     },
51465
51466     /**
51467      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
51468      * @return {Mixed} value The field value
51469      */
51470     getValue : function()
51471     {
51472         
51473         if (this.frame && this.frame.dom.style.display == 'none') {
51474             return Roo.form.FCKeditor.superclass.getValue.call(this);
51475         }
51476         
51477         if(!this.el || !this.getEditor()) {
51478            
51479            // this.getValue.defer(100,this); 
51480             return this.value;
51481         }
51482        
51483         
51484         var value=this.getEditor().GetData();
51485         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
51486         return Roo.form.FCKeditor.superclass.getValue.call(this);
51487         
51488
51489     },
51490
51491     /**
51492      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
51493      * @return {Mixed} value The field value
51494      */
51495     getRawValue : function()
51496     {
51497         if (this.frame && this.frame.dom.style.display == 'none') {
51498             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
51499         }
51500         
51501         if(!this.el || !this.getEditor()) {
51502             //this.getRawValue.defer(100,this); 
51503             return this.value;
51504             return;
51505         }
51506         
51507         
51508         
51509         var value=this.getEditor().GetData();
51510         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
51511         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
51512          
51513     },
51514     
51515     setSize : function(w,h) {
51516         
51517         
51518         
51519         //if (this.frame && this.frame.dom.style.display == 'none') {
51520         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
51521         //    return;
51522         //}
51523         //if(!this.el || !this.getEditor()) {
51524         //    this.setSize.defer(100,this, [w,h]); 
51525         //    return;
51526         //}
51527         
51528         
51529         
51530         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
51531         
51532         this.frame.dom.setAttribute('width', w);
51533         this.frame.dom.setAttribute('height', h);
51534         this.frame.setSize(w,h);
51535         
51536     },
51537     
51538     toggleSourceEdit : function(value) {
51539         
51540       
51541          
51542         this.el.dom.style.display = value ? '' : 'none';
51543         this.frame.dom.style.display = value ?  'none' : '';
51544         
51545     },
51546     
51547     
51548     focus: function(tag)
51549     {
51550         if (this.frame.dom.style.display == 'none') {
51551             return Roo.form.FCKeditor.superclass.focus.call(this);
51552         }
51553         if(!this.el || !this.getEditor()) {
51554             this.focus.defer(100,this, [tag]); 
51555             return;
51556         }
51557         
51558         
51559         
51560         
51561         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
51562         this.getEditor().Focus();
51563         if (tgs.length) {
51564             if (!this.getEditor().Selection.GetSelection()) {
51565                 this.focus.defer(100,this, [tag]); 
51566                 return;
51567             }
51568             
51569             
51570             var r = this.getEditor().EditorDocument.createRange();
51571             r.setStart(tgs[0],0);
51572             r.setEnd(tgs[0],0);
51573             this.getEditor().Selection.GetSelection().removeAllRanges();
51574             this.getEditor().Selection.GetSelection().addRange(r);
51575             this.getEditor().Focus();
51576         }
51577         
51578     },
51579     
51580     
51581     
51582     replaceTextarea : function()
51583     {
51584         if ( document.getElementById( this.getId() + '___Frame' ) ) {
51585             return ;
51586         }
51587         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
51588         //{
51589             // We must check the elements firstly using the Id and then the name.
51590         var oTextarea = document.getElementById( this.getId() );
51591         
51592         var colElementsByName = document.getElementsByName( this.getId() ) ;
51593          
51594         oTextarea.style.display = 'none' ;
51595
51596         if ( oTextarea.tabIndex ) {            
51597             this.TabIndex = oTextarea.tabIndex ;
51598         }
51599         
51600         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
51601         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
51602         this.frame = Roo.get(this.getId() + '___Frame')
51603     },
51604     
51605     _getConfigHtml : function()
51606     {
51607         var sConfig = '' ;
51608
51609         for ( var o in this.fckconfig ) {
51610             sConfig += sConfig.length > 0  ? '&amp;' : '';
51611             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
51612         }
51613
51614         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
51615     },
51616     
51617     
51618     _getIFrameHtml : function()
51619     {
51620         var sFile = 'fckeditor.html' ;
51621         /* no idea what this is about..
51622         try
51623         {
51624             if ( (/fcksource=true/i).test( window.top.location.search ) )
51625                 sFile = 'fckeditor.original.html' ;
51626         }
51627         catch (e) { 
51628         */
51629
51630         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
51631         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
51632         
51633         
51634         var html = '<iframe id="' + this.getId() +
51635             '___Frame" src="' + sLink +
51636             '" width="' + this.width +
51637             '" height="' + this.height + '"' +
51638             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
51639             ' frameborder="0" scrolling="no"></iframe>' ;
51640
51641         return html ;
51642     },
51643     
51644     _insertHtmlBefore : function( html, element )
51645     {
51646         if ( element.insertAdjacentHTML )       {
51647             // IE
51648             element.insertAdjacentHTML( 'beforeBegin', html ) ;
51649         } else { // Gecko
51650             var oRange = document.createRange() ;
51651             oRange.setStartBefore( element ) ;
51652             var oFragment = oRange.createContextualFragment( html );
51653             element.parentNode.insertBefore( oFragment, element ) ;
51654         }
51655     }
51656     
51657     
51658   
51659     
51660     
51661     
51662     
51663
51664 });
51665
51666 //Roo.reg('fckeditor', Roo.form.FCKeditor);
51667
51668 function FCKeditor_OnComplete(editorInstance){
51669     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
51670     f.fckEditor = editorInstance;
51671     //console.log("loaded");
51672     f.fireEvent('editorinit', f, editorInstance);
51673
51674   
51675
51676  
51677
51678
51679
51680
51681
51682
51683
51684
51685
51686
51687
51688
51689
51690
51691
51692 //<script type="text/javascript">
51693 /**
51694  * @class Roo.form.GridField
51695  * @extends Roo.form.Field
51696  * Embed a grid (or editable grid into a form)
51697  * STATUS ALPHA
51698  * 
51699  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
51700  * it needs 
51701  * xgrid.store = Roo.data.Store
51702  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
51703  * xgrid.store.reader = Roo.data.JsonReader 
51704  * 
51705  * 
51706  * @constructor
51707  * Creates a new GridField
51708  * @param {Object} config Configuration options
51709  */
51710 Roo.form.GridField = function(config){
51711     Roo.form.GridField.superclass.constructor.call(this, config);
51712      
51713 };
51714
51715 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
51716     /**
51717      * @cfg {Number} width  - used to restrict width of grid..
51718      */
51719     width : 100,
51720     /**
51721      * @cfg {Number} height - used to restrict height of grid..
51722      */
51723     height : 50,
51724      /**
51725      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
51726          * 
51727          *}
51728      */
51729     xgrid : false, 
51730     /**
51731      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51732      * {tag: "input", type: "checkbox", autocomplete: "off"})
51733      */
51734    // defaultAutoCreate : { tag: 'div' },
51735     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
51736     /**
51737      * @cfg {String} addTitle Text to include for adding a title.
51738      */
51739     addTitle : false,
51740     //
51741     onResize : function(){
51742         Roo.form.Field.superclass.onResize.apply(this, arguments);
51743     },
51744
51745     initEvents : function(){
51746         // Roo.form.Checkbox.superclass.initEvents.call(this);
51747         // has no events...
51748        
51749     },
51750
51751
51752     getResizeEl : function(){
51753         return this.wrap;
51754     },
51755
51756     getPositionEl : function(){
51757         return this.wrap;
51758     },
51759
51760     // private
51761     onRender : function(ct, position){
51762         
51763         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
51764         var style = this.style;
51765         delete this.style;
51766         
51767         Roo.form.GridField.superclass.onRender.call(this, ct, position);
51768         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
51769         this.viewEl = this.wrap.createChild({ tag: 'div' });
51770         if (style) {
51771             this.viewEl.applyStyles(style);
51772         }
51773         if (this.width) {
51774             this.viewEl.setWidth(this.width);
51775         }
51776         if (this.height) {
51777             this.viewEl.setHeight(this.height);
51778         }
51779         //if(this.inputValue !== undefined){
51780         //this.setValue(this.value);
51781         
51782         
51783         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
51784         
51785         
51786         this.grid.render();
51787         this.grid.getDataSource().on('remove', this.refreshValue, this);
51788         this.grid.getDataSource().on('update', this.refreshValue, this);
51789         this.grid.on('afteredit', this.refreshValue, this);
51790  
51791     },
51792      
51793     
51794     /**
51795      * Sets the value of the item. 
51796      * @param {String} either an object  or a string..
51797      */
51798     setValue : function(v){
51799         //this.value = v;
51800         v = v || []; // empty set..
51801         // this does not seem smart - it really only affects memoryproxy grids..
51802         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
51803             var ds = this.grid.getDataSource();
51804             // assumes a json reader..
51805             var data = {}
51806             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
51807             ds.loadData( data);
51808         }
51809         // clear selection so it does not get stale.
51810         if (this.grid.sm) { 
51811             this.grid.sm.clearSelections();
51812         }
51813         
51814         Roo.form.GridField.superclass.setValue.call(this, v);
51815         this.refreshValue();
51816         // should load data in the grid really....
51817     },
51818     
51819     // private
51820     refreshValue: function() {
51821          var val = [];
51822         this.grid.getDataSource().each(function(r) {
51823             val.push(r.data);
51824         });
51825         this.el.dom.value = Roo.encode(val);
51826     }
51827     
51828      
51829     
51830     
51831 });/*
51832  * Based on:
51833  * Ext JS Library 1.1.1
51834  * Copyright(c) 2006-2007, Ext JS, LLC.
51835  *
51836  * Originally Released Under LGPL - original licence link has changed is not relivant.
51837  *
51838  * Fork - LGPL
51839  * <script type="text/javascript">
51840  */
51841 /**
51842  * @class Roo.form.DisplayField
51843  * @extends Roo.form.Field
51844  * A generic Field to display non-editable data.
51845  * @cfg {Boolean} closable (true|false) default false
51846  * @constructor
51847  * Creates a new Display Field item.
51848  * @param {Object} config Configuration options
51849  */
51850 Roo.form.DisplayField = function(config){
51851     Roo.form.DisplayField.superclass.constructor.call(this, config);
51852     
51853     this.addEvents({
51854         /**
51855          * @event close
51856          * Fires after the click the close btn
51857              * @param {Roo.form.DisplayField} this
51858              */
51859         close : true
51860     });
51861 };
51862
51863 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
51864     inputType:      'hidden',
51865     allowBlank:     true,
51866     readOnly:         true,
51867     
51868  
51869     /**
51870      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51871      */
51872     focusClass : undefined,
51873     /**
51874      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51875      */
51876     fieldClass: 'x-form-field',
51877     
51878      /**
51879      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
51880      */
51881     valueRenderer: undefined,
51882     
51883     width: 100,
51884     /**
51885      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
51886      * {tag: "input", type: "checkbox", autocomplete: "off"})
51887      */
51888      
51889  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
51890  
51891     closable : false,
51892     
51893     onResize : function(){
51894         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
51895         
51896     },
51897
51898     initEvents : function(){
51899         // Roo.form.Checkbox.superclass.initEvents.call(this);
51900         // has no events...
51901         
51902         if(this.closable){
51903             this.closeEl.on('click', this.onClose, this);
51904         }
51905        
51906     },
51907
51908
51909     getResizeEl : function(){
51910         return this.wrap;
51911     },
51912
51913     getPositionEl : function(){
51914         return this.wrap;
51915     },
51916
51917     // private
51918     onRender : function(ct, position){
51919         
51920         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
51921         //if(this.inputValue !== undefined){
51922         this.wrap = this.el.wrap();
51923         
51924         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
51925         
51926         if(this.closable){
51927             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
51928         }
51929         
51930         if (this.bodyStyle) {
51931             this.viewEl.applyStyles(this.bodyStyle);
51932         }
51933         //this.viewEl.setStyle('padding', '2px');
51934         
51935         this.setValue(this.value);
51936         
51937     },
51938 /*
51939     // private
51940     initValue : Roo.emptyFn,
51941
51942   */
51943
51944         // private
51945     onClick : function(){
51946         
51947     },
51948
51949     /**
51950      * Sets the checked state of the checkbox.
51951      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
51952      */
51953     setValue : function(v){
51954         this.value = v;
51955         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
51956         // this might be called before we have a dom element..
51957         if (!this.viewEl) {
51958             return;
51959         }
51960         this.viewEl.dom.innerHTML = html;
51961         Roo.form.DisplayField.superclass.setValue.call(this, v);
51962
51963     },
51964     
51965     onClose : function(e)
51966     {
51967         e.preventDefault();
51968         
51969         this.fireEvent('close', this);
51970     }
51971 });/*
51972  * 
51973  * Licence- LGPL
51974  * 
51975  */
51976
51977 /**
51978  * @class Roo.form.DayPicker
51979  * @extends Roo.form.Field
51980  * A Day picker show [M] [T] [W] ....
51981  * @constructor
51982  * Creates a new Day Picker
51983  * @param {Object} config Configuration options
51984  */
51985 Roo.form.DayPicker= function(config){
51986     Roo.form.DayPicker.superclass.constructor.call(this, config);
51987      
51988 };
51989
51990 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
51991     /**
51992      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
51993      */
51994     focusClass : undefined,
51995     /**
51996      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
51997      */
51998     fieldClass: "x-form-field",
51999    
52000     /**
52001      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52002      * {tag: "input", type: "checkbox", autocomplete: "off"})
52003      */
52004     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52005     
52006    
52007     actionMode : 'viewEl', 
52008     //
52009     // private
52010  
52011     inputType : 'hidden',
52012     
52013      
52014     inputElement: false, // real input element?
52015     basedOn: false, // ????
52016     
52017     isFormField: true, // not sure where this is needed!!!!
52018
52019     onResize : function(){
52020         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52021         if(!this.boxLabel){
52022             this.el.alignTo(this.wrap, 'c-c');
52023         }
52024     },
52025
52026     initEvents : function(){
52027         Roo.form.Checkbox.superclass.initEvents.call(this);
52028         this.el.on("click", this.onClick,  this);
52029         this.el.on("change", this.onClick,  this);
52030     },
52031
52032
52033     getResizeEl : function(){
52034         return this.wrap;
52035     },
52036
52037     getPositionEl : function(){
52038         return this.wrap;
52039     },
52040
52041     
52042     // private
52043     onRender : function(ct, position){
52044         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
52045        
52046         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
52047         
52048         var r1 = '<table><tr>';
52049         var r2 = '<tr class="x-form-daypick-icons">';
52050         for (var i=0; i < 7; i++) {
52051             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
52052             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
52053         }
52054         
52055         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
52056         viewEl.select('img').on('click', this.onClick, this);
52057         this.viewEl = viewEl;   
52058         
52059         
52060         // this will not work on Chrome!!!
52061         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
52062         this.el.on('propertychange', this.setFromHidden,  this);  //ie
52063         
52064         
52065           
52066
52067     },
52068
52069     // private
52070     initValue : Roo.emptyFn,
52071
52072     /**
52073      * Returns the checked state of the checkbox.
52074      * @return {Boolean} True if checked, else false
52075      */
52076     getValue : function(){
52077         return this.el.dom.value;
52078         
52079     },
52080
52081         // private
52082     onClick : function(e){ 
52083         //this.setChecked(!this.checked);
52084         Roo.get(e.target).toggleClass('x-menu-item-checked');
52085         this.refreshValue();
52086         //if(this.el.dom.checked != this.checked){
52087         //    this.setValue(this.el.dom.checked);
52088        // }
52089     },
52090     
52091     // private
52092     refreshValue : function()
52093     {
52094         var val = '';
52095         this.viewEl.select('img',true).each(function(e,i,n)  {
52096             val += e.is(".x-menu-item-checked") ? String(n) : '';
52097         });
52098         this.setValue(val, true);
52099     },
52100
52101     /**
52102      * Sets the checked state of the checkbox.
52103      * On is always based on a string comparison between inputValue and the param.
52104      * @param {Boolean/String} value - the value to set 
52105      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
52106      */
52107     setValue : function(v,suppressEvent){
52108         if (!this.el.dom) {
52109             return;
52110         }
52111         var old = this.el.dom.value ;
52112         this.el.dom.value = v;
52113         if (suppressEvent) {
52114             return ;
52115         }
52116          
52117         // update display..
52118         this.viewEl.select('img',true).each(function(e,i,n)  {
52119             
52120             var on = e.is(".x-menu-item-checked");
52121             var newv = v.indexOf(String(n)) > -1;
52122             if (on != newv) {
52123                 e.toggleClass('x-menu-item-checked');
52124             }
52125             
52126         });
52127         
52128         
52129         this.fireEvent('change', this, v, old);
52130         
52131         
52132     },
52133    
52134     // handle setting of hidden value by some other method!!?!?
52135     setFromHidden: function()
52136     {
52137         if(!this.el){
52138             return;
52139         }
52140         //console.log("SET FROM HIDDEN");
52141         //alert('setFrom hidden');
52142         this.setValue(this.el.dom.value);
52143     },
52144     
52145     onDestroy : function()
52146     {
52147         if(this.viewEl){
52148             Roo.get(this.viewEl).remove();
52149         }
52150          
52151         Roo.form.DayPicker.superclass.onDestroy.call(this);
52152     }
52153
52154 });/*
52155  * RooJS Library 1.1.1
52156  * Copyright(c) 2008-2011  Alan Knowles
52157  *
52158  * License - LGPL
52159  */
52160  
52161
52162 /**
52163  * @class Roo.form.ComboCheck
52164  * @extends Roo.form.ComboBox
52165  * A combobox for multiple select items.
52166  *
52167  * FIXME - could do with a reset button..
52168  * 
52169  * @constructor
52170  * Create a new ComboCheck
52171  * @param {Object} config Configuration options
52172  */
52173 Roo.form.ComboCheck = function(config){
52174     Roo.form.ComboCheck.superclass.constructor.call(this, config);
52175     // should verify some data...
52176     // like
52177     // hiddenName = required..
52178     // displayField = required
52179     // valudField == required
52180     var req= [ 'hiddenName', 'displayField', 'valueField' ];
52181     var _t = this;
52182     Roo.each(req, function(e) {
52183         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
52184             throw "Roo.form.ComboCheck : missing value for: " + e;
52185         }
52186     });
52187     
52188     
52189 };
52190
52191 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
52192      
52193      
52194     editable : false,
52195      
52196     selectedClass: 'x-menu-item-checked', 
52197     
52198     // private
52199     onRender : function(ct, position){
52200         var _t = this;
52201         
52202         
52203         
52204         if(!this.tpl){
52205             var cls = 'x-combo-list';
52206
52207             
52208             this.tpl =  new Roo.Template({
52209                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
52210                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
52211                    '<span>{' + this.displayField + '}</span>' +
52212                     '</div>' 
52213                 
52214             });
52215         }
52216  
52217         
52218         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
52219         this.view.singleSelect = false;
52220         this.view.multiSelect = true;
52221         this.view.toggleSelect = true;
52222         this.pageTb.add(new Roo.Toolbar.Fill(), {
52223             
52224             text: 'Done',
52225             handler: function()
52226             {
52227                 _t.collapse();
52228             }
52229         });
52230     },
52231     
52232     onViewOver : function(e, t){
52233         // do nothing...
52234         return;
52235         
52236     },
52237     
52238     onViewClick : function(doFocus,index){
52239         return;
52240         
52241     },
52242     select: function () {
52243         //Roo.log("SELECT CALLED");
52244     },
52245      
52246     selectByValue : function(xv, scrollIntoView){
52247         var ar = this.getValueArray();
52248         var sels = [];
52249         
52250         Roo.each(ar, function(v) {
52251             if(v === undefined || v === null){
52252                 return;
52253             }
52254             var r = this.findRecord(this.valueField, v);
52255             if(r){
52256                 sels.push(this.store.indexOf(r))
52257                 
52258             }
52259         },this);
52260         this.view.select(sels);
52261         return false;
52262     },
52263     
52264     
52265     
52266     onSelect : function(record, index){
52267        // Roo.log("onselect Called");
52268        // this is only called by the clear button now..
52269         this.view.clearSelections();
52270         this.setValue('[]');
52271         if (this.value != this.valueBefore) {
52272             this.fireEvent('change', this, this.value, this.valueBefore);
52273             this.valueBefore = this.value;
52274         }
52275     },
52276     getValueArray : function()
52277     {
52278         var ar = [] ;
52279         
52280         try {
52281             //Roo.log(this.value);
52282             if (typeof(this.value) == 'undefined') {
52283                 return [];
52284             }
52285             var ar = Roo.decode(this.value);
52286             return  ar instanceof Array ? ar : []; //?? valid?
52287             
52288         } catch(e) {
52289             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
52290             return [];
52291         }
52292          
52293     },
52294     expand : function ()
52295     {
52296         
52297         Roo.form.ComboCheck.superclass.expand.call(this);
52298         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
52299         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
52300         
52301
52302     },
52303     
52304     collapse : function(){
52305         Roo.form.ComboCheck.superclass.collapse.call(this);
52306         var sl = this.view.getSelectedIndexes();
52307         var st = this.store;
52308         var nv = [];
52309         var tv = [];
52310         var r;
52311         Roo.each(sl, function(i) {
52312             r = st.getAt(i);
52313             nv.push(r.get(this.valueField));
52314         },this);
52315         this.setValue(Roo.encode(nv));
52316         if (this.value != this.valueBefore) {
52317
52318             this.fireEvent('change', this, this.value, this.valueBefore);
52319             this.valueBefore = this.value;
52320         }
52321         
52322     },
52323     
52324     setValue : function(v){
52325         // Roo.log(v);
52326         this.value = v;
52327         
52328         var vals = this.getValueArray();
52329         var tv = [];
52330         Roo.each(vals, function(k) {
52331             var r = this.findRecord(this.valueField, k);
52332             if(r){
52333                 tv.push(r.data[this.displayField]);
52334             }else if(this.valueNotFoundText !== undefined){
52335                 tv.push( this.valueNotFoundText );
52336             }
52337         },this);
52338        // Roo.log(tv);
52339         
52340         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
52341         this.hiddenField.value = v;
52342         this.value = v;
52343     }
52344     
52345 });/*
52346  * Based on:
52347  * Ext JS Library 1.1.1
52348  * Copyright(c) 2006-2007, Ext JS, LLC.
52349  *
52350  * Originally Released Under LGPL - original licence link has changed is not relivant.
52351  *
52352  * Fork - LGPL
52353  * <script type="text/javascript">
52354  */
52355  
52356 /**
52357  * @class Roo.form.Signature
52358  * @extends Roo.form.Field
52359  * Signature field.  
52360  * @constructor
52361  * 
52362  * @param {Object} config Configuration options
52363  */
52364
52365 Roo.form.Signature = function(config){
52366     Roo.form.Signature.superclass.constructor.call(this, config);
52367     
52368     this.addEvents({// not in used??
52369          /**
52370          * @event confirm
52371          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
52372              * @param {Roo.form.Signature} combo This combo box
52373              */
52374         'confirm' : true,
52375         /**
52376          * @event reset
52377          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
52378              * @param {Roo.form.ComboBox} combo This combo box
52379              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
52380              */
52381         'reset' : true
52382     });
52383 };
52384
52385 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
52386     /**
52387      * @cfg {Object} labels Label to use when rendering a form.
52388      * defaults to 
52389      * labels : { 
52390      *      clear : "Clear",
52391      *      confirm : "Confirm"
52392      *  }
52393      */
52394     labels : { 
52395         clear : "Clear",
52396         confirm : "Confirm"
52397     },
52398     /**
52399      * @cfg {Number} width The signature panel width (defaults to 300)
52400      */
52401     width: 300,
52402     /**
52403      * @cfg {Number} height The signature panel height (defaults to 100)
52404      */
52405     height : 100,
52406     /**
52407      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
52408      */
52409     allowBlank : false,
52410     
52411     //private
52412     // {Object} signPanel The signature SVG panel element (defaults to {})
52413     signPanel : {},
52414     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
52415     isMouseDown : false,
52416     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
52417     isConfirmed : false,
52418     // {String} signatureTmp SVG mapping string (defaults to empty string)
52419     signatureTmp : '',
52420     
52421     
52422     defaultAutoCreate : { // modified by initCompnoent..
52423         tag: "input",
52424         type:"hidden"
52425     },
52426
52427     // private
52428     onRender : function(ct, position){
52429         
52430         Roo.form.Signature.superclass.onRender.call(this, ct, position);
52431         
52432         this.wrap = this.el.wrap({
52433             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
52434         });
52435         
52436         this.createToolbar(this);
52437         this.signPanel = this.wrap.createChild({
52438                 tag: 'div',
52439                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
52440             }, this.el
52441         );
52442             
52443         this.svgID = Roo.id();
52444         this.svgEl = this.signPanel.createChild({
52445               xmlns : 'http://www.w3.org/2000/svg',
52446               tag : 'svg',
52447               id : this.svgID + "-svg",
52448               width: this.width,
52449               height: this.height,
52450               viewBox: '0 0 '+this.width+' '+this.height,
52451               cn : [
52452                 {
52453                     tag: "rect",
52454                     id: this.svgID + "-svg-r",
52455                     width: this.width,
52456                     height: this.height,
52457                     fill: "#ffa"
52458                 },
52459                 {
52460                     tag: "line",
52461                     id: this.svgID + "-svg-l",
52462                     x1: "0", // start
52463                     y1: (this.height*0.8), // start set the line in 80% of height
52464                     x2: this.width, // end
52465                     y2: (this.height*0.8), // end set the line in 80% of height
52466                     'stroke': "#666",
52467                     'stroke-width': "1",
52468                     'stroke-dasharray': "3",
52469                     'shape-rendering': "crispEdges",
52470                     'pointer-events': "none"
52471                 },
52472                 {
52473                     tag: "path",
52474                     id: this.svgID + "-svg-p",
52475                     'stroke': "navy",
52476                     'stroke-width': "3",
52477                     'fill': "none",
52478                     'pointer-events': 'none'
52479                 }
52480               ]
52481         });
52482         this.createSVG();
52483         this.svgBox = this.svgEl.dom.getScreenCTM();
52484     },
52485     createSVG : function(){ 
52486         var svg = this.signPanel;
52487         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
52488         var t = this;
52489
52490         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
52491         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
52492         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
52493         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
52494         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
52495         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
52496         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
52497         
52498     },
52499     isTouchEvent : function(e){
52500         return e.type.match(/^touch/);
52501     },
52502     getCoords : function (e) {
52503         var pt    = this.svgEl.dom.createSVGPoint();
52504         pt.x = e.clientX; 
52505         pt.y = e.clientY;
52506         if (this.isTouchEvent(e)) {
52507             pt.x =  e.targetTouches[0].clientX;
52508             pt.y = e.targetTouches[0].clientY;
52509         }
52510         var a = this.svgEl.dom.getScreenCTM();
52511         var b = a.inverse();
52512         var mx = pt.matrixTransform(b);
52513         return mx.x + ',' + mx.y;
52514     },
52515     //mouse event headler 
52516     down : function (e) {
52517         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
52518         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
52519         
52520         this.isMouseDown = true;
52521         
52522         e.preventDefault();
52523     },
52524     move : function (e) {
52525         if (this.isMouseDown) {
52526             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
52527             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
52528         }
52529         
52530         e.preventDefault();
52531     },
52532     up : function (e) {
52533         this.isMouseDown = false;
52534         var sp = this.signatureTmp.split(' ');
52535         
52536         if(sp.length > 1){
52537             if(!sp[sp.length-2].match(/^L/)){
52538                 sp.pop();
52539                 sp.pop();
52540                 sp.push("");
52541                 this.signatureTmp = sp.join(" ");
52542             }
52543         }
52544         if(this.getValue() != this.signatureTmp){
52545             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52546             this.isConfirmed = false;
52547         }
52548         e.preventDefault();
52549     },
52550     
52551     /**
52552      * Protected method that will not generally be called directly. It
52553      * is called when the editor creates its toolbar. Override this method if you need to
52554      * add custom toolbar buttons.
52555      * @param {HtmlEditor} editor
52556      */
52557     createToolbar : function(editor){
52558          function btn(id, toggle, handler){
52559             var xid = fid + '-'+ id ;
52560             return {
52561                 id : xid,
52562                 cmd : id,
52563                 cls : 'x-btn-icon x-edit-'+id,
52564                 enableToggle:toggle !== false,
52565                 scope: editor, // was editor...
52566                 handler:handler||editor.relayBtnCmd,
52567                 clickEvent:'mousedown',
52568                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
52569                 tabIndex:-1
52570             };
52571         }
52572         
52573         
52574         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
52575         this.tb = tb;
52576         this.tb.add(
52577            {
52578                 cls : ' x-signature-btn x-signature-'+id,
52579                 scope: editor, // was editor...
52580                 handler: this.reset,
52581                 clickEvent:'mousedown',
52582                 text: this.labels.clear
52583             },
52584             {
52585                  xtype : 'Fill',
52586                  xns: Roo.Toolbar
52587             }, 
52588             {
52589                 cls : '  x-signature-btn x-signature-'+id,
52590                 scope: editor, // was editor...
52591                 handler: this.confirmHandler,
52592                 clickEvent:'mousedown',
52593                 text: this.labels.confirm
52594             }
52595         );
52596     
52597     },
52598     //public
52599     /**
52600      * when user is clicked confirm then show this image.....
52601      * 
52602      * @return {String} Image Data URI
52603      */
52604     getImageDataURI : function(){
52605         var svg = this.svgEl.dom.parentNode.innerHTML;
52606         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
52607         return src; 
52608     },
52609     /**
52610      * 
52611      * @return {Boolean} this.isConfirmed
52612      */
52613     getConfirmed : function(){
52614         return this.isConfirmed;
52615     },
52616     /**
52617      * 
52618      * @return {Number} this.width
52619      */
52620     getWidth : function(){
52621         return this.width;
52622     },
52623     /**
52624      * 
52625      * @return {Number} this.height
52626      */
52627     getHeight : function(){
52628         return this.height;
52629     },
52630     // private
52631     getSignature : function(){
52632         return this.signatureTmp;
52633     },
52634     // private
52635     reset : function(){
52636         this.signatureTmp = '';
52637         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52638         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
52639         this.isConfirmed = false;
52640         Roo.form.Signature.superclass.reset.call(this);
52641     },
52642     setSignature : function(s){
52643         this.signatureTmp = s;
52644         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
52645         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
52646         this.setValue(s);
52647         this.isConfirmed = false;
52648         Roo.form.Signature.superclass.reset.call(this);
52649     }, 
52650     test : function(){
52651 //        Roo.log(this.signPanel.dom.contentWindow.up())
52652     },
52653     //private
52654     setConfirmed : function(){
52655         
52656         
52657         
52658 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
52659     },
52660     // private
52661     confirmHandler : function(){
52662         if(!this.getSignature()){
52663             return;
52664         }
52665         
52666         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
52667         this.setValue(this.getSignature());
52668         this.isConfirmed = true;
52669         
52670         this.fireEvent('confirm', this);
52671     },
52672     // private
52673     // Subclasses should provide the validation implementation by overriding this
52674     validateValue : function(value){
52675         if(this.allowBlank){
52676             return true;
52677         }
52678         
52679         if(this.isConfirmed){
52680             return true;
52681         }
52682         return false;
52683     }
52684 });/*
52685  * Based on:
52686  * Ext JS Library 1.1.1
52687  * Copyright(c) 2006-2007, Ext JS, LLC.
52688  *
52689  * Originally Released Under LGPL - original licence link has changed is not relivant.
52690  *
52691  * Fork - LGPL
52692  * <script type="text/javascript">
52693  */
52694  
52695
52696 /**
52697  * @class Roo.form.ComboBox
52698  * @extends Roo.form.TriggerField
52699  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
52700  * @constructor
52701  * Create a new ComboBox.
52702  * @param {Object} config Configuration options
52703  */
52704 Roo.form.Select = function(config){
52705     Roo.form.Select.superclass.constructor.call(this, config);
52706      
52707 };
52708
52709 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
52710     /**
52711      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
52712      */
52713     /**
52714      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
52715      * rendering into an Roo.Editor, defaults to false)
52716      */
52717     /**
52718      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
52719      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
52720      */
52721     /**
52722      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
52723      */
52724     /**
52725      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
52726      * the dropdown list (defaults to undefined, with no header element)
52727      */
52728
52729      /**
52730      * @cfg {String/Roo.Template} tpl The template to use to render the output
52731      */
52732      
52733     // private
52734     defaultAutoCreate : {tag: "select"  },
52735     /**
52736      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
52737      */
52738     listWidth: undefined,
52739     /**
52740      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
52741      * mode = 'remote' or 'text' if mode = 'local')
52742      */
52743     displayField: undefined,
52744     /**
52745      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
52746      * mode = 'remote' or 'value' if mode = 'local'). 
52747      * Note: use of a valueField requires the user make a selection
52748      * in order for a value to be mapped.
52749      */
52750     valueField: undefined,
52751     
52752     
52753     /**
52754      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
52755      * field's data value (defaults to the underlying DOM element's name)
52756      */
52757     hiddenName: undefined,
52758     /**
52759      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
52760      */
52761     listClass: '',
52762     /**
52763      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
52764      */
52765     selectedClass: 'x-combo-selected',
52766     /**
52767      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
52768      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
52769      * which displays a downward arrow icon).
52770      */
52771     triggerClass : 'x-form-arrow-trigger',
52772     /**
52773      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
52774      */
52775     shadow:'sides',
52776     /**
52777      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
52778      * anchor positions (defaults to 'tl-bl')
52779      */
52780     listAlign: 'tl-bl?',
52781     /**
52782      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
52783      */
52784     maxHeight: 300,
52785     /**
52786      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
52787      * query specified by the allQuery config option (defaults to 'query')
52788      */
52789     triggerAction: 'query',
52790     /**
52791      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
52792      * (defaults to 4, does not apply if editable = false)
52793      */
52794     minChars : 4,
52795     /**
52796      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
52797      * delay (typeAheadDelay) if it matches a known value (defaults to false)
52798      */
52799     typeAhead: false,
52800     /**
52801      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
52802      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
52803      */
52804     queryDelay: 500,
52805     /**
52806      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
52807      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
52808      */
52809     pageSize: 0,
52810     /**
52811      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
52812      * when editable = true (defaults to false)
52813      */
52814     selectOnFocus:false,
52815     /**
52816      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
52817      */
52818     queryParam: 'query',
52819     /**
52820      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
52821      * when mode = 'remote' (defaults to 'Loading...')
52822      */
52823     loadingText: 'Loading...',
52824     /**
52825      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
52826      */
52827     resizable: false,
52828     /**
52829      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
52830      */
52831     handleHeight : 8,
52832     /**
52833      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
52834      * traditional select (defaults to true)
52835      */
52836     editable: true,
52837     /**
52838      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
52839      */
52840     allQuery: '',
52841     /**
52842      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
52843      */
52844     mode: 'remote',
52845     /**
52846      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
52847      * listWidth has a higher value)
52848      */
52849     minListWidth : 70,
52850     /**
52851      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
52852      * allow the user to set arbitrary text into the field (defaults to false)
52853      */
52854     forceSelection:false,
52855     /**
52856      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
52857      * if typeAhead = true (defaults to 250)
52858      */
52859     typeAheadDelay : 250,
52860     /**
52861      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
52862      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
52863      */
52864     valueNotFoundText : undefined,
52865     
52866     /**
52867      * @cfg {String} defaultValue The value displayed after loading the store.
52868      */
52869     defaultValue: '',
52870     
52871     /**
52872      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
52873      */
52874     blockFocus : false,
52875     
52876     /**
52877      * @cfg {Boolean} disableClear Disable showing of clear button.
52878      */
52879     disableClear : false,
52880     /**
52881      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
52882      */
52883     alwaysQuery : false,
52884     
52885     //private
52886     addicon : false,
52887     editicon: false,
52888     
52889     // element that contains real text value.. (when hidden is used..)
52890      
52891     // private
52892     onRender : function(ct, position){
52893         Roo.form.Field.prototype.onRender.call(this, ct, position);
52894         
52895         if(this.store){
52896             this.store.on('beforeload', this.onBeforeLoad, this);
52897             this.store.on('load', this.onLoad, this);
52898             this.store.on('loadexception', this.onLoadException, this);
52899             this.store.load({});
52900         }
52901         
52902         
52903         
52904     },
52905
52906     // private
52907     initEvents : function(){
52908         //Roo.form.ComboBox.superclass.initEvents.call(this);
52909  
52910     },
52911
52912     onDestroy : function(){
52913        
52914         if(this.store){
52915             this.store.un('beforeload', this.onBeforeLoad, this);
52916             this.store.un('load', this.onLoad, this);
52917             this.store.un('loadexception', this.onLoadException, this);
52918         }
52919         //Roo.form.ComboBox.superclass.onDestroy.call(this);
52920     },
52921
52922     // private
52923     fireKey : function(e){
52924         if(e.isNavKeyPress() && !this.list.isVisible()){
52925             this.fireEvent("specialkey", this, e);
52926         }
52927     },
52928
52929     // private
52930     onResize: function(w, h){
52931         
52932         return; 
52933     
52934         
52935     },
52936
52937     /**
52938      * Allow or prevent the user from directly editing the field text.  If false is passed,
52939      * the user will only be able to select from the items defined in the dropdown list.  This method
52940      * is the runtime equivalent of setting the 'editable' config option at config time.
52941      * @param {Boolean} value True to allow the user to directly edit the field text
52942      */
52943     setEditable : function(value){
52944          
52945     },
52946
52947     // private
52948     onBeforeLoad : function(){
52949         
52950         Roo.log("Select before load");
52951         return;
52952     
52953         this.innerList.update(this.loadingText ?
52954                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
52955         //this.restrictHeight();
52956         this.selectedIndex = -1;
52957     },
52958
52959     // private
52960     onLoad : function(){
52961
52962     
52963         var dom = this.el.dom;
52964         dom.innerHTML = '';
52965          var od = dom.ownerDocument;
52966          
52967         if (this.emptyText) {
52968             var op = od.createElement('option');
52969             op.setAttribute('value', '');
52970             op.innerHTML = String.format('{0}', this.emptyText);
52971             dom.appendChild(op);
52972         }
52973         if(this.store.getCount() > 0){
52974            
52975             var vf = this.valueField;
52976             var df = this.displayField;
52977             this.store.data.each(function(r) {
52978                 // which colmsn to use... testing - cdoe / title..
52979                 var op = od.createElement('option');
52980                 op.setAttribute('value', r.data[vf]);
52981                 op.innerHTML = String.format('{0}', r.data[df]);
52982                 dom.appendChild(op);
52983             });
52984             if (typeof(this.defaultValue != 'undefined')) {
52985                 this.setValue(this.defaultValue);
52986             }
52987             
52988              
52989         }else{
52990             //this.onEmptyResults();
52991         }
52992         //this.el.focus();
52993     },
52994     // private
52995     onLoadException : function()
52996     {
52997         dom.innerHTML = '';
52998             
52999         Roo.log("Select on load exception");
53000         return;
53001     
53002         this.collapse();
53003         Roo.log(this.store.reader.jsonData);
53004         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53005             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53006         }
53007         
53008         
53009     },
53010     // private
53011     onTypeAhead : function(){
53012          
53013     },
53014
53015     // private
53016     onSelect : function(record, index){
53017         Roo.log('on select?');
53018         return;
53019         if(this.fireEvent('beforeselect', this, record, index) !== false){
53020             this.setFromData(index > -1 ? record.data : false);
53021             this.collapse();
53022             this.fireEvent('select', this, record, index);
53023         }
53024     },
53025
53026     /**
53027      * Returns the currently selected field value or empty string if no value is set.
53028      * @return {String} value The selected value
53029      */
53030     getValue : function(){
53031         var dom = this.el.dom;
53032         this.value = dom.options[dom.selectedIndex].value;
53033         return this.value;
53034         
53035     },
53036
53037     /**
53038      * Clears any text/value currently set in the field
53039      */
53040     clearValue : function(){
53041         this.value = '';
53042         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
53043         
53044     },
53045
53046     /**
53047      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
53048      * will be displayed in the field.  If the value does not match the data value of an existing item,
53049      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
53050      * Otherwise the field will be blank (although the value will still be set).
53051      * @param {String} value The value to match
53052      */
53053     setValue : function(v){
53054         var d = this.el.dom;
53055         for (var i =0; i < d.options.length;i++) {
53056             if (v == d.options[i].value) {
53057                 d.selectedIndex = i;
53058                 this.value = v;
53059                 return;
53060             }
53061         }
53062         this.clearValue();
53063     },
53064     /**
53065      * @property {Object} the last set data for the element
53066      */
53067     
53068     lastData : false,
53069     /**
53070      * Sets the value of the field based on a object which is related to the record format for the store.
53071      * @param {Object} value the value to set as. or false on reset?
53072      */
53073     setFromData : function(o){
53074         Roo.log('setfrom data?');
53075          
53076         
53077         
53078     },
53079     // private
53080     reset : function(){
53081         this.clearValue();
53082     },
53083     // private
53084     findRecord : function(prop, value){
53085         
53086         return false;
53087     
53088         var record;
53089         if(this.store.getCount() > 0){
53090             this.store.each(function(r){
53091                 if(r.data[prop] == value){
53092                     record = r;
53093                     return false;
53094                 }
53095                 return true;
53096             });
53097         }
53098         return record;
53099     },
53100     
53101     getName: function()
53102     {
53103         // returns hidden if it's set..
53104         if (!this.rendered) {return ''};
53105         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
53106         
53107     },
53108      
53109
53110     
53111
53112     // private
53113     onEmptyResults : function(){
53114         Roo.log('empty results');
53115         //this.collapse();
53116     },
53117
53118     /**
53119      * Returns true if the dropdown list is expanded, else false.
53120      */
53121     isExpanded : function(){
53122         return false;
53123     },
53124
53125     /**
53126      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
53127      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53128      * @param {String} value The data value of the item to select
53129      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53130      * selected item if it is not currently in view (defaults to true)
53131      * @return {Boolean} True if the value matched an item in the list, else false
53132      */
53133     selectByValue : function(v, scrollIntoView){
53134         Roo.log('select By Value');
53135         return false;
53136     
53137         if(v !== undefined && v !== null){
53138             var r = this.findRecord(this.valueField || this.displayField, v);
53139             if(r){
53140                 this.select(this.store.indexOf(r), scrollIntoView);
53141                 return true;
53142             }
53143         }
53144         return false;
53145     },
53146
53147     /**
53148      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
53149      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53150      * @param {Number} index The zero-based index of the list item to select
53151      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53152      * selected item if it is not currently in view (defaults to true)
53153      */
53154     select : function(index, scrollIntoView){
53155         Roo.log('select ');
53156         return  ;
53157         
53158         this.selectedIndex = index;
53159         this.view.select(index);
53160         if(scrollIntoView !== false){
53161             var el = this.view.getNode(index);
53162             if(el){
53163                 this.innerList.scrollChildIntoView(el, false);
53164             }
53165         }
53166     },
53167
53168       
53169
53170     // private
53171     validateBlur : function(){
53172         
53173         return;
53174         
53175     },
53176
53177     // private
53178     initQuery : function(){
53179         this.doQuery(this.getRawValue());
53180     },
53181
53182     // private
53183     doForce : function(){
53184         if(this.el.dom.value.length > 0){
53185             this.el.dom.value =
53186                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
53187              
53188         }
53189     },
53190
53191     /**
53192      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
53193      * query allowing the query action to be canceled if needed.
53194      * @param {String} query The SQL query to execute
53195      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
53196      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
53197      * saved in the current store (defaults to false)
53198      */
53199     doQuery : function(q, forceAll){
53200         
53201         Roo.log('doQuery?');
53202         if(q === undefined || q === null){
53203             q = '';
53204         }
53205         var qe = {
53206             query: q,
53207             forceAll: forceAll,
53208             combo: this,
53209             cancel:false
53210         };
53211         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
53212             return false;
53213         }
53214         q = qe.query;
53215         forceAll = qe.forceAll;
53216         if(forceAll === true || (q.length >= this.minChars)){
53217             if(this.lastQuery != q || this.alwaysQuery){
53218                 this.lastQuery = q;
53219                 if(this.mode == 'local'){
53220                     this.selectedIndex = -1;
53221                     if(forceAll){
53222                         this.store.clearFilter();
53223                     }else{
53224                         this.store.filter(this.displayField, q);
53225                     }
53226                     this.onLoad();
53227                 }else{
53228                     this.store.baseParams[this.queryParam] = q;
53229                     this.store.load({
53230                         params: this.getParams(q)
53231                     });
53232                     this.expand();
53233                 }
53234             }else{
53235                 this.selectedIndex = -1;
53236                 this.onLoad();   
53237             }
53238         }
53239     },
53240
53241     // private
53242     getParams : function(q){
53243         var p = {};
53244         //p[this.queryParam] = q;
53245         if(this.pageSize){
53246             p.start = 0;
53247             p.limit = this.pageSize;
53248         }
53249         return p;
53250     },
53251
53252     /**
53253      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
53254      */
53255     collapse : function(){
53256         
53257     },
53258
53259     // private
53260     collapseIf : function(e){
53261         
53262     },
53263
53264     /**
53265      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
53266      */
53267     expand : function(){
53268         
53269     } ,
53270
53271     // private
53272      
53273
53274     /** 
53275     * @cfg {Boolean} grow 
53276     * @hide 
53277     */
53278     /** 
53279     * @cfg {Number} growMin 
53280     * @hide 
53281     */
53282     /** 
53283     * @cfg {Number} growMax 
53284     * @hide 
53285     */
53286     /**
53287      * @hide
53288      * @method autoSize
53289      */
53290     
53291     setWidth : function()
53292     {
53293         
53294     },
53295     getResizeEl : function(){
53296         return this.el;
53297     }
53298 });//<script type="text/javasscript">
53299  
53300
53301 /**
53302  * @class Roo.DDView
53303  * A DnD enabled version of Roo.View.
53304  * @param {Element/String} container The Element in which to create the View.
53305  * @param {String} tpl The template string used to create the markup for each element of the View
53306  * @param {Object} config The configuration properties. These include all the config options of
53307  * {@link Roo.View} plus some specific to this class.<br>
53308  * <p>
53309  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
53310  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
53311  * <p>
53312  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
53313 .x-view-drag-insert-above {
53314         border-top:1px dotted #3366cc;
53315 }
53316 .x-view-drag-insert-below {
53317         border-bottom:1px dotted #3366cc;
53318 }
53319 </code></pre>
53320  * 
53321  */
53322  
53323 Roo.DDView = function(container, tpl, config) {
53324     Roo.DDView.superclass.constructor.apply(this, arguments);
53325     this.getEl().setStyle("outline", "0px none");
53326     this.getEl().unselectable();
53327     if (this.dragGroup) {
53328         this.setDraggable(this.dragGroup.split(","));
53329     }
53330     if (this.dropGroup) {
53331         this.setDroppable(this.dropGroup.split(","));
53332     }
53333     if (this.deletable) {
53334         this.setDeletable();
53335     }
53336     this.isDirtyFlag = false;
53337         this.addEvents({
53338                 "drop" : true
53339         });
53340 };
53341
53342 Roo.extend(Roo.DDView, Roo.View, {
53343 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
53344 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
53345 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
53346 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
53347
53348         isFormField: true,
53349
53350         reset: Roo.emptyFn,
53351         
53352         clearInvalid: Roo.form.Field.prototype.clearInvalid,
53353
53354         validate: function() {
53355                 return true;
53356         },
53357         
53358         destroy: function() {
53359                 this.purgeListeners();
53360                 this.getEl.removeAllListeners();
53361                 this.getEl().remove();
53362                 if (this.dragZone) {
53363                         if (this.dragZone.destroy) {
53364                                 this.dragZone.destroy();
53365                         }
53366                 }
53367                 if (this.dropZone) {
53368                         if (this.dropZone.destroy) {
53369                                 this.dropZone.destroy();
53370                         }
53371                 }
53372         },
53373
53374 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
53375         getName: function() {
53376                 return this.name;
53377         },
53378
53379 /**     Loads the View from a JSON string representing the Records to put into the Store. */
53380         setValue: function(v) {
53381                 if (!this.store) {
53382                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
53383                 }
53384                 var data = {};
53385                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
53386                 this.store.proxy = new Roo.data.MemoryProxy(data);
53387                 this.store.load();
53388         },
53389
53390 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
53391         getValue: function() {
53392                 var result = '(';
53393                 this.store.each(function(rec) {
53394                         result += rec.id + ',';
53395                 });
53396                 return result.substr(0, result.length - 1) + ')';
53397         },
53398         
53399         getIds: function() {
53400                 var i = 0, result = new Array(this.store.getCount());
53401                 this.store.each(function(rec) {
53402                         result[i++] = rec.id;
53403                 });
53404                 return result;
53405         },
53406         
53407         isDirty: function() {
53408                 return this.isDirtyFlag;
53409         },
53410
53411 /**
53412  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
53413  *      whole Element becomes the target, and this causes the drop gesture to append.
53414  */
53415     getTargetFromEvent : function(e) {
53416                 var target = e.getTarget();
53417                 while ((target !== null) && (target.parentNode != this.el.dom)) {
53418                 target = target.parentNode;
53419                 }
53420                 if (!target) {
53421                         target = this.el.dom.lastChild || this.el.dom;
53422                 }
53423                 return target;
53424     },
53425
53426 /**
53427  *      Create the drag data which consists of an object which has the property "ddel" as
53428  *      the drag proxy element. 
53429  */
53430     getDragData : function(e) {
53431         var target = this.findItemFromChild(e.getTarget());
53432                 if(target) {
53433                         this.handleSelection(e);
53434                         var selNodes = this.getSelectedNodes();
53435             var dragData = {
53436                 source: this,
53437                 copy: this.copy || (this.allowCopy && e.ctrlKey),
53438                 nodes: selNodes,
53439                 records: []
53440                         };
53441                         var selectedIndices = this.getSelectedIndexes();
53442                         for (var i = 0; i < selectedIndices.length; i++) {
53443                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
53444                         }
53445                         if (selNodes.length == 1) {
53446                                 dragData.ddel = target.cloneNode(true); // the div element
53447                         } else {
53448                                 var div = document.createElement('div'); // create the multi element drag "ghost"
53449                                 div.className = 'multi-proxy';
53450                                 for (var i = 0, len = selNodes.length; i < len; i++) {
53451                                         div.appendChild(selNodes[i].cloneNode(true));
53452                                 }
53453                                 dragData.ddel = div;
53454                         }
53455             //console.log(dragData)
53456             //console.log(dragData.ddel.innerHTML)
53457                         return dragData;
53458                 }
53459         //console.log('nodragData')
53460                 return false;
53461     },
53462     
53463 /**     Specify to which ddGroup items in this DDView may be dragged. */
53464     setDraggable: function(ddGroup) {
53465         if (ddGroup instanceof Array) {
53466                 Roo.each(ddGroup, this.setDraggable, this);
53467                 return;
53468         }
53469         if (this.dragZone) {
53470                 this.dragZone.addToGroup(ddGroup);
53471         } else {
53472                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
53473                                 containerScroll: true,
53474                                 ddGroup: ddGroup 
53475
53476                         });
53477 //                      Draggability implies selection. DragZone's mousedown selects the element.
53478                         if (!this.multiSelect) { this.singleSelect = true; }
53479
53480 //                      Wire the DragZone's handlers up to methods in *this*
53481                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
53482                 }
53483     },
53484
53485 /**     Specify from which ddGroup this DDView accepts drops. */
53486     setDroppable: function(ddGroup) {
53487         if (ddGroup instanceof Array) {
53488                 Roo.each(ddGroup, this.setDroppable, this);
53489                 return;
53490         }
53491         if (this.dropZone) {
53492                 this.dropZone.addToGroup(ddGroup);
53493         } else {
53494                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
53495                                 containerScroll: true,
53496                                 ddGroup: ddGroup
53497                         });
53498
53499 //                      Wire the DropZone's handlers up to methods in *this*
53500                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
53501                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
53502                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
53503                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
53504                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
53505                 }
53506     },
53507
53508 /**     Decide whether to drop above or below a View node. */
53509     getDropPoint : function(e, n, dd){
53510         if (n == this.el.dom) { return "above"; }
53511                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
53512                 var c = t + (b - t) / 2;
53513                 var y = Roo.lib.Event.getPageY(e);
53514                 if(y <= c) {
53515                         return "above";
53516                 }else{
53517                         return "below";
53518                 }
53519     },
53520
53521     onNodeEnter : function(n, dd, e, data){
53522                 return false;
53523     },
53524     
53525     onNodeOver : function(n, dd, e, data){
53526                 var pt = this.getDropPoint(e, n, dd);
53527                 // set the insert point style on the target node
53528                 var dragElClass = this.dropNotAllowed;
53529                 if (pt) {
53530                         var targetElClass;
53531                         if (pt == "above"){
53532                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
53533                                 targetElClass = "x-view-drag-insert-above";
53534                         } else {
53535                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
53536                                 targetElClass = "x-view-drag-insert-below";
53537                         }
53538                         if (this.lastInsertClass != targetElClass){
53539                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
53540                                 this.lastInsertClass = targetElClass;
53541                         }
53542                 }
53543                 return dragElClass;
53544         },
53545
53546     onNodeOut : function(n, dd, e, data){
53547                 this.removeDropIndicators(n);
53548     },
53549
53550     onNodeDrop : function(n, dd, e, data){
53551         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
53552                 return false;
53553         }
53554         var pt = this.getDropPoint(e, n, dd);
53555                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
53556                 if (pt == "below") { insertAt++; }
53557                 for (var i = 0; i < data.records.length; i++) {
53558                         var r = data.records[i];
53559                         var dup = this.store.getById(r.id);
53560                         if (dup && (dd != this.dragZone)) {
53561                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
53562                         } else {
53563                                 if (data.copy) {
53564                                         this.store.insert(insertAt++, r.copy());
53565                                 } else {
53566                                         data.source.isDirtyFlag = true;
53567                                         r.store.remove(r);
53568                                         this.store.insert(insertAt++, r);
53569                                 }
53570                                 this.isDirtyFlag = true;
53571                         }
53572                 }
53573                 this.dragZone.cachedTarget = null;
53574                 return true;
53575     },
53576
53577     removeDropIndicators : function(n){
53578                 if(n){
53579                         Roo.fly(n).removeClass([
53580                                 "x-view-drag-insert-above",
53581                                 "x-view-drag-insert-below"]);
53582                         this.lastInsertClass = "_noclass";
53583                 }
53584     },
53585
53586 /**
53587  *      Utility method. Add a delete option to the DDView's context menu.
53588  *      @param {String} imageUrl The URL of the "delete" icon image.
53589  */
53590         setDeletable: function(imageUrl) {
53591                 if (!this.singleSelect && !this.multiSelect) {
53592                         this.singleSelect = true;
53593                 }
53594                 var c = this.getContextMenu();
53595                 this.contextMenu.on("itemclick", function(item) {
53596                         switch (item.id) {
53597                                 case "delete":
53598                                         this.remove(this.getSelectedIndexes());
53599                                         break;
53600                         }
53601                 }, this);
53602                 this.contextMenu.add({
53603                         icon: imageUrl,
53604                         id: "delete",
53605                         text: 'Delete'
53606                 });
53607         },
53608         
53609 /**     Return the context menu for this DDView. */
53610         getContextMenu: function() {
53611                 if (!this.contextMenu) {
53612 //                      Create the View's context menu
53613                         this.contextMenu = new Roo.menu.Menu({
53614                                 id: this.id + "-contextmenu"
53615                         });
53616                         this.el.on("contextmenu", this.showContextMenu, this);
53617                 }
53618                 return this.contextMenu;
53619         },
53620         
53621         disableContextMenu: function() {
53622                 if (this.contextMenu) {
53623                         this.el.un("contextmenu", this.showContextMenu, this);
53624                 }
53625         },
53626
53627         showContextMenu: function(e, item) {
53628         item = this.findItemFromChild(e.getTarget());
53629                 if (item) {
53630                         e.stopEvent();
53631                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
53632                         this.contextMenu.showAt(e.getXY());
53633             }
53634     },
53635
53636 /**
53637  *      Remove {@link Roo.data.Record}s at the specified indices.
53638  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
53639  */
53640     remove: function(selectedIndices) {
53641                 selectedIndices = [].concat(selectedIndices);
53642                 for (var i = 0; i < selectedIndices.length; i++) {
53643                         var rec = this.store.getAt(selectedIndices[i]);
53644                         this.store.remove(rec);
53645                 }
53646     },
53647
53648 /**
53649  *      Double click fires the event, but also, if this is draggable, and there is only one other
53650  *      related DropZone, it transfers the selected node.
53651  */
53652     onDblClick : function(e){
53653         var item = this.findItemFromChild(e.getTarget());
53654         if(item){
53655             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
53656                 return false;
53657             }
53658             if (this.dragGroup) {
53659                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
53660                     while (targets.indexOf(this.dropZone) > -1) {
53661                             targets.remove(this.dropZone);
53662                                 }
53663                     if (targets.length == 1) {
53664                                         this.dragZone.cachedTarget = null;
53665                         var el = Roo.get(targets[0].getEl());
53666                         var box = el.getBox(true);
53667                         targets[0].onNodeDrop(el.dom, {
53668                                 target: el.dom,
53669                                 xy: [box.x, box.y + box.height - 1]
53670                         }, null, this.getDragData(e));
53671                     }
53672                 }
53673         }
53674     },
53675     
53676     handleSelection: function(e) {
53677                 this.dragZone.cachedTarget = null;
53678         var item = this.findItemFromChild(e.getTarget());
53679         if (!item) {
53680                 this.clearSelections(true);
53681                 return;
53682         }
53683                 if (item && (this.multiSelect || this.singleSelect)){
53684                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
53685                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
53686                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
53687                                 this.unselect(item);
53688                         } else {
53689                                 this.select(item, this.multiSelect && e.ctrlKey);
53690                                 this.lastSelection = item;
53691                         }
53692                 }
53693     },
53694
53695     onItemClick : function(item, index, e){
53696                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
53697                         return false;
53698                 }
53699                 return true;
53700     },
53701
53702     unselect : function(nodeInfo, suppressEvent){
53703                 var node = this.getNode(nodeInfo);
53704                 if(node && this.isSelected(node)){
53705                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
53706                                 Roo.fly(node).removeClass(this.selectedClass);
53707                                 this.selections.remove(node);
53708                                 if(!suppressEvent){
53709                                         this.fireEvent("selectionchange", this, this.selections);
53710                                 }
53711                         }
53712                 }
53713     }
53714 });
53715 /*
53716  * Based on:
53717  * Ext JS Library 1.1.1
53718  * Copyright(c) 2006-2007, Ext JS, LLC.
53719  *
53720  * Originally Released Under LGPL - original licence link has changed is not relivant.
53721  *
53722  * Fork - LGPL
53723  * <script type="text/javascript">
53724  */
53725  
53726 /**
53727  * @class Roo.LayoutManager
53728  * @extends Roo.util.Observable
53729  * Base class for layout managers.
53730  */
53731 Roo.LayoutManager = function(container, config){
53732     Roo.LayoutManager.superclass.constructor.call(this);
53733     this.el = Roo.get(container);
53734     // ie scrollbar fix
53735     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
53736         document.body.scroll = "no";
53737     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
53738         this.el.position('relative');
53739     }
53740     this.id = this.el.id;
53741     this.el.addClass("x-layout-container");
53742     /** false to disable window resize monitoring @type Boolean */
53743     this.monitorWindowResize = true;
53744     this.regions = {};
53745     this.addEvents({
53746         /**
53747          * @event layout
53748          * Fires when a layout is performed. 
53749          * @param {Roo.LayoutManager} this
53750          */
53751         "layout" : true,
53752         /**
53753          * @event regionresized
53754          * Fires when the user resizes a region. 
53755          * @param {Roo.LayoutRegion} region The resized region
53756          * @param {Number} newSize The new size (width for east/west, height for north/south)
53757          */
53758         "regionresized" : true,
53759         /**
53760          * @event regioncollapsed
53761          * Fires when a region is collapsed. 
53762          * @param {Roo.LayoutRegion} region The collapsed region
53763          */
53764         "regioncollapsed" : true,
53765         /**
53766          * @event regionexpanded
53767          * Fires when a region is expanded.  
53768          * @param {Roo.LayoutRegion} region The expanded region
53769          */
53770         "regionexpanded" : true
53771     });
53772     this.updating = false;
53773     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
53774 };
53775
53776 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
53777     /**
53778      * Returns true if this layout is currently being updated
53779      * @return {Boolean}
53780      */
53781     isUpdating : function(){
53782         return this.updating; 
53783     },
53784     
53785     /**
53786      * Suspend the LayoutManager from doing auto-layouts while
53787      * making multiple add or remove calls
53788      */
53789     beginUpdate : function(){
53790         this.updating = true;    
53791     },
53792     
53793     /**
53794      * Restore auto-layouts and optionally disable the manager from performing a layout
53795      * @param {Boolean} noLayout true to disable a layout update 
53796      */
53797     endUpdate : function(noLayout){
53798         this.updating = false;
53799         if(!noLayout){
53800             this.layout();
53801         }    
53802     },
53803     
53804     layout: function(){
53805         
53806     },
53807     
53808     onRegionResized : function(region, newSize){
53809         this.fireEvent("regionresized", region, newSize);
53810         this.layout();
53811     },
53812     
53813     onRegionCollapsed : function(region){
53814         this.fireEvent("regioncollapsed", region);
53815     },
53816     
53817     onRegionExpanded : function(region){
53818         this.fireEvent("regionexpanded", region);
53819     },
53820         
53821     /**
53822      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
53823      * performs box-model adjustments.
53824      * @return {Object} The size as an object {width: (the width), height: (the height)}
53825      */
53826     getViewSize : function(){
53827         var size;
53828         if(this.el.dom != document.body){
53829             size = this.el.getSize();
53830         }else{
53831             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
53832         }
53833         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
53834         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
53835         return size;
53836     },
53837     
53838     /**
53839      * Returns the Element this layout is bound to.
53840      * @return {Roo.Element}
53841      */
53842     getEl : function(){
53843         return this.el;
53844     },
53845     
53846     /**
53847      * Returns the specified region.
53848      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
53849      * @return {Roo.LayoutRegion}
53850      */
53851     getRegion : function(target){
53852         return this.regions[target.toLowerCase()];
53853     },
53854     
53855     onWindowResize : function(){
53856         if(this.monitorWindowResize){
53857             this.layout();
53858         }
53859     }
53860 });/*
53861  * Based on:
53862  * Ext JS Library 1.1.1
53863  * Copyright(c) 2006-2007, Ext JS, LLC.
53864  *
53865  * Originally Released Under LGPL - original licence link has changed is not relivant.
53866  *
53867  * Fork - LGPL
53868  * <script type="text/javascript">
53869  */
53870 /**
53871  * @class Roo.BorderLayout
53872  * @extends Roo.LayoutManager
53873  * @children Roo.ContentPanel
53874  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
53875  * please see: <br><br>
53876  * <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>
53877  * <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>
53878  * Example:
53879  <pre><code>
53880  var layout = new Roo.BorderLayout(document.body, {
53881     north: {
53882         initialSize: 25,
53883         titlebar: false
53884     },
53885     west: {
53886         split:true,
53887         initialSize: 200,
53888         minSize: 175,
53889         maxSize: 400,
53890         titlebar: true,
53891         collapsible: true
53892     },
53893     east: {
53894         split:true,
53895         initialSize: 202,
53896         minSize: 175,
53897         maxSize: 400,
53898         titlebar: true,
53899         collapsible: true
53900     },
53901     south: {
53902         split:true,
53903         initialSize: 100,
53904         minSize: 100,
53905         maxSize: 200,
53906         titlebar: true,
53907         collapsible: true
53908     },
53909     center: {
53910         titlebar: true,
53911         autoScroll:true,
53912         resizeTabs: true,
53913         minTabWidth: 50,
53914         preferredTabWidth: 150
53915     }
53916 });
53917
53918 // shorthand
53919 var CP = Roo.ContentPanel;
53920
53921 layout.beginUpdate();
53922 layout.add("north", new CP("north", "North"));
53923 layout.add("south", new CP("south", {title: "South", closable: true}));
53924 layout.add("west", new CP("west", {title: "West"}));
53925 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
53926 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
53927 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
53928 layout.getRegion("center").showPanel("center1");
53929 layout.endUpdate();
53930 </code></pre>
53931
53932 <b>The container the layout is rendered into can be either the body element or any other element.
53933 If it is not the body element, the container needs to either be an absolute positioned element,
53934 or you will need to add "position:relative" to the css of the container.  You will also need to specify
53935 the container size if it is not the body element.</b>
53936
53937 * @constructor
53938 * Create a new BorderLayout
53939 * @param {String/HTMLElement/Element} container The container this layout is bound to
53940 * @param {Object} config Configuration options
53941  */
53942 Roo.BorderLayout = function(container, config){
53943     config = config || {};
53944     Roo.BorderLayout.superclass.constructor.call(this, container, config);
53945     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
53946     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
53947         var target = this.factory.validRegions[i];
53948         if(config[target]){
53949             this.addRegion(target, config[target]);
53950         }
53951     }
53952 };
53953
53954 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
53955         
53956         /**
53957          * @cfg {Roo.LayoutRegion} east
53958          */
53959         /**
53960          * @cfg {Roo.LayoutRegion} west
53961          */
53962         /**
53963          * @cfg {Roo.LayoutRegion} north
53964          */
53965         /**
53966          * @cfg {Roo.LayoutRegion} south
53967          */
53968         /**
53969          * @cfg {Roo.LayoutRegion} center
53970          */
53971     /**
53972      * Creates and adds a new region if it doesn't already exist.
53973      * @param {String} target The target region key (north, south, east, west or center).
53974      * @param {Object} config The regions config object
53975      * @return {BorderLayoutRegion} The new region
53976      */
53977     addRegion : function(target, config){
53978         if(!this.regions[target]){
53979             var r = this.factory.create(target, this, config);
53980             this.bindRegion(target, r);
53981         }
53982         return this.regions[target];
53983     },
53984
53985     // private (kinda)
53986     bindRegion : function(name, r){
53987         this.regions[name] = r;
53988         r.on("visibilitychange", this.layout, this);
53989         r.on("paneladded", this.layout, this);
53990         r.on("panelremoved", this.layout, this);
53991         r.on("invalidated", this.layout, this);
53992         r.on("resized", this.onRegionResized, this);
53993         r.on("collapsed", this.onRegionCollapsed, this);
53994         r.on("expanded", this.onRegionExpanded, this);
53995     },
53996
53997     /**
53998      * Performs a layout update.
53999      */
54000     layout : function(){
54001         if(this.updating) {
54002             return;
54003         }
54004         var size = this.getViewSize();
54005         var w = size.width;
54006         var h = size.height;
54007         var centerW = w;
54008         var centerH = h;
54009         var centerY = 0;
54010         var centerX = 0;
54011         //var x = 0, y = 0;
54012
54013         var rs = this.regions;
54014         var north = rs["north"];
54015         var south = rs["south"]; 
54016         var west = rs["west"];
54017         var east = rs["east"];
54018         var center = rs["center"];
54019         //if(this.hideOnLayout){ // not supported anymore
54020             //c.el.setStyle("display", "none");
54021         //}
54022         if(north && north.isVisible()){
54023             var b = north.getBox();
54024             var m = north.getMargins();
54025             b.width = w - (m.left+m.right);
54026             b.x = m.left;
54027             b.y = m.top;
54028             centerY = b.height + b.y + m.bottom;
54029             centerH -= centerY;
54030             north.updateBox(this.safeBox(b));
54031         }
54032         if(south && south.isVisible()){
54033             var b = south.getBox();
54034             var m = south.getMargins();
54035             b.width = w - (m.left+m.right);
54036             b.x = m.left;
54037             var totalHeight = (b.height + m.top + m.bottom);
54038             b.y = h - totalHeight + m.top;
54039             centerH -= totalHeight;
54040             south.updateBox(this.safeBox(b));
54041         }
54042         if(west && west.isVisible()){
54043             var b = west.getBox();
54044             var m = west.getMargins();
54045             b.height = centerH - (m.top+m.bottom);
54046             b.x = m.left;
54047             b.y = centerY + m.top;
54048             var totalWidth = (b.width + m.left + m.right);
54049             centerX += totalWidth;
54050             centerW -= totalWidth;
54051             west.updateBox(this.safeBox(b));
54052         }
54053         if(east && east.isVisible()){
54054             var b = east.getBox();
54055             var m = east.getMargins();
54056             b.height = centerH - (m.top+m.bottom);
54057             var totalWidth = (b.width + m.left + m.right);
54058             b.x = w - totalWidth + m.left;
54059             b.y = centerY + m.top;
54060             centerW -= totalWidth;
54061             east.updateBox(this.safeBox(b));
54062         }
54063         if(center){
54064             var m = center.getMargins();
54065             var centerBox = {
54066                 x: centerX + m.left,
54067                 y: centerY + m.top,
54068                 width: centerW - (m.left+m.right),
54069                 height: centerH - (m.top+m.bottom)
54070             };
54071             //if(this.hideOnLayout){
54072                 //center.el.setStyle("display", "block");
54073             //}
54074             center.updateBox(this.safeBox(centerBox));
54075         }
54076         this.el.repaint();
54077         this.fireEvent("layout", this);
54078     },
54079
54080     // private
54081     safeBox : function(box){
54082         box.width = Math.max(0, box.width);
54083         box.height = Math.max(0, box.height);
54084         return box;
54085     },
54086
54087     /**
54088      * Adds a ContentPanel (or subclass) to this layout.
54089      * @param {String} target The target region key (north, south, east, west or center).
54090      * @param {Roo.ContentPanel} panel The panel to add
54091      * @return {Roo.ContentPanel} The added panel
54092      */
54093     add : function(target, panel){
54094          
54095         target = target.toLowerCase();
54096         return this.regions[target].add(panel);
54097     },
54098
54099     /**
54100      * Remove a ContentPanel (or subclass) to this layout.
54101      * @param {String} target The target region key (north, south, east, west or center).
54102      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
54103      * @return {Roo.ContentPanel} The removed panel
54104      */
54105     remove : function(target, panel){
54106         target = target.toLowerCase();
54107         return this.regions[target].remove(panel);
54108     },
54109
54110     /**
54111      * Searches all regions for a panel with the specified id
54112      * @param {String} panelId
54113      * @return {Roo.ContentPanel} The panel or null if it wasn't found
54114      */
54115     findPanel : function(panelId){
54116         var rs = this.regions;
54117         for(var target in rs){
54118             if(typeof rs[target] != "function"){
54119                 var p = rs[target].getPanel(panelId);
54120                 if(p){
54121                     return p;
54122                 }
54123             }
54124         }
54125         return null;
54126     },
54127
54128     /**
54129      * Searches all regions for a panel with the specified id and activates (shows) it.
54130      * @param {String/ContentPanel} panelId The panels id or the panel itself
54131      * @return {Roo.ContentPanel} The shown panel or null
54132      */
54133     showPanel : function(panelId) {
54134       var rs = this.regions;
54135       for(var target in rs){
54136          var r = rs[target];
54137          if(typeof r != "function"){
54138             if(r.hasPanel(panelId)){
54139                return r.showPanel(panelId);
54140             }
54141          }
54142       }
54143       return null;
54144    },
54145
54146    /**
54147      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
54148      * @param {Roo.state.Provider} provider (optional) An alternate state provider
54149      */
54150     restoreState : function(provider){
54151         if(!provider){
54152             provider = Roo.state.Manager;
54153         }
54154         var sm = new Roo.LayoutStateManager();
54155         sm.init(this, provider);
54156     },
54157
54158     /**
54159      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
54160      * object should contain properties for each region to add ContentPanels to, and each property's value should be
54161      * a valid ContentPanel config object.  Example:
54162      * <pre><code>
54163 // Create the main layout
54164 var layout = new Roo.BorderLayout('main-ct', {
54165     west: {
54166         split:true,
54167         minSize: 175,
54168         titlebar: true
54169     },
54170     center: {
54171         title:'Components'
54172     }
54173 }, 'main-ct');
54174
54175 // Create and add multiple ContentPanels at once via configs
54176 layout.batchAdd({
54177    west: {
54178        id: 'source-files',
54179        autoCreate:true,
54180        title:'Ext Source Files',
54181        autoScroll:true,
54182        fitToFrame:true
54183    },
54184    center : {
54185        el: cview,
54186        autoScroll:true,
54187        fitToFrame:true,
54188        toolbar: tb,
54189        resizeEl:'cbody'
54190    }
54191 });
54192 </code></pre>
54193      * @param {Object} regions An object containing ContentPanel configs by region name
54194      */
54195     batchAdd : function(regions){
54196         this.beginUpdate();
54197         for(var rname in regions){
54198             var lr = this.regions[rname];
54199             if(lr){
54200                 this.addTypedPanels(lr, regions[rname]);
54201             }
54202         }
54203         this.endUpdate();
54204     },
54205
54206     // private
54207     addTypedPanels : function(lr, ps){
54208         if(typeof ps == 'string'){
54209             lr.add(new Roo.ContentPanel(ps));
54210         }
54211         else if(ps instanceof Array){
54212             for(var i =0, len = ps.length; i < len; i++){
54213                 this.addTypedPanels(lr, ps[i]);
54214             }
54215         }
54216         else if(!ps.events){ // raw config?
54217             var el = ps.el;
54218             delete ps.el; // prevent conflict
54219             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
54220         }
54221         else {  // panel object assumed!
54222             lr.add(ps);
54223         }
54224     },
54225     /**
54226      * Adds a xtype elements to the layout.
54227      * <pre><code>
54228
54229 layout.addxtype({
54230        xtype : 'ContentPanel',
54231        region: 'west',
54232        items: [ .... ]
54233    }
54234 );
54235
54236 layout.addxtype({
54237         xtype : 'NestedLayoutPanel',
54238         region: 'west',
54239         layout: {
54240            center: { },
54241            west: { }   
54242         },
54243         items : [ ... list of content panels or nested layout panels.. ]
54244    }
54245 );
54246 </code></pre>
54247      * @param {Object} cfg Xtype definition of item to add.
54248      */
54249     addxtype : function(cfg)
54250     {
54251         // basically accepts a pannel...
54252         // can accept a layout region..!?!?
54253         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
54254         
54255         if (!cfg.xtype.match(/Panel$/)) {
54256             return false;
54257         }
54258         var ret = false;
54259         
54260         if (typeof(cfg.region) == 'undefined') {
54261             Roo.log("Failed to add Panel, region was not set");
54262             Roo.log(cfg);
54263             return false;
54264         }
54265         var region = cfg.region;
54266         delete cfg.region;
54267         
54268           
54269         var xitems = [];
54270         if (cfg.items) {
54271             xitems = cfg.items;
54272             delete cfg.items;
54273         }
54274         var nb = false;
54275         
54276         switch(cfg.xtype) 
54277         {
54278             case 'ContentPanel':  // ContentPanel (el, cfg)
54279             case 'ScrollPanel':  // ContentPanel (el, cfg)
54280             case 'ViewPanel': 
54281                 if(cfg.autoCreate) {
54282                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54283                 } else {
54284                     var el = this.el.createChild();
54285                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
54286                 }
54287                 
54288                 this.add(region, ret);
54289                 break;
54290             
54291             
54292             case 'TreePanel': // our new panel!
54293                 cfg.el = this.el.createChild();
54294                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54295                 this.add(region, ret);
54296                 break;
54297             
54298             case 'NestedLayoutPanel': 
54299                 // create a new Layout (which is  a Border Layout...
54300                 var el = this.el.createChild();
54301                 var clayout = cfg.layout;
54302                 delete cfg.layout;
54303                 clayout.items   = clayout.items  || [];
54304                 // replace this exitems with the clayout ones..
54305                 xitems = clayout.items;
54306                  
54307                 
54308                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
54309                     cfg.background = false;
54310                 }
54311                 var layout = new Roo.BorderLayout(el, clayout);
54312                 
54313                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
54314                 //console.log('adding nested layout panel '  + cfg.toSource());
54315                 this.add(region, ret);
54316                 nb = {}; /// find first...
54317                 break;
54318                 
54319             case 'GridPanel': 
54320             
54321                 // needs grid and region
54322                 
54323                 //var el = this.getRegion(region).el.createChild();
54324                 var el = this.el.createChild();
54325                 // create the grid first...
54326                 
54327                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
54328                 delete cfg.grid;
54329                 if (region == 'center' && this.active ) {
54330                     cfg.background = false;
54331                 }
54332                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
54333                 
54334                 this.add(region, ret);
54335                 if (cfg.background) {
54336                     ret.on('activate', function(gp) {
54337                         if (!gp.grid.rendered) {
54338                             gp.grid.render();
54339                         }
54340                     });
54341                 } else {
54342                     grid.render();
54343                 }
54344                 break;
54345            
54346            
54347            
54348                 
54349                 
54350                 
54351             default:
54352                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
54353                     
54354                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54355                     this.add(region, ret);
54356                 } else {
54357                 
54358                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
54359                     return null;
54360                 }
54361                 
54362              // GridPanel (grid, cfg)
54363             
54364         }
54365         this.beginUpdate();
54366         // add children..
54367         var region = '';
54368         var abn = {};
54369         Roo.each(xitems, function(i)  {
54370             region = nb && i.region ? i.region : false;
54371             
54372             var add = ret.addxtype(i);
54373            
54374             if (region) {
54375                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
54376                 if (!i.background) {
54377                     abn[region] = nb[region] ;
54378                 }
54379             }
54380             
54381         });
54382         this.endUpdate();
54383
54384         // make the last non-background panel active..
54385         //if (nb) { Roo.log(abn); }
54386         if (nb) {
54387             
54388             for(var r in abn) {
54389                 region = this.getRegion(r);
54390                 if (region) {
54391                     // tried using nb[r], but it does not work..
54392                      
54393                     region.showPanel(abn[r]);
54394                    
54395                 }
54396             }
54397         }
54398         return ret;
54399         
54400     }
54401 });
54402
54403 /**
54404  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
54405  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
54406  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
54407  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
54408  * <pre><code>
54409 // shorthand
54410 var CP = Roo.ContentPanel;
54411
54412 var layout = Roo.BorderLayout.create({
54413     north: {
54414         initialSize: 25,
54415         titlebar: false,
54416         panels: [new CP("north", "North")]
54417     },
54418     west: {
54419         split:true,
54420         initialSize: 200,
54421         minSize: 175,
54422         maxSize: 400,
54423         titlebar: true,
54424         collapsible: true,
54425         panels: [new CP("west", {title: "West"})]
54426     },
54427     east: {
54428         split:true,
54429         initialSize: 202,
54430         minSize: 175,
54431         maxSize: 400,
54432         titlebar: true,
54433         collapsible: true,
54434         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
54435     },
54436     south: {
54437         split:true,
54438         initialSize: 100,
54439         minSize: 100,
54440         maxSize: 200,
54441         titlebar: true,
54442         collapsible: true,
54443         panels: [new CP("south", {title: "South", closable: true})]
54444     },
54445     center: {
54446         titlebar: true,
54447         autoScroll:true,
54448         resizeTabs: true,
54449         minTabWidth: 50,
54450         preferredTabWidth: 150,
54451         panels: [
54452             new CP("center1", {title: "Close Me", closable: true}),
54453             new CP("center2", {title: "Center Panel", closable: false})
54454         ]
54455     }
54456 }, document.body);
54457
54458 layout.getRegion("center").showPanel("center1");
54459 </code></pre>
54460  * @param config
54461  * @param targetEl
54462  */
54463 Roo.BorderLayout.create = function(config, targetEl){
54464     var layout = new Roo.BorderLayout(targetEl || document.body, config);
54465     layout.beginUpdate();
54466     var regions = Roo.BorderLayout.RegionFactory.validRegions;
54467     for(var j = 0, jlen = regions.length; j < jlen; j++){
54468         var lr = regions[j];
54469         if(layout.regions[lr] && config[lr].panels){
54470             var r = layout.regions[lr];
54471             var ps = config[lr].panels;
54472             layout.addTypedPanels(r, ps);
54473         }
54474     }
54475     layout.endUpdate();
54476     return layout;
54477 };
54478
54479 // private
54480 Roo.BorderLayout.RegionFactory = {
54481     // private
54482     validRegions : ["north","south","east","west","center"],
54483
54484     // private
54485     create : function(target, mgr, config){
54486         target = target.toLowerCase();
54487         if(config.lightweight || config.basic){
54488             return new Roo.BasicLayoutRegion(mgr, config, target);
54489         }
54490         switch(target){
54491             case "north":
54492                 return new Roo.NorthLayoutRegion(mgr, config);
54493             case "south":
54494                 return new Roo.SouthLayoutRegion(mgr, config);
54495             case "east":
54496                 return new Roo.EastLayoutRegion(mgr, config);
54497             case "west":
54498                 return new Roo.WestLayoutRegion(mgr, config);
54499             case "center":
54500                 return new Roo.CenterLayoutRegion(mgr, config);
54501         }
54502         throw 'Layout region "'+target+'" not supported.';
54503     }
54504 };/*
54505  * Based on:
54506  * Ext JS Library 1.1.1
54507  * Copyright(c) 2006-2007, Ext JS, LLC.
54508  *
54509  * Originally Released Under LGPL - original licence link has changed is not relivant.
54510  *
54511  * Fork - LGPL
54512  * <script type="text/javascript">
54513  */
54514  
54515 /**
54516  * @class Roo.BasicLayoutRegion
54517  * @extends Roo.util.Observable
54518  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
54519  * and does not have a titlebar, tabs or any other features. All it does is size and position 
54520  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
54521  */
54522 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
54523     this.mgr = mgr;
54524     this.position  = pos;
54525     this.events = {
54526         /**
54527          * @scope Roo.BasicLayoutRegion
54528          */
54529         
54530         /**
54531          * @event beforeremove
54532          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
54533          * @param {Roo.LayoutRegion} this
54534          * @param {Roo.ContentPanel} panel The panel
54535          * @param {Object} e The cancel event object
54536          */
54537         "beforeremove" : true,
54538         /**
54539          * @event invalidated
54540          * Fires when the layout for this region is changed.
54541          * @param {Roo.LayoutRegion} this
54542          */
54543         "invalidated" : true,
54544         /**
54545          * @event visibilitychange
54546          * Fires when this region is shown or hidden 
54547          * @param {Roo.LayoutRegion} this
54548          * @param {Boolean} visibility true or false
54549          */
54550         "visibilitychange" : true,
54551         /**
54552          * @event paneladded
54553          * Fires when a panel is added. 
54554          * @param {Roo.LayoutRegion} this
54555          * @param {Roo.ContentPanel} panel The panel
54556          */
54557         "paneladded" : true,
54558         /**
54559          * @event panelremoved
54560          * Fires when a panel is removed. 
54561          * @param {Roo.LayoutRegion} this
54562          * @param {Roo.ContentPanel} panel The panel
54563          */
54564         "panelremoved" : true,
54565         /**
54566          * @event beforecollapse
54567          * Fires when this region before collapse.
54568          * @param {Roo.LayoutRegion} this
54569          */
54570         "beforecollapse" : true,
54571         /**
54572          * @event collapsed
54573          * Fires when this region is collapsed.
54574          * @param {Roo.LayoutRegion} this
54575          */
54576         "collapsed" : true,
54577         /**
54578          * @event expanded
54579          * Fires when this region is expanded.
54580          * @param {Roo.LayoutRegion} this
54581          */
54582         "expanded" : true,
54583         /**
54584          * @event slideshow
54585          * Fires when this region is slid into view.
54586          * @param {Roo.LayoutRegion} this
54587          */
54588         "slideshow" : true,
54589         /**
54590          * @event slidehide
54591          * Fires when this region slides out of view. 
54592          * @param {Roo.LayoutRegion} this
54593          */
54594         "slidehide" : true,
54595         /**
54596          * @event panelactivated
54597          * Fires when a panel is activated. 
54598          * @param {Roo.LayoutRegion} this
54599          * @param {Roo.ContentPanel} panel The activated panel
54600          */
54601         "panelactivated" : true,
54602         /**
54603          * @event resized
54604          * Fires when the user resizes this region. 
54605          * @param {Roo.LayoutRegion} this
54606          * @param {Number} newSize The new size (width for east/west, height for north/south)
54607          */
54608         "resized" : true
54609     };
54610     /** A collection of panels in this region. @type Roo.util.MixedCollection */
54611     this.panels = new Roo.util.MixedCollection();
54612     this.panels.getKey = this.getPanelId.createDelegate(this);
54613     this.box = null;
54614     this.activePanel = null;
54615     // ensure listeners are added...
54616     
54617     if (config.listeners || config.events) {
54618         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
54619             listeners : config.listeners || {},
54620             events : config.events || {}
54621         });
54622     }
54623     
54624     if(skipConfig !== true){
54625         this.applyConfig(config);
54626     }
54627 };
54628
54629 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
54630     getPanelId : function(p){
54631         return p.getId();
54632     },
54633     
54634     applyConfig : function(config){
54635         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54636         this.config = config;
54637         
54638     },
54639     
54640     /**
54641      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
54642      * the width, for horizontal (north, south) the height.
54643      * @param {Number} newSize The new width or height
54644      */
54645     resizeTo : function(newSize){
54646         var el = this.el ? this.el :
54647                  (this.activePanel ? this.activePanel.getEl() : null);
54648         if(el){
54649             switch(this.position){
54650                 case "east":
54651                 case "west":
54652                     el.setWidth(newSize);
54653                     this.fireEvent("resized", this, newSize);
54654                 break;
54655                 case "north":
54656                 case "south":
54657                     el.setHeight(newSize);
54658                     this.fireEvent("resized", this, newSize);
54659                 break;                
54660             }
54661         }
54662     },
54663     
54664     getBox : function(){
54665         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
54666     },
54667     
54668     getMargins : function(){
54669         return this.margins;
54670     },
54671     
54672     updateBox : function(box){
54673         this.box = box;
54674         var el = this.activePanel.getEl();
54675         el.dom.style.left = box.x + "px";
54676         el.dom.style.top = box.y + "px";
54677         this.activePanel.setSize(box.width, box.height);
54678     },
54679     
54680     /**
54681      * Returns the container element for this region.
54682      * @return {Roo.Element}
54683      */
54684     getEl : function(){
54685         return this.activePanel;
54686     },
54687     
54688     /**
54689      * Returns true if this region is currently visible.
54690      * @return {Boolean}
54691      */
54692     isVisible : function(){
54693         return this.activePanel ? true : false;
54694     },
54695     
54696     setActivePanel : function(panel){
54697         panel = this.getPanel(panel);
54698         if(this.activePanel && this.activePanel != panel){
54699             this.activePanel.setActiveState(false);
54700             this.activePanel.getEl().setLeftTop(-10000,-10000);
54701         }
54702         this.activePanel = panel;
54703         panel.setActiveState(true);
54704         if(this.box){
54705             panel.setSize(this.box.width, this.box.height);
54706         }
54707         this.fireEvent("panelactivated", this, panel);
54708         this.fireEvent("invalidated");
54709     },
54710     
54711     /**
54712      * Show the specified panel.
54713      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
54714      * @return {Roo.ContentPanel} The shown panel or null
54715      */
54716     showPanel : function(panel){
54717         if(panel = this.getPanel(panel)){
54718             this.setActivePanel(panel);
54719         }
54720         return panel;
54721     },
54722     
54723     /**
54724      * Get the active panel for this region.
54725      * @return {Roo.ContentPanel} The active panel or null
54726      */
54727     getActivePanel : function(){
54728         return this.activePanel;
54729     },
54730     
54731     /**
54732      * Add the passed ContentPanel(s)
54733      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
54734      * @return {Roo.ContentPanel} The panel added (if only one was added)
54735      */
54736     add : function(panel){
54737         if(arguments.length > 1){
54738             for(var i = 0, len = arguments.length; i < len; i++) {
54739                 this.add(arguments[i]);
54740             }
54741             return null;
54742         }
54743         if(this.hasPanel(panel)){
54744             this.showPanel(panel);
54745             return panel;
54746         }
54747         var el = panel.getEl();
54748         if(el.dom.parentNode != this.mgr.el.dom){
54749             this.mgr.el.dom.appendChild(el.dom);
54750         }
54751         if(panel.setRegion){
54752             panel.setRegion(this);
54753         }
54754         this.panels.add(panel);
54755         el.setStyle("position", "absolute");
54756         if(!panel.background){
54757             this.setActivePanel(panel);
54758             if(this.config.initialSize && this.panels.getCount()==1){
54759                 this.resizeTo(this.config.initialSize);
54760             }
54761         }
54762         this.fireEvent("paneladded", this, panel);
54763         return panel;
54764     },
54765     
54766     /**
54767      * Returns true if the panel is in this region.
54768      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54769      * @return {Boolean}
54770      */
54771     hasPanel : function(panel){
54772         if(typeof panel == "object"){ // must be panel obj
54773             panel = panel.getId();
54774         }
54775         return this.getPanel(panel) ? true : false;
54776     },
54777     
54778     /**
54779      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
54780      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54781      * @param {Boolean} preservePanel Overrides the config preservePanel option
54782      * @return {Roo.ContentPanel} The panel that was removed
54783      */
54784     remove : function(panel, preservePanel){
54785         panel = this.getPanel(panel);
54786         if(!panel){
54787             return null;
54788         }
54789         var e = {};
54790         this.fireEvent("beforeremove", this, panel, e);
54791         if(e.cancel === true){
54792             return null;
54793         }
54794         var panelId = panel.getId();
54795         this.panels.removeKey(panelId);
54796         return panel;
54797     },
54798     
54799     /**
54800      * Returns the panel specified or null if it's not in this region.
54801      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
54802      * @return {Roo.ContentPanel}
54803      */
54804     getPanel : function(id){
54805         if(typeof id == "object"){ // must be panel obj
54806             return id;
54807         }
54808         return this.panels.get(id);
54809     },
54810     
54811     /**
54812      * Returns this regions position (north/south/east/west/center).
54813      * @return {String} 
54814      */
54815     getPosition: function(){
54816         return this.position;    
54817     }
54818 });/*
54819  * Based on:
54820  * Ext JS Library 1.1.1
54821  * Copyright(c) 2006-2007, Ext JS, LLC.
54822  *
54823  * Originally Released Under LGPL - original licence link has changed is not relivant.
54824  *
54825  * Fork - LGPL
54826  * <script type="text/javascript">
54827  */
54828  
54829 /**
54830  * @class Roo.LayoutRegion
54831  * @extends Roo.BasicLayoutRegion
54832  * This class represents a region in a layout manager.
54833  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
54834  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
54835  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
54836  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
54837  * @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})
54838  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
54839  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
54840  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
54841  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
54842  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
54843  * @cfg {String}    title           The title for the region (overrides panel titles)
54844  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
54845  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
54846  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
54847  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
54848  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
54849  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
54850  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
54851  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
54852  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
54853  * @cfg {Boolean}   showPin         True to show a pin button
54854  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
54855  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
54856  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
54857  * @cfg {Number}    width           For East/West panels
54858  * @cfg {Number}    height          For North/South panels
54859  * @cfg {Boolean}   split           To show the splitter
54860  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
54861  */
54862 Roo.LayoutRegion = function(mgr, config, pos){
54863     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
54864     var dh = Roo.DomHelper;
54865     /** This region's container element 
54866     * @type Roo.Element */
54867     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
54868     /** This region's title element 
54869     * @type Roo.Element */
54870
54871     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
54872         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
54873         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
54874     ]}, true);
54875     this.titleEl.enableDisplayMode();
54876     /** This region's title text element 
54877     * @type HTMLElement */
54878     this.titleTextEl = this.titleEl.dom.firstChild;
54879     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
54880     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
54881     this.closeBtn.enableDisplayMode();
54882     this.closeBtn.on("click", this.closeClicked, this);
54883     this.closeBtn.hide();
54884
54885     this.createBody(config);
54886     this.visible = true;
54887     this.collapsed = false;
54888
54889     if(config.hideWhenEmpty){
54890         this.hide();
54891         this.on("paneladded", this.validateVisibility, this);
54892         this.on("panelremoved", this.validateVisibility, this);
54893     }
54894     this.applyConfig(config);
54895 };
54896
54897 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
54898
54899     createBody : function(){
54900         /** This region's body element 
54901         * @type Roo.Element */
54902         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
54903     },
54904
54905     applyConfig : function(c){
54906         if(c.collapsible && this.position != "center" && !this.collapsedEl){
54907             var dh = Roo.DomHelper;
54908             if(c.titlebar !== false){
54909                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
54910                 this.collapseBtn.on("click", this.collapse, this);
54911                 this.collapseBtn.enableDisplayMode();
54912
54913                 if(c.showPin === true || this.showPin){
54914                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
54915                     this.stickBtn.enableDisplayMode();
54916                     this.stickBtn.on("click", this.expand, this);
54917                     this.stickBtn.hide();
54918                 }
54919             }
54920             /** This region's collapsed element
54921             * @type Roo.Element */
54922             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
54923                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
54924             ]}, true);
54925             if(c.floatable !== false){
54926                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
54927                this.collapsedEl.on("click", this.collapseClick, this);
54928             }
54929
54930             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
54931                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
54932                    id: "message", unselectable: "on", style:{"float":"left"}});
54933                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
54934              }
54935             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
54936             this.expandBtn.on("click", this.expand, this);
54937         }
54938         if(this.collapseBtn){
54939             this.collapseBtn.setVisible(c.collapsible == true);
54940         }
54941         this.cmargins = c.cmargins || this.cmargins ||
54942                          (this.position == "west" || this.position == "east" ?
54943                              {top: 0, left: 2, right:2, bottom: 0} :
54944                              {top: 2, left: 0, right:0, bottom: 2});
54945         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
54946         this.bottomTabs = c.tabPosition != "top";
54947         this.autoScroll = c.autoScroll || false;
54948         if(this.autoScroll){
54949             this.bodyEl.setStyle("overflow", "auto");
54950         }else{
54951             this.bodyEl.setStyle("overflow", "hidden");
54952         }
54953         //if(c.titlebar !== false){
54954             if((!c.titlebar && !c.title) || c.titlebar === false){
54955                 this.titleEl.hide();
54956             }else{
54957                 this.titleEl.show();
54958                 if(c.title){
54959                     this.titleTextEl.innerHTML = c.title;
54960                 }
54961             }
54962         //}
54963         this.duration = c.duration || .30;
54964         this.slideDuration = c.slideDuration || .45;
54965         this.config = c;
54966         if(c.collapsed){
54967             this.collapse(true);
54968         }
54969         if(c.hidden){
54970             this.hide();
54971         }
54972     },
54973     /**
54974      * Returns true if this region is currently visible.
54975      * @return {Boolean}
54976      */
54977     isVisible : function(){
54978         return this.visible;
54979     },
54980
54981     /**
54982      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
54983      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
54984      */
54985     setCollapsedTitle : function(title){
54986         title = title || "&#160;";
54987         if(this.collapsedTitleTextEl){
54988             this.collapsedTitleTextEl.innerHTML = title;
54989         }
54990     },
54991
54992     getBox : function(){
54993         var b;
54994         if(!this.collapsed){
54995             b = this.el.getBox(false, true);
54996         }else{
54997             b = this.collapsedEl.getBox(false, true);
54998         }
54999         return b;
55000     },
55001
55002     getMargins : function(){
55003         return this.collapsed ? this.cmargins : this.margins;
55004     },
55005
55006     highlight : function(){
55007         this.el.addClass("x-layout-panel-dragover");
55008     },
55009
55010     unhighlight : function(){
55011         this.el.removeClass("x-layout-panel-dragover");
55012     },
55013
55014     updateBox : function(box){
55015         this.box = box;
55016         if(!this.collapsed){
55017             this.el.dom.style.left = box.x + "px";
55018             this.el.dom.style.top = box.y + "px";
55019             this.updateBody(box.width, box.height);
55020         }else{
55021             this.collapsedEl.dom.style.left = box.x + "px";
55022             this.collapsedEl.dom.style.top = box.y + "px";
55023             this.collapsedEl.setSize(box.width, box.height);
55024         }
55025         if(this.tabs){
55026             this.tabs.autoSizeTabs();
55027         }
55028     },
55029
55030     updateBody : function(w, h){
55031         if(w !== null){
55032             this.el.setWidth(w);
55033             w -= this.el.getBorderWidth("rl");
55034             if(this.config.adjustments){
55035                 w += this.config.adjustments[0];
55036             }
55037         }
55038         if(h !== null){
55039             this.el.setHeight(h);
55040             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
55041             h -= this.el.getBorderWidth("tb");
55042             if(this.config.adjustments){
55043                 h += this.config.adjustments[1];
55044             }
55045             this.bodyEl.setHeight(h);
55046             if(this.tabs){
55047                 h = this.tabs.syncHeight(h);
55048             }
55049         }
55050         if(this.panelSize){
55051             w = w !== null ? w : this.panelSize.width;
55052             h = h !== null ? h : this.panelSize.height;
55053         }
55054         if(this.activePanel){
55055             var el = this.activePanel.getEl();
55056             w = w !== null ? w : el.getWidth();
55057             h = h !== null ? h : el.getHeight();
55058             this.panelSize = {width: w, height: h};
55059             this.activePanel.setSize(w, h);
55060         }
55061         if(Roo.isIE && this.tabs){
55062             this.tabs.el.repaint();
55063         }
55064     },
55065
55066     /**
55067      * Returns the container element for this region.
55068      * @return {Roo.Element}
55069      */
55070     getEl : function(){
55071         return this.el;
55072     },
55073
55074     /**
55075      * Hides this region.
55076      */
55077     hide : function(){
55078         if(!this.collapsed){
55079             this.el.dom.style.left = "-2000px";
55080             this.el.hide();
55081         }else{
55082             this.collapsedEl.dom.style.left = "-2000px";
55083             this.collapsedEl.hide();
55084         }
55085         this.visible = false;
55086         this.fireEvent("visibilitychange", this, false);
55087     },
55088
55089     /**
55090      * Shows this region if it was previously hidden.
55091      */
55092     show : function(){
55093         if(!this.collapsed){
55094             this.el.show();
55095         }else{
55096             this.collapsedEl.show();
55097         }
55098         this.visible = true;
55099         this.fireEvent("visibilitychange", this, true);
55100     },
55101
55102     closeClicked : function(){
55103         if(this.activePanel){
55104             this.remove(this.activePanel);
55105         }
55106     },
55107
55108     collapseClick : function(e){
55109         if(this.isSlid){
55110            e.stopPropagation();
55111            this.slideIn();
55112         }else{
55113            e.stopPropagation();
55114            this.slideOut();
55115         }
55116     },
55117
55118     /**
55119      * Collapses this region.
55120      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
55121      */
55122     collapse : function(skipAnim, skipCheck){
55123         if(this.collapsed) {
55124             return;
55125         }
55126         
55127         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
55128             
55129             this.collapsed = true;
55130             if(this.split){
55131                 this.split.el.hide();
55132             }
55133             if(this.config.animate && skipAnim !== true){
55134                 this.fireEvent("invalidated", this);
55135                 this.animateCollapse();
55136             }else{
55137                 this.el.setLocation(-20000,-20000);
55138                 this.el.hide();
55139                 this.collapsedEl.show();
55140                 this.fireEvent("collapsed", this);
55141                 this.fireEvent("invalidated", this);
55142             }
55143         }
55144         
55145     },
55146
55147     animateCollapse : function(){
55148         // overridden
55149     },
55150
55151     /**
55152      * Expands this region if it was previously collapsed.
55153      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
55154      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
55155      */
55156     expand : function(e, skipAnim){
55157         if(e) {
55158             e.stopPropagation();
55159         }
55160         if(!this.collapsed || this.el.hasActiveFx()) {
55161             return;
55162         }
55163         if(this.isSlid){
55164             this.afterSlideIn();
55165             skipAnim = true;
55166         }
55167         this.collapsed = false;
55168         if(this.config.animate && skipAnim !== true){
55169             this.animateExpand();
55170         }else{
55171             this.el.show();
55172             if(this.split){
55173                 this.split.el.show();
55174             }
55175             this.collapsedEl.setLocation(-2000,-2000);
55176             this.collapsedEl.hide();
55177             this.fireEvent("invalidated", this);
55178             this.fireEvent("expanded", this);
55179         }
55180     },
55181
55182     animateExpand : function(){
55183         // overridden
55184     },
55185
55186     initTabs : function()
55187     {
55188         this.bodyEl.setStyle("overflow", "hidden");
55189         var ts = new Roo.TabPanel(
55190                 this.bodyEl.dom,
55191                 {
55192                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
55193                     disableTooltips: this.config.disableTabTips,
55194                     toolbar : this.config.toolbar
55195                 }
55196         );
55197         if(this.config.hideTabs){
55198             ts.stripWrap.setDisplayed(false);
55199         }
55200         this.tabs = ts;
55201         ts.resizeTabs = this.config.resizeTabs === true;
55202         ts.minTabWidth = this.config.minTabWidth || 40;
55203         ts.maxTabWidth = this.config.maxTabWidth || 250;
55204         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
55205         ts.monitorResize = false;
55206         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55207         ts.bodyEl.addClass('x-layout-tabs-body');
55208         this.panels.each(this.initPanelAsTab, this);
55209     },
55210
55211     initPanelAsTab : function(panel){
55212         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
55213                     this.config.closeOnTab && panel.isClosable());
55214         if(panel.tabTip !== undefined){
55215             ti.setTooltip(panel.tabTip);
55216         }
55217         ti.on("activate", function(){
55218               this.setActivePanel(panel);
55219         }, this);
55220         if(this.config.closeOnTab){
55221             ti.on("beforeclose", function(t, e){
55222                 e.cancel = true;
55223                 this.remove(panel);
55224             }, this);
55225         }
55226         return ti;
55227     },
55228
55229     updatePanelTitle : function(panel, title){
55230         if(this.activePanel == panel){
55231             this.updateTitle(title);
55232         }
55233         if(this.tabs){
55234             var ti = this.tabs.getTab(panel.getEl().id);
55235             ti.setText(title);
55236             if(panel.tabTip !== undefined){
55237                 ti.setTooltip(panel.tabTip);
55238             }
55239         }
55240     },
55241
55242     updateTitle : function(title){
55243         if(this.titleTextEl && !this.config.title){
55244             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
55245         }
55246     },
55247
55248     setActivePanel : function(panel){
55249         panel = this.getPanel(panel);
55250         if(this.activePanel && this.activePanel != panel){
55251             this.activePanel.setActiveState(false);
55252         }
55253         this.activePanel = panel;
55254         panel.setActiveState(true);
55255         if(this.panelSize){
55256             panel.setSize(this.panelSize.width, this.panelSize.height);
55257         }
55258         if(this.closeBtn){
55259             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
55260         }
55261         this.updateTitle(panel.getTitle());
55262         if(this.tabs){
55263             this.fireEvent("invalidated", this);
55264         }
55265         this.fireEvent("panelactivated", this, panel);
55266     },
55267
55268     /**
55269      * Shows the specified panel.
55270      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
55271      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
55272      */
55273     showPanel : function(panel)
55274     {
55275         panel = this.getPanel(panel);
55276         if(panel){
55277             if(this.tabs){
55278                 var tab = this.tabs.getTab(panel.getEl().id);
55279                 if(tab.isHidden()){
55280                     this.tabs.unhideTab(tab.id);
55281                 }
55282                 tab.activate();
55283             }else{
55284                 this.setActivePanel(panel);
55285             }
55286         }
55287         return panel;
55288     },
55289
55290     /**
55291      * Get the active panel for this region.
55292      * @return {Roo.ContentPanel} The active panel or null
55293      */
55294     getActivePanel : function(){
55295         return this.activePanel;
55296     },
55297
55298     validateVisibility : function(){
55299         if(this.panels.getCount() < 1){
55300             this.updateTitle("&#160;");
55301             this.closeBtn.hide();
55302             this.hide();
55303         }else{
55304             if(!this.isVisible()){
55305                 this.show();
55306             }
55307         }
55308     },
55309
55310     /**
55311      * Adds the passed ContentPanel(s) to this region.
55312      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55313      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
55314      */
55315     add : function(panel){
55316         if(arguments.length > 1){
55317             for(var i = 0, len = arguments.length; i < len; i++) {
55318                 this.add(arguments[i]);
55319             }
55320             return null;
55321         }
55322         if(this.hasPanel(panel)){
55323             this.showPanel(panel);
55324             return panel;
55325         }
55326         panel.setRegion(this);
55327         this.panels.add(panel);
55328         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
55329             this.bodyEl.dom.appendChild(panel.getEl().dom);
55330             if(panel.background !== true){
55331                 this.setActivePanel(panel);
55332             }
55333             this.fireEvent("paneladded", this, panel);
55334             return panel;
55335         }
55336         if(!this.tabs){
55337             this.initTabs();
55338         }else{
55339             this.initPanelAsTab(panel);
55340         }
55341         if(panel.background !== true){
55342             this.tabs.activate(panel.getEl().id);
55343         }
55344         this.fireEvent("paneladded", this, panel);
55345         return panel;
55346     },
55347
55348     /**
55349      * Hides the tab for the specified panel.
55350      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55351      */
55352     hidePanel : function(panel){
55353         if(this.tabs && (panel = this.getPanel(panel))){
55354             this.tabs.hideTab(panel.getEl().id);
55355         }
55356     },
55357
55358     /**
55359      * Unhides the tab for a previously hidden panel.
55360      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55361      */
55362     unhidePanel : function(panel){
55363         if(this.tabs && (panel = this.getPanel(panel))){
55364             this.tabs.unhideTab(panel.getEl().id);
55365         }
55366     },
55367
55368     clearPanels : function(){
55369         while(this.panels.getCount() > 0){
55370              this.remove(this.panels.first());
55371         }
55372     },
55373
55374     /**
55375      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55376      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55377      * @param {Boolean} preservePanel Overrides the config preservePanel option
55378      * @return {Roo.ContentPanel} The panel that was removed
55379      */
55380     remove : function(panel, preservePanel){
55381         panel = this.getPanel(panel);
55382         if(!panel){
55383             return null;
55384         }
55385         var e = {};
55386         this.fireEvent("beforeremove", this, panel, e);
55387         if(e.cancel === true){
55388             return null;
55389         }
55390         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
55391         var panelId = panel.getId();
55392         this.panels.removeKey(panelId);
55393         if(preservePanel){
55394             document.body.appendChild(panel.getEl().dom);
55395         }
55396         if(this.tabs){
55397             this.tabs.removeTab(panel.getEl().id);
55398         }else if (!preservePanel){
55399             this.bodyEl.dom.removeChild(panel.getEl().dom);
55400         }
55401         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
55402             var p = this.panels.first();
55403             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
55404             tempEl.appendChild(p.getEl().dom);
55405             this.bodyEl.update("");
55406             this.bodyEl.dom.appendChild(p.getEl().dom);
55407             tempEl = null;
55408             this.updateTitle(p.getTitle());
55409             this.tabs = null;
55410             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55411             this.setActivePanel(p);
55412         }
55413         panel.setRegion(null);
55414         if(this.activePanel == panel){
55415             this.activePanel = null;
55416         }
55417         if(this.config.autoDestroy !== false && preservePanel !== true){
55418             try{panel.destroy();}catch(e){}
55419         }
55420         this.fireEvent("panelremoved", this, panel);
55421         return panel;
55422     },
55423
55424     /**
55425      * Returns the TabPanel component used by this region
55426      * @return {Roo.TabPanel}
55427      */
55428     getTabs : function(){
55429         return this.tabs;
55430     },
55431
55432     createTool : function(parentEl, className){
55433         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
55434             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
55435         btn.addClassOnOver("x-layout-tools-button-over");
55436         return btn;
55437     }
55438 });/*
55439  * Based on:
55440  * Ext JS Library 1.1.1
55441  * Copyright(c) 2006-2007, Ext JS, LLC.
55442  *
55443  * Originally Released Under LGPL - original licence link has changed is not relivant.
55444  *
55445  * Fork - LGPL
55446  * <script type="text/javascript">
55447  */
55448  
55449
55450
55451 /**
55452  * @class Roo.SplitLayoutRegion
55453  * @extends Roo.LayoutRegion
55454  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
55455  */
55456 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
55457     this.cursor = cursor;
55458     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
55459 };
55460
55461 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
55462     splitTip : "Drag to resize.",
55463     collapsibleSplitTip : "Drag to resize. Double click to hide.",
55464     useSplitTips : false,
55465
55466     applyConfig : function(config){
55467         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
55468         if(config.split){
55469             if(!this.split){
55470                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
55471                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
55472                 /** The SplitBar for this region 
55473                 * @type Roo.SplitBar */
55474                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
55475                 this.split.on("moved", this.onSplitMove, this);
55476                 this.split.useShim = config.useShim === true;
55477                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
55478                 if(this.useSplitTips){
55479                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
55480                 }
55481                 if(config.collapsible){
55482                     this.split.el.on("dblclick", this.collapse,  this);
55483                 }
55484             }
55485             if(typeof config.minSize != "undefined"){
55486                 this.split.minSize = config.minSize;
55487             }
55488             if(typeof config.maxSize != "undefined"){
55489                 this.split.maxSize = config.maxSize;
55490             }
55491             if(config.hideWhenEmpty || config.hidden || config.collapsed){
55492                 this.hideSplitter();
55493             }
55494         }
55495     },
55496
55497     getHMaxSize : function(){
55498          var cmax = this.config.maxSize || 10000;
55499          var center = this.mgr.getRegion("center");
55500          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
55501     },
55502
55503     getVMaxSize : function(){
55504          var cmax = this.config.maxSize || 10000;
55505          var center = this.mgr.getRegion("center");
55506          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
55507     },
55508
55509     onSplitMove : function(split, newSize){
55510         this.fireEvent("resized", this, newSize);
55511     },
55512     
55513     /** 
55514      * Returns the {@link Roo.SplitBar} for this region.
55515      * @return {Roo.SplitBar}
55516      */
55517     getSplitBar : function(){
55518         return this.split;
55519     },
55520     
55521     hide : function(){
55522         this.hideSplitter();
55523         Roo.SplitLayoutRegion.superclass.hide.call(this);
55524     },
55525
55526     hideSplitter : function(){
55527         if(this.split){
55528             this.split.el.setLocation(-2000,-2000);
55529             this.split.el.hide();
55530         }
55531     },
55532
55533     show : function(){
55534         if(this.split){
55535             this.split.el.show();
55536         }
55537         Roo.SplitLayoutRegion.superclass.show.call(this);
55538     },
55539     
55540     beforeSlide: function(){
55541         if(Roo.isGecko){// firefox overflow auto bug workaround
55542             this.bodyEl.clip();
55543             if(this.tabs) {
55544                 this.tabs.bodyEl.clip();
55545             }
55546             if(this.activePanel){
55547                 this.activePanel.getEl().clip();
55548                 
55549                 if(this.activePanel.beforeSlide){
55550                     this.activePanel.beforeSlide();
55551                 }
55552             }
55553         }
55554     },
55555     
55556     afterSlide : function(){
55557         if(Roo.isGecko){// firefox overflow auto bug workaround
55558             this.bodyEl.unclip();
55559             if(this.tabs) {
55560                 this.tabs.bodyEl.unclip();
55561             }
55562             if(this.activePanel){
55563                 this.activePanel.getEl().unclip();
55564                 if(this.activePanel.afterSlide){
55565                     this.activePanel.afterSlide();
55566                 }
55567             }
55568         }
55569     },
55570
55571     initAutoHide : function(){
55572         if(this.autoHide !== false){
55573             if(!this.autoHideHd){
55574                 var st = new Roo.util.DelayedTask(this.slideIn, this);
55575                 this.autoHideHd = {
55576                     "mouseout": function(e){
55577                         if(!e.within(this.el, true)){
55578                             st.delay(500);
55579                         }
55580                     },
55581                     "mouseover" : function(e){
55582                         st.cancel();
55583                     },
55584                     scope : this
55585                 };
55586             }
55587             this.el.on(this.autoHideHd);
55588         }
55589     },
55590
55591     clearAutoHide : function(){
55592         if(this.autoHide !== false){
55593             this.el.un("mouseout", this.autoHideHd.mouseout);
55594             this.el.un("mouseover", this.autoHideHd.mouseover);
55595         }
55596     },
55597
55598     clearMonitor : function(){
55599         Roo.get(document).un("click", this.slideInIf, this);
55600     },
55601
55602     // these names are backwards but not changed for compat
55603     slideOut : function(){
55604         if(this.isSlid || this.el.hasActiveFx()){
55605             return;
55606         }
55607         this.isSlid = true;
55608         if(this.collapseBtn){
55609             this.collapseBtn.hide();
55610         }
55611         this.closeBtnState = this.closeBtn.getStyle('display');
55612         this.closeBtn.hide();
55613         if(this.stickBtn){
55614             this.stickBtn.show();
55615         }
55616         this.el.show();
55617         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
55618         this.beforeSlide();
55619         this.el.setStyle("z-index", 10001);
55620         this.el.slideIn(this.getSlideAnchor(), {
55621             callback: function(){
55622                 this.afterSlide();
55623                 this.initAutoHide();
55624                 Roo.get(document).on("click", this.slideInIf, this);
55625                 this.fireEvent("slideshow", this);
55626             },
55627             scope: this,
55628             block: true
55629         });
55630     },
55631
55632     afterSlideIn : function(){
55633         this.clearAutoHide();
55634         this.isSlid = false;
55635         this.clearMonitor();
55636         this.el.setStyle("z-index", "");
55637         if(this.collapseBtn){
55638             this.collapseBtn.show();
55639         }
55640         this.closeBtn.setStyle('display', this.closeBtnState);
55641         if(this.stickBtn){
55642             this.stickBtn.hide();
55643         }
55644         this.fireEvent("slidehide", this);
55645     },
55646
55647     slideIn : function(cb){
55648         if(!this.isSlid || this.el.hasActiveFx()){
55649             Roo.callback(cb);
55650             return;
55651         }
55652         this.isSlid = false;
55653         this.beforeSlide();
55654         this.el.slideOut(this.getSlideAnchor(), {
55655             callback: function(){
55656                 this.el.setLeftTop(-10000, -10000);
55657                 this.afterSlide();
55658                 this.afterSlideIn();
55659                 Roo.callback(cb);
55660             },
55661             scope: this,
55662             block: true
55663         });
55664     },
55665     
55666     slideInIf : function(e){
55667         if(!e.within(this.el)){
55668             this.slideIn();
55669         }
55670     },
55671
55672     animateCollapse : function(){
55673         this.beforeSlide();
55674         this.el.setStyle("z-index", 20000);
55675         var anchor = this.getSlideAnchor();
55676         this.el.slideOut(anchor, {
55677             callback : function(){
55678                 this.el.setStyle("z-index", "");
55679                 this.collapsedEl.slideIn(anchor, {duration:.3});
55680                 this.afterSlide();
55681                 this.el.setLocation(-10000,-10000);
55682                 this.el.hide();
55683                 this.fireEvent("collapsed", this);
55684             },
55685             scope: this,
55686             block: true
55687         });
55688     },
55689
55690     animateExpand : function(){
55691         this.beforeSlide();
55692         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
55693         this.el.setStyle("z-index", 20000);
55694         this.collapsedEl.hide({
55695             duration:.1
55696         });
55697         this.el.slideIn(this.getSlideAnchor(), {
55698             callback : function(){
55699                 this.el.setStyle("z-index", "");
55700                 this.afterSlide();
55701                 if(this.split){
55702                     this.split.el.show();
55703                 }
55704                 this.fireEvent("invalidated", this);
55705                 this.fireEvent("expanded", this);
55706             },
55707             scope: this,
55708             block: true
55709         });
55710     },
55711
55712     anchors : {
55713         "west" : "left",
55714         "east" : "right",
55715         "north" : "top",
55716         "south" : "bottom"
55717     },
55718
55719     sanchors : {
55720         "west" : "l",
55721         "east" : "r",
55722         "north" : "t",
55723         "south" : "b"
55724     },
55725
55726     canchors : {
55727         "west" : "tl-tr",
55728         "east" : "tr-tl",
55729         "north" : "tl-bl",
55730         "south" : "bl-tl"
55731     },
55732
55733     getAnchor : function(){
55734         return this.anchors[this.position];
55735     },
55736
55737     getCollapseAnchor : function(){
55738         return this.canchors[this.position];
55739     },
55740
55741     getSlideAnchor : function(){
55742         return this.sanchors[this.position];
55743     },
55744
55745     getAlignAdj : function(){
55746         var cm = this.cmargins;
55747         switch(this.position){
55748             case "west":
55749                 return [0, 0];
55750             break;
55751             case "east":
55752                 return [0, 0];
55753             break;
55754             case "north":
55755                 return [0, 0];
55756             break;
55757             case "south":
55758                 return [0, 0];
55759             break;
55760         }
55761     },
55762
55763     getExpandAdj : function(){
55764         var c = this.collapsedEl, cm = this.cmargins;
55765         switch(this.position){
55766             case "west":
55767                 return [-(cm.right+c.getWidth()+cm.left), 0];
55768             break;
55769             case "east":
55770                 return [cm.right+c.getWidth()+cm.left, 0];
55771             break;
55772             case "north":
55773                 return [0, -(cm.top+cm.bottom+c.getHeight())];
55774             break;
55775             case "south":
55776                 return [0, cm.top+cm.bottom+c.getHeight()];
55777             break;
55778         }
55779     }
55780 });/*
55781  * Based on:
55782  * Ext JS Library 1.1.1
55783  * Copyright(c) 2006-2007, Ext JS, LLC.
55784  *
55785  * Originally Released Under LGPL - original licence link has changed is not relivant.
55786  *
55787  * Fork - LGPL
55788  * <script type="text/javascript">
55789  */
55790 /*
55791  * These classes are private internal classes
55792  */
55793 Roo.CenterLayoutRegion = function(mgr, config){
55794     Roo.LayoutRegion.call(this, mgr, config, "center");
55795     this.visible = true;
55796     this.minWidth = config.minWidth || 20;
55797     this.minHeight = config.minHeight || 20;
55798 };
55799
55800 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
55801     hide : function(){
55802         // center panel can't be hidden
55803     },
55804     
55805     show : function(){
55806         // center panel can't be hidden
55807     },
55808     
55809     getMinWidth: function(){
55810         return this.minWidth;
55811     },
55812     
55813     getMinHeight: function(){
55814         return this.minHeight;
55815     }
55816 });
55817
55818
55819 Roo.NorthLayoutRegion = function(mgr, config){
55820     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
55821     if(this.split){
55822         this.split.placement = Roo.SplitBar.TOP;
55823         this.split.orientation = Roo.SplitBar.VERTICAL;
55824         this.split.el.addClass("x-layout-split-v");
55825     }
55826     var size = config.initialSize || config.height;
55827     if(typeof size != "undefined"){
55828         this.el.setHeight(size);
55829     }
55830 };
55831 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
55832     orientation: Roo.SplitBar.VERTICAL,
55833     getBox : function(){
55834         if(this.collapsed){
55835             return this.collapsedEl.getBox();
55836         }
55837         var box = this.el.getBox();
55838         if(this.split){
55839             box.height += this.split.el.getHeight();
55840         }
55841         return box;
55842     },
55843     
55844     updateBox : function(box){
55845         if(this.split && !this.collapsed){
55846             box.height -= this.split.el.getHeight();
55847             this.split.el.setLeft(box.x);
55848             this.split.el.setTop(box.y+box.height);
55849             this.split.el.setWidth(box.width);
55850         }
55851         if(this.collapsed){
55852             this.updateBody(box.width, null);
55853         }
55854         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55855     }
55856 });
55857
55858 Roo.SouthLayoutRegion = function(mgr, config){
55859     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
55860     if(this.split){
55861         this.split.placement = Roo.SplitBar.BOTTOM;
55862         this.split.orientation = Roo.SplitBar.VERTICAL;
55863         this.split.el.addClass("x-layout-split-v");
55864     }
55865     var size = config.initialSize || config.height;
55866     if(typeof size != "undefined"){
55867         this.el.setHeight(size);
55868     }
55869 };
55870 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
55871     orientation: Roo.SplitBar.VERTICAL,
55872     getBox : function(){
55873         if(this.collapsed){
55874             return this.collapsedEl.getBox();
55875         }
55876         var box = this.el.getBox();
55877         if(this.split){
55878             var sh = this.split.el.getHeight();
55879             box.height += sh;
55880             box.y -= sh;
55881         }
55882         return box;
55883     },
55884     
55885     updateBox : function(box){
55886         if(this.split && !this.collapsed){
55887             var sh = this.split.el.getHeight();
55888             box.height -= sh;
55889             box.y += sh;
55890             this.split.el.setLeft(box.x);
55891             this.split.el.setTop(box.y-sh);
55892             this.split.el.setWidth(box.width);
55893         }
55894         if(this.collapsed){
55895             this.updateBody(box.width, null);
55896         }
55897         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55898     }
55899 });
55900
55901 Roo.EastLayoutRegion = function(mgr, config){
55902     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
55903     if(this.split){
55904         this.split.placement = Roo.SplitBar.RIGHT;
55905         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55906         this.split.el.addClass("x-layout-split-h");
55907     }
55908     var size = config.initialSize || config.width;
55909     if(typeof size != "undefined"){
55910         this.el.setWidth(size);
55911     }
55912 };
55913 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
55914     orientation: Roo.SplitBar.HORIZONTAL,
55915     getBox : function(){
55916         if(this.collapsed){
55917             return this.collapsedEl.getBox();
55918         }
55919         var box = this.el.getBox();
55920         if(this.split){
55921             var sw = this.split.el.getWidth();
55922             box.width += sw;
55923             box.x -= sw;
55924         }
55925         return box;
55926     },
55927
55928     updateBox : function(box){
55929         if(this.split && !this.collapsed){
55930             var sw = this.split.el.getWidth();
55931             box.width -= sw;
55932             this.split.el.setLeft(box.x);
55933             this.split.el.setTop(box.y);
55934             this.split.el.setHeight(box.height);
55935             box.x += sw;
55936         }
55937         if(this.collapsed){
55938             this.updateBody(null, box.height);
55939         }
55940         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55941     }
55942 });
55943
55944 Roo.WestLayoutRegion = function(mgr, config){
55945     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
55946     if(this.split){
55947         this.split.placement = Roo.SplitBar.LEFT;
55948         this.split.orientation = Roo.SplitBar.HORIZONTAL;
55949         this.split.el.addClass("x-layout-split-h");
55950     }
55951     var size = config.initialSize || config.width;
55952     if(typeof size != "undefined"){
55953         this.el.setWidth(size);
55954     }
55955 };
55956 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
55957     orientation: Roo.SplitBar.HORIZONTAL,
55958     getBox : function(){
55959         if(this.collapsed){
55960             return this.collapsedEl.getBox();
55961         }
55962         var box = this.el.getBox();
55963         if(this.split){
55964             box.width += this.split.el.getWidth();
55965         }
55966         return box;
55967     },
55968     
55969     updateBox : function(box){
55970         if(this.split && !this.collapsed){
55971             var sw = this.split.el.getWidth();
55972             box.width -= sw;
55973             this.split.el.setLeft(box.x+box.width);
55974             this.split.el.setTop(box.y);
55975             this.split.el.setHeight(box.height);
55976         }
55977         if(this.collapsed){
55978             this.updateBody(null, box.height);
55979         }
55980         Roo.LayoutRegion.prototype.updateBox.call(this, box);
55981     }
55982 });
55983 /*
55984  * Based on:
55985  * Ext JS Library 1.1.1
55986  * Copyright(c) 2006-2007, Ext JS, LLC.
55987  *
55988  * Originally Released Under LGPL - original licence link has changed is not relivant.
55989  *
55990  * Fork - LGPL
55991  * <script type="text/javascript">
55992  */
55993  
55994  
55995 /*
55996  * Private internal class for reading and applying state
55997  */
55998 Roo.LayoutStateManager = function(layout){
55999      // default empty state
56000      this.state = {
56001         north: {},
56002         south: {},
56003         east: {},
56004         west: {}       
56005     };
56006 };
56007
56008 Roo.LayoutStateManager.prototype = {
56009     init : function(layout, provider){
56010         this.provider = provider;
56011         var state = provider.get(layout.id+"-layout-state");
56012         if(state){
56013             var wasUpdating = layout.isUpdating();
56014             if(!wasUpdating){
56015                 layout.beginUpdate();
56016             }
56017             for(var key in state){
56018                 if(typeof state[key] != "function"){
56019                     var rstate = state[key];
56020                     var r = layout.getRegion(key);
56021                     if(r && rstate){
56022                         if(rstate.size){
56023                             r.resizeTo(rstate.size);
56024                         }
56025                         if(rstate.collapsed == true){
56026                             r.collapse(true);
56027                         }else{
56028                             r.expand(null, true);
56029                         }
56030                     }
56031                 }
56032             }
56033             if(!wasUpdating){
56034                 layout.endUpdate();
56035             }
56036             this.state = state; 
56037         }
56038         this.layout = layout;
56039         layout.on("regionresized", this.onRegionResized, this);
56040         layout.on("regioncollapsed", this.onRegionCollapsed, this);
56041         layout.on("regionexpanded", this.onRegionExpanded, this);
56042     },
56043     
56044     storeState : function(){
56045         this.provider.set(this.layout.id+"-layout-state", this.state);
56046     },
56047     
56048     onRegionResized : function(region, newSize){
56049         this.state[region.getPosition()].size = newSize;
56050         this.storeState();
56051     },
56052     
56053     onRegionCollapsed : function(region){
56054         this.state[region.getPosition()].collapsed = true;
56055         this.storeState();
56056     },
56057     
56058     onRegionExpanded : function(region){
56059         this.state[region.getPosition()].collapsed = false;
56060         this.storeState();
56061     }
56062 };/*
56063  * Based on:
56064  * Ext JS Library 1.1.1
56065  * Copyright(c) 2006-2007, Ext JS, LLC.
56066  *
56067  * Originally Released Under LGPL - original licence link has changed is not relivant.
56068  *
56069  * Fork - LGPL
56070  * <script type="text/javascript">
56071  */
56072 /**
56073  * @class Roo.ContentPanel
56074  * @extends Roo.util.Observable
56075  * @children Roo.form.Form Roo.JsonView Roo.View
56076  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
56077  * A basic ContentPanel element.
56078  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
56079  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
56080  * @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
56081  * @cfg {Boolean}   closable      True if the panel can be closed/removed
56082  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
56083  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
56084  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
56085  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
56086  * @cfg {String} title          The title for this panel
56087  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
56088  * @cfg {String} url            Calls {@link #setUrl} with this value
56089  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
56090  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
56091  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
56092  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
56093  * @cfg {String}    style  Extra style to add to the content panel
56094  * @cfg {Roo.menu.Menu} menu  popup menu
56095
56096  * @constructor
56097  * Create a new ContentPanel.
56098  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
56099  * @param {String/Object} config A string to set only the title or a config object
56100  * @param {String} content (optional) Set the HTML content for this panel
56101  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
56102  */
56103 Roo.ContentPanel = function(el, config, content){
56104     
56105      
56106     /*
56107     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
56108         config = el;
56109         el = Roo.id();
56110     }
56111     if (config && config.parentLayout) { 
56112         el = config.parentLayout.el.createChild(); 
56113     }
56114     */
56115     if(el.autoCreate){ // xtype is available if this is called from factory
56116         config = el;
56117         el = Roo.id();
56118     }
56119     this.el = Roo.get(el);
56120     if(!this.el && config && config.autoCreate){
56121         if(typeof config.autoCreate == "object"){
56122             if(!config.autoCreate.id){
56123                 config.autoCreate.id = config.id||el;
56124             }
56125             this.el = Roo.DomHelper.append(document.body,
56126                         config.autoCreate, true);
56127         }else{
56128             this.el = Roo.DomHelper.append(document.body,
56129                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
56130         }
56131     }
56132     
56133     
56134     this.closable = false;
56135     this.loaded = false;
56136     this.active = false;
56137     if(typeof config == "string"){
56138         this.title = config;
56139     }else{
56140         Roo.apply(this, config);
56141     }
56142     
56143     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
56144         this.wrapEl = this.el.wrap();
56145         this.toolbar.container = this.el.insertSibling(false, 'before');
56146         this.toolbar = new Roo.Toolbar(this.toolbar);
56147     }
56148     
56149     // xtype created footer. - not sure if will work as we normally have to render first..
56150     if (this.footer && !this.footer.el && this.footer.xtype) {
56151         if (!this.wrapEl) {
56152             this.wrapEl = this.el.wrap();
56153         }
56154     
56155         this.footer.container = this.wrapEl.createChild();
56156          
56157         this.footer = Roo.factory(this.footer, Roo);
56158         
56159     }
56160     
56161     if(this.resizeEl){
56162         this.resizeEl = Roo.get(this.resizeEl, true);
56163     }else{
56164         this.resizeEl = this.el;
56165     }
56166     // handle view.xtype
56167     
56168  
56169     
56170     
56171     this.addEvents({
56172         /**
56173          * @event activate
56174          * Fires when this panel is activated. 
56175          * @param {Roo.ContentPanel} this
56176          */
56177         "activate" : true,
56178         /**
56179          * @event deactivate
56180          * Fires when this panel is activated. 
56181          * @param {Roo.ContentPanel} this
56182          */
56183         "deactivate" : true,
56184
56185         /**
56186          * @event resize
56187          * Fires when this panel is resized if fitToFrame is true.
56188          * @param {Roo.ContentPanel} this
56189          * @param {Number} width The width after any component adjustments
56190          * @param {Number} height The height after any component adjustments
56191          */
56192         "resize" : true,
56193         
56194          /**
56195          * @event render
56196          * Fires when this tab is created
56197          * @param {Roo.ContentPanel} this
56198          */
56199         "render" : true
56200          
56201         
56202     });
56203     
56204
56205     
56206     
56207     if(this.autoScroll){
56208         this.resizeEl.setStyle("overflow", "auto");
56209     } else {
56210         // fix randome scrolling
56211         this.el.on('scroll', function() {
56212             Roo.log('fix random scolling');
56213             this.scrollTo('top',0); 
56214         });
56215     }
56216     content = content || this.content;
56217     if(content){
56218         this.setContent(content);
56219     }
56220     if(config && config.url){
56221         this.setUrl(this.url, this.params, this.loadOnce);
56222     }
56223     
56224     
56225     
56226     Roo.ContentPanel.superclass.constructor.call(this);
56227     
56228     if (this.view && typeof(this.view.xtype) != 'undefined') {
56229         this.view.el = this.el.appendChild(document.createElement("div"));
56230         this.view = Roo.factory(this.view); 
56231         this.view.render  &&  this.view.render(false, '');  
56232     }
56233     
56234     
56235     this.fireEvent('render', this);
56236 };
56237
56238 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
56239     tabTip:'',
56240     setRegion : function(region){
56241         this.region = region;
56242         if(region){
56243            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
56244         }else{
56245            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
56246         } 
56247     },
56248     
56249     /**
56250      * Returns the toolbar for this Panel if one was configured. 
56251      * @return {Roo.Toolbar} 
56252      */
56253     getToolbar : function(){
56254         return this.toolbar;
56255     },
56256     
56257     setActiveState : function(active){
56258         this.active = active;
56259         if(!active){
56260             this.fireEvent("deactivate", this);
56261         }else{
56262             this.fireEvent("activate", this);
56263         }
56264     },
56265     /**
56266      * Updates this panel's element
56267      * @param {String} content The new content
56268      * @param {Boolean} loadScripts (optional) true to look for and process scripts
56269     */
56270     setContent : function(content, loadScripts){
56271         this.el.update(content, loadScripts);
56272     },
56273
56274     ignoreResize : function(w, h){
56275         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
56276             return true;
56277         }else{
56278             this.lastSize = {width: w, height: h};
56279             return false;
56280         }
56281     },
56282     /**
56283      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
56284      * @return {Roo.UpdateManager} The UpdateManager
56285      */
56286     getUpdateManager : function(){
56287         return this.el.getUpdateManager();
56288     },
56289      /**
56290      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
56291      * @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:
56292 <pre><code>
56293 panel.load({
56294     url: "your-url.php",
56295     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
56296     callback: yourFunction,
56297     scope: yourObject, //(optional scope)
56298     discardUrl: false,
56299     nocache: false,
56300     text: "Loading...",
56301     timeout: 30,
56302     scripts: false
56303 });
56304 </code></pre>
56305      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
56306      * 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.
56307      * @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}
56308      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
56309      * @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.
56310      * @return {Roo.ContentPanel} this
56311      */
56312     load : function(){
56313         var um = this.el.getUpdateManager();
56314         um.update.apply(um, arguments);
56315         return this;
56316     },
56317
56318
56319     /**
56320      * 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.
56321      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
56322      * @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)
56323      * @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)
56324      * @return {Roo.UpdateManager} The UpdateManager
56325      */
56326     setUrl : function(url, params, loadOnce){
56327         if(this.refreshDelegate){
56328             this.removeListener("activate", this.refreshDelegate);
56329         }
56330         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
56331         this.on("activate", this.refreshDelegate);
56332         return this.el.getUpdateManager();
56333     },
56334     
56335     _handleRefresh : function(url, params, loadOnce){
56336         if(!loadOnce || !this.loaded){
56337             var updater = this.el.getUpdateManager();
56338             updater.update(url, params, this._setLoaded.createDelegate(this));
56339         }
56340     },
56341     
56342     _setLoaded : function(){
56343         this.loaded = true;
56344     }, 
56345     
56346     /**
56347      * Returns this panel's id
56348      * @return {String} 
56349      */
56350     getId : function(){
56351         return this.el.id;
56352     },
56353     
56354     /** 
56355      * Returns this panel's element - used by regiosn to add.
56356      * @return {Roo.Element} 
56357      */
56358     getEl : function(){
56359         return this.wrapEl || this.el;
56360     },
56361     
56362     adjustForComponents : function(width, height)
56363     {
56364         //Roo.log('adjustForComponents ');
56365         if(this.resizeEl != this.el){
56366             width -= this.el.getFrameWidth('lr');
56367             height -= this.el.getFrameWidth('tb');
56368         }
56369         if(this.toolbar){
56370             var te = this.toolbar.getEl();
56371             height -= te.getHeight();
56372             te.setWidth(width);
56373         }
56374         if(this.footer){
56375             var te = this.footer.getEl();
56376             //Roo.log("footer:" + te.getHeight());
56377             
56378             height -= te.getHeight();
56379             te.setWidth(width);
56380         }
56381         
56382         
56383         if(this.adjustments){
56384             width += this.adjustments[0];
56385             height += this.adjustments[1];
56386         }
56387         return {"width": width, "height": height};
56388     },
56389     
56390     setSize : function(width, height){
56391         if(this.fitToFrame && !this.ignoreResize(width, height)){
56392             if(this.fitContainer && this.resizeEl != this.el){
56393                 this.el.setSize(width, height);
56394             }
56395             var size = this.adjustForComponents(width, height);
56396             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
56397             this.fireEvent('resize', this, size.width, size.height);
56398         }
56399     },
56400     
56401     /**
56402      * Returns this panel's title
56403      * @return {String} 
56404      */
56405     getTitle : function(){
56406         return this.title;
56407     },
56408     
56409     /**
56410      * Set this panel's title
56411      * @param {String} title
56412      */
56413     setTitle : function(title){
56414         this.title = title;
56415         if(this.region){
56416             this.region.updatePanelTitle(this, title);
56417         }
56418     },
56419     
56420     /**
56421      * Returns true is this panel was configured to be closable
56422      * @return {Boolean} 
56423      */
56424     isClosable : function(){
56425         return this.closable;
56426     },
56427     
56428     beforeSlide : function(){
56429         this.el.clip();
56430         this.resizeEl.clip();
56431     },
56432     
56433     afterSlide : function(){
56434         this.el.unclip();
56435         this.resizeEl.unclip();
56436     },
56437     
56438     /**
56439      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
56440      *   Will fail silently if the {@link #setUrl} method has not been called.
56441      *   This does not activate the panel, just updates its content.
56442      */
56443     refresh : function(){
56444         if(this.refreshDelegate){
56445            this.loaded = false;
56446            this.refreshDelegate();
56447         }
56448     },
56449     
56450     /**
56451      * Destroys this panel
56452      */
56453     destroy : function(){
56454         this.el.removeAllListeners();
56455         var tempEl = document.createElement("span");
56456         tempEl.appendChild(this.el.dom);
56457         tempEl.innerHTML = "";
56458         this.el.remove();
56459         this.el = null;
56460     },
56461     
56462     /**
56463      * form - if the content panel contains a form - this is a reference to it.
56464      * @type {Roo.form.Form}
56465      */
56466     form : false,
56467     /**
56468      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
56469      *    This contains a reference to it.
56470      * @type {Roo.View}
56471      */
56472     view : false,
56473     
56474       /**
56475      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
56476      * <pre><code>
56477
56478 layout.addxtype({
56479        xtype : 'Form',
56480        items: [ .... ]
56481    }
56482 );
56483
56484 </code></pre>
56485      * @param {Object} cfg Xtype definition of item to add.
56486      */
56487     
56488     addxtype : function(cfg) {
56489         // add form..
56490         if (cfg.xtype.match(/^Form$/)) {
56491             
56492             var el;
56493             //if (this.footer) {
56494             //    el = this.footer.container.insertSibling(false, 'before');
56495             //} else {
56496                 el = this.el.createChild();
56497             //}
56498
56499             this.form = new  Roo.form.Form(cfg);
56500             
56501             
56502             if ( this.form.allItems.length) {
56503                 this.form.render(el.dom);
56504             }
56505             return this.form;
56506         }
56507         // should only have one of theses..
56508         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
56509             // views.. should not be just added - used named prop 'view''
56510             
56511             cfg.el = this.el.appendChild(document.createElement("div"));
56512             // factory?
56513             
56514             var ret = new Roo.factory(cfg);
56515              
56516              ret.render && ret.render(false, ''); // render blank..
56517             this.view = ret;
56518             return ret;
56519         }
56520         return false;
56521     }
56522 });
56523
56524 /**
56525  * @class Roo.GridPanel
56526  * @extends Roo.ContentPanel
56527  * @constructor
56528  * Create a new GridPanel.
56529  * @param {Roo.grid.Grid} grid The grid for this panel
56530  * @param {String/Object} config A string to set only the panel's title, or a config object
56531  */
56532 Roo.GridPanel = function(grid, config){
56533     
56534   
56535     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
56536         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
56537         
56538     this.wrapper.dom.appendChild(grid.getGridEl().dom);
56539     
56540     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
56541     
56542     if(this.toolbar){
56543         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
56544     }
56545     // xtype created footer. - not sure if will work as we normally have to render first..
56546     if (this.footer && !this.footer.el && this.footer.xtype) {
56547         
56548         this.footer.container = this.grid.getView().getFooterPanel(true);
56549         this.footer.dataSource = this.grid.dataSource;
56550         this.footer = Roo.factory(this.footer, Roo);
56551         
56552     }
56553     
56554     grid.monitorWindowResize = false; // turn off autosizing
56555     grid.autoHeight = false;
56556     grid.autoWidth = false;
56557     this.grid = grid;
56558     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
56559 };
56560
56561 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
56562     getId : function(){
56563         return this.grid.id;
56564     },
56565     
56566     /**
56567      * Returns the grid for this panel
56568      * @return {Roo.grid.Grid} 
56569      */
56570     getGrid : function(){
56571         return this.grid;    
56572     },
56573     
56574     setSize : function(width, height){
56575         if(!this.ignoreResize(width, height)){
56576             var grid = this.grid;
56577             var size = this.adjustForComponents(width, height);
56578             grid.getGridEl().setSize(size.width, size.height);
56579             grid.autoSize();
56580         }
56581     },
56582     
56583     beforeSlide : function(){
56584         this.grid.getView().scroller.clip();
56585     },
56586     
56587     afterSlide : function(){
56588         this.grid.getView().scroller.unclip();
56589     },
56590     
56591     destroy : function(){
56592         this.grid.destroy();
56593         delete this.grid;
56594         Roo.GridPanel.superclass.destroy.call(this); 
56595     }
56596 });
56597
56598
56599 /**
56600  * @class Roo.NestedLayoutPanel
56601  * @extends Roo.ContentPanel
56602  * @constructor
56603  * Create a new NestedLayoutPanel.
56604  * 
56605  * 
56606  * @param {Roo.BorderLayout} layout [required] The layout for this panel
56607  * @param {String/Object} config A string to set only the title or a config object
56608  */
56609 Roo.NestedLayoutPanel = function(layout, config)
56610 {
56611     // construct with only one argument..
56612     /* FIXME - implement nicer consturctors
56613     if (layout.layout) {
56614         config = layout;
56615         layout = config.layout;
56616         delete config.layout;
56617     }
56618     if (layout.xtype && !layout.getEl) {
56619         // then layout needs constructing..
56620         layout = Roo.factory(layout, Roo);
56621     }
56622     */
56623     
56624     
56625     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
56626     
56627     layout.monitorWindowResize = false; // turn off autosizing
56628     this.layout = layout;
56629     this.layout.getEl().addClass("x-layout-nested-layout");
56630     
56631     
56632     
56633     
56634 };
56635
56636 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
56637
56638     setSize : function(width, height){
56639         if(!this.ignoreResize(width, height)){
56640             var size = this.adjustForComponents(width, height);
56641             var el = this.layout.getEl();
56642             el.setSize(size.width, size.height);
56643             var touch = el.dom.offsetWidth;
56644             this.layout.layout();
56645             // ie requires a double layout on the first pass
56646             if(Roo.isIE && !this.initialized){
56647                 this.initialized = true;
56648                 this.layout.layout();
56649             }
56650         }
56651     },
56652     
56653     // activate all subpanels if not currently active..
56654     
56655     setActiveState : function(active){
56656         this.active = active;
56657         if(!active){
56658             this.fireEvent("deactivate", this);
56659             return;
56660         }
56661         
56662         this.fireEvent("activate", this);
56663         // not sure if this should happen before or after..
56664         if (!this.layout) {
56665             return; // should not happen..
56666         }
56667         var reg = false;
56668         for (var r in this.layout.regions) {
56669             reg = this.layout.getRegion(r);
56670             if (reg.getActivePanel()) {
56671                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
56672                 reg.setActivePanel(reg.getActivePanel());
56673                 continue;
56674             }
56675             if (!reg.panels.length) {
56676                 continue;
56677             }
56678             reg.showPanel(reg.getPanel(0));
56679         }
56680         
56681         
56682         
56683         
56684     },
56685     
56686     /**
56687      * Returns the nested BorderLayout for this panel
56688      * @return {Roo.BorderLayout} 
56689      */
56690     getLayout : function(){
56691         return this.layout;
56692     },
56693     
56694      /**
56695      * Adds a xtype elements to the layout of the nested panel
56696      * <pre><code>
56697
56698 panel.addxtype({
56699        xtype : 'ContentPanel',
56700        region: 'west',
56701        items: [ .... ]
56702    }
56703 );
56704
56705 panel.addxtype({
56706         xtype : 'NestedLayoutPanel',
56707         region: 'west',
56708         layout: {
56709            center: { },
56710            west: { }   
56711         },
56712         items : [ ... list of content panels or nested layout panels.. ]
56713    }
56714 );
56715 </code></pre>
56716      * @param {Object} cfg Xtype definition of item to add.
56717      */
56718     addxtype : function(cfg) {
56719         return this.layout.addxtype(cfg);
56720     
56721     }
56722 });
56723
56724 Roo.ScrollPanel = function(el, config, content){
56725     config = config || {};
56726     config.fitToFrame = true;
56727     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
56728     
56729     this.el.dom.style.overflow = "hidden";
56730     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
56731     this.el.removeClass("x-layout-inactive-content");
56732     this.el.on("mousewheel", this.onWheel, this);
56733
56734     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
56735     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
56736     up.unselectable(); down.unselectable();
56737     up.on("click", this.scrollUp, this);
56738     down.on("click", this.scrollDown, this);
56739     up.addClassOnOver("x-scroller-btn-over");
56740     down.addClassOnOver("x-scroller-btn-over");
56741     up.addClassOnClick("x-scroller-btn-click");
56742     down.addClassOnClick("x-scroller-btn-click");
56743     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
56744
56745     this.resizeEl = this.el;
56746     this.el = wrap; this.up = up; this.down = down;
56747 };
56748
56749 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
56750     increment : 100,
56751     wheelIncrement : 5,
56752     scrollUp : function(){
56753         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
56754     },
56755
56756     scrollDown : function(){
56757         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
56758     },
56759
56760     afterScroll : function(){
56761         var el = this.resizeEl;
56762         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
56763         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56764         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
56765     },
56766
56767     setSize : function(){
56768         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
56769         this.afterScroll();
56770     },
56771
56772     onWheel : function(e){
56773         var d = e.getWheelDelta();
56774         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
56775         this.afterScroll();
56776         e.stopEvent();
56777     },
56778
56779     setContent : function(content, loadScripts){
56780         this.resizeEl.update(content, loadScripts);
56781     }
56782
56783 });
56784
56785
56786
56787 /**
56788  * @class Roo.TreePanel
56789  * @extends Roo.ContentPanel
56790  * Treepanel component
56791  * 
56792  * @constructor
56793  * Create a new TreePanel. - defaults to fit/scoll contents.
56794  * @param {String/Object} config A string to set only the panel's title, or a config object
56795  */
56796 Roo.TreePanel = function(config){
56797     var el = config.el;
56798     var tree = config.tree;
56799     delete config.tree; 
56800     delete config.el; // hopefull!
56801     
56802     // wrapper for IE7 strict & safari scroll issue
56803     
56804     var treeEl = el.createChild();
56805     config.resizeEl = treeEl;
56806     
56807     
56808     
56809     Roo.TreePanel.superclass.constructor.call(this, el, config);
56810  
56811  
56812     this.tree = new Roo.tree.TreePanel(treeEl , tree);
56813     //console.log(tree);
56814     this.on('activate', function()
56815     {
56816         if (this.tree.rendered) {
56817             return;
56818         }
56819         //console.log('render tree');
56820         this.tree.render();
56821     });
56822     // this should not be needed.. - it's actually the 'el' that resizes?
56823     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
56824     
56825     //this.on('resize',  function (cp, w, h) {
56826     //        this.tree.innerCt.setWidth(w);
56827     //        this.tree.innerCt.setHeight(h);
56828     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
56829     //});
56830
56831         
56832     
56833 };
56834
56835 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
56836     fitToFrame : true,
56837     autoScroll : true,
56838     /*
56839      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
56840      */
56841     tree : false
56842
56843 });
56844
56845
56846
56847
56848
56849
56850
56851
56852
56853
56854
56855 /*
56856  * Based on:
56857  * Ext JS Library 1.1.1
56858  * Copyright(c) 2006-2007, Ext JS, LLC.
56859  *
56860  * Originally Released Under LGPL - original licence link has changed is not relivant.
56861  *
56862  * Fork - LGPL
56863  * <script type="text/javascript">
56864  */
56865  
56866
56867 /**
56868  * @class Roo.ReaderLayout
56869  * @extends Roo.BorderLayout
56870  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
56871  * center region containing two nested regions (a top one for a list view and one for item preview below),
56872  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
56873  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
56874  * expedites the setup of the overall layout and regions for this common application style.
56875  * Example:
56876  <pre><code>
56877 var reader = new Roo.ReaderLayout();
56878 var CP = Roo.ContentPanel;  // shortcut for adding
56879
56880 reader.beginUpdate();
56881 reader.add("north", new CP("north", "North"));
56882 reader.add("west", new CP("west", {title: "West"}));
56883 reader.add("east", new CP("east", {title: "East"}));
56884
56885 reader.regions.listView.add(new CP("listView", "List"));
56886 reader.regions.preview.add(new CP("preview", "Preview"));
56887 reader.endUpdate();
56888 </code></pre>
56889 * @constructor
56890 * Create a new ReaderLayout
56891 * @param {Object} config Configuration options
56892 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
56893 * document.body if omitted)
56894 */
56895 Roo.ReaderLayout = function(config, renderTo){
56896     var c = config || {size:{}};
56897     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
56898         north: c.north !== false ? Roo.apply({
56899             split:false,
56900             initialSize: 32,
56901             titlebar: false
56902         }, c.north) : false,
56903         west: c.west !== false ? Roo.apply({
56904             split:true,
56905             initialSize: 200,
56906             minSize: 175,
56907             maxSize: 400,
56908             titlebar: true,
56909             collapsible: true,
56910             animate: true,
56911             margins:{left:5,right:0,bottom:5,top:5},
56912             cmargins:{left:5,right:5,bottom:5,top:5}
56913         }, c.west) : false,
56914         east: c.east !== false ? Roo.apply({
56915             split:true,
56916             initialSize: 200,
56917             minSize: 175,
56918             maxSize: 400,
56919             titlebar: true,
56920             collapsible: true,
56921             animate: true,
56922             margins:{left:0,right:5,bottom:5,top:5},
56923             cmargins:{left:5,right:5,bottom:5,top:5}
56924         }, c.east) : false,
56925         center: Roo.apply({
56926             tabPosition: 'top',
56927             autoScroll:false,
56928             closeOnTab: true,
56929             titlebar:false,
56930             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
56931         }, c.center)
56932     });
56933
56934     this.el.addClass('x-reader');
56935
56936     this.beginUpdate();
56937
56938     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
56939         south: c.preview !== false ? Roo.apply({
56940             split:true,
56941             initialSize: 200,
56942             minSize: 100,
56943             autoScroll:true,
56944             collapsible:true,
56945             titlebar: true,
56946             cmargins:{top:5,left:0, right:0, bottom:0}
56947         }, c.preview) : false,
56948         center: Roo.apply({
56949             autoScroll:false,
56950             titlebar:false,
56951             minHeight:200
56952         }, c.listView)
56953     });
56954     this.add('center', new Roo.NestedLayoutPanel(inner,
56955             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
56956
56957     this.endUpdate();
56958
56959     this.regions.preview = inner.getRegion('south');
56960     this.regions.listView = inner.getRegion('center');
56961 };
56962
56963 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
56964  * Based on:
56965  * Ext JS Library 1.1.1
56966  * Copyright(c) 2006-2007, Ext JS, LLC.
56967  *
56968  * Originally Released Under LGPL - original licence link has changed is not relivant.
56969  *
56970  * Fork - LGPL
56971  * <script type="text/javascript">
56972  */
56973  
56974 /**
56975  * @class Roo.grid.Grid
56976  * @extends Roo.util.Observable
56977  * This class represents the primary interface of a component based grid control.
56978  * <br><br>Usage:<pre><code>
56979  var grid = new Roo.grid.Grid("my-container-id", {
56980      ds: myDataStore,
56981      cm: myColModel,
56982      selModel: mySelectionModel,
56983      autoSizeColumns: true,
56984      monitorWindowResize: false,
56985      trackMouseOver: true
56986  });
56987  // set any options
56988  grid.render();
56989  * </code></pre>
56990  * <b>Common Problems:</b><br/>
56991  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
56992  * element will correct this<br/>
56993  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
56994  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
56995  * are unpredictable.<br/>
56996  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
56997  * grid to calculate dimensions/offsets.<br/>
56998   * @constructor
56999  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57000  * The container MUST have some type of size defined for the grid to fill. The container will be
57001  * automatically set to position relative if it isn't already.
57002  * @param {Object} config A config object that sets properties on this grid.
57003  */
57004 Roo.grid.Grid = function(container, config){
57005         // initialize the container
57006         this.container = Roo.get(container);
57007         this.container.update("");
57008         this.container.setStyle("overflow", "hidden");
57009     this.container.addClass('x-grid-container');
57010
57011     this.id = this.container.id;
57012
57013     Roo.apply(this, config);
57014     // check and correct shorthanded configs
57015     if(this.ds){
57016         this.dataSource = this.ds;
57017         delete this.ds;
57018     }
57019     if(this.cm){
57020         this.colModel = this.cm;
57021         delete this.cm;
57022     }
57023     if(this.sm){
57024         this.selModel = this.sm;
57025         delete this.sm;
57026     }
57027
57028     if (this.selModel) {
57029         this.selModel = Roo.factory(this.selModel, Roo.grid);
57030         this.sm = this.selModel;
57031         this.sm.xmodule = this.xmodule || false;
57032     }
57033     if (typeof(this.colModel.config) == 'undefined') {
57034         this.colModel = new Roo.grid.ColumnModel(this.colModel);
57035         this.cm = this.colModel;
57036         this.cm.xmodule = this.xmodule || false;
57037     }
57038     if (this.dataSource) {
57039         this.dataSource= Roo.factory(this.dataSource, Roo.data);
57040         this.ds = this.dataSource;
57041         this.ds.xmodule = this.xmodule || false;
57042          
57043     }
57044     
57045     
57046     
57047     if(this.width){
57048         this.container.setWidth(this.width);
57049     }
57050
57051     if(this.height){
57052         this.container.setHeight(this.height);
57053     }
57054     /** @private */
57055         this.addEvents({
57056         // raw events
57057         /**
57058          * @event click
57059          * The raw click event for the entire grid.
57060          * @param {Roo.EventObject} e
57061          */
57062         "click" : true,
57063         /**
57064          * @event dblclick
57065          * The raw dblclick event for the entire grid.
57066          * @param {Roo.EventObject} e
57067          */
57068         "dblclick" : true,
57069         /**
57070          * @event contextmenu
57071          * The raw contextmenu event for the entire grid.
57072          * @param {Roo.EventObject} e
57073          */
57074         "contextmenu" : true,
57075         /**
57076          * @event mousedown
57077          * The raw mousedown event for the entire grid.
57078          * @param {Roo.EventObject} e
57079          */
57080         "mousedown" : true,
57081         /**
57082          * @event mouseup
57083          * The raw mouseup event for the entire grid.
57084          * @param {Roo.EventObject} e
57085          */
57086         "mouseup" : true,
57087         /**
57088          * @event mouseover
57089          * The raw mouseover event for the entire grid.
57090          * @param {Roo.EventObject} e
57091          */
57092         "mouseover" : true,
57093         /**
57094          * @event mouseout
57095          * The raw mouseout event for the entire grid.
57096          * @param {Roo.EventObject} e
57097          */
57098         "mouseout" : true,
57099         /**
57100          * @event keypress
57101          * The raw keypress event for the entire grid.
57102          * @param {Roo.EventObject} e
57103          */
57104         "keypress" : true,
57105         /**
57106          * @event keydown
57107          * The raw keydown event for the entire grid.
57108          * @param {Roo.EventObject} e
57109          */
57110         "keydown" : true,
57111
57112         // custom events
57113
57114         /**
57115          * @event cellclick
57116          * Fires when a cell is clicked
57117          * @param {Grid} this
57118          * @param {Number} rowIndex
57119          * @param {Number} columnIndex
57120          * @param {Roo.EventObject} e
57121          */
57122         "cellclick" : true,
57123         /**
57124          * @event celldblclick
57125          * Fires when a cell is double clicked
57126          * @param {Grid} this
57127          * @param {Number} rowIndex
57128          * @param {Number} columnIndex
57129          * @param {Roo.EventObject} e
57130          */
57131         "celldblclick" : true,
57132         /**
57133          * @event rowclick
57134          * Fires when a row is clicked
57135          * @param {Grid} this
57136          * @param {Number} rowIndex
57137          * @param {Roo.EventObject} e
57138          */
57139         "rowclick" : true,
57140         /**
57141          * @event rowdblclick
57142          * Fires when a row is double clicked
57143          * @param {Grid} this
57144          * @param {Number} rowIndex
57145          * @param {Roo.EventObject} e
57146          */
57147         "rowdblclick" : true,
57148         /**
57149          * @event headerclick
57150          * Fires when a header is clicked
57151          * @param {Grid} this
57152          * @param {Number} columnIndex
57153          * @param {Roo.EventObject} e
57154          */
57155         "headerclick" : true,
57156         /**
57157          * @event headerdblclick
57158          * Fires when a header cell is double clicked
57159          * @param {Grid} this
57160          * @param {Number} columnIndex
57161          * @param {Roo.EventObject} e
57162          */
57163         "headerdblclick" : true,
57164         /**
57165          * @event rowcontextmenu
57166          * Fires when a row is right clicked
57167          * @param {Grid} this
57168          * @param {Number} rowIndex
57169          * @param {Roo.EventObject} e
57170          */
57171         "rowcontextmenu" : true,
57172         /**
57173          * @event cellcontextmenu
57174          * Fires when a cell is right clicked
57175          * @param {Grid} this
57176          * @param {Number} rowIndex
57177          * @param {Number} cellIndex
57178          * @param {Roo.EventObject} e
57179          */
57180          "cellcontextmenu" : true,
57181         /**
57182          * @event headercontextmenu
57183          * Fires when a header is right clicked
57184          * @param {Grid} this
57185          * @param {Number} columnIndex
57186          * @param {Roo.EventObject} e
57187          */
57188         "headercontextmenu" : true,
57189         /**
57190          * @event bodyscroll
57191          * Fires when the body element is scrolled
57192          * @param {Number} scrollLeft
57193          * @param {Number} scrollTop
57194          */
57195         "bodyscroll" : true,
57196         /**
57197          * @event columnresize
57198          * Fires when the user resizes a column
57199          * @param {Number} columnIndex
57200          * @param {Number} newSize
57201          */
57202         "columnresize" : true,
57203         /**
57204          * @event columnmove
57205          * Fires when the user moves a column
57206          * @param {Number} oldIndex
57207          * @param {Number} newIndex
57208          */
57209         "columnmove" : true,
57210         /**
57211          * @event startdrag
57212          * Fires when row(s) start being dragged
57213          * @param {Grid} this
57214          * @param {Roo.GridDD} dd The drag drop object
57215          * @param {event} e The raw browser event
57216          */
57217         "startdrag" : true,
57218         /**
57219          * @event enddrag
57220          * Fires when a drag operation is complete
57221          * @param {Grid} this
57222          * @param {Roo.GridDD} dd The drag drop object
57223          * @param {event} e The raw browser event
57224          */
57225         "enddrag" : true,
57226         /**
57227          * @event dragdrop
57228          * Fires when dragged row(s) are dropped on a valid DD target
57229          * @param {Grid} this
57230          * @param {Roo.GridDD} dd The drag drop object
57231          * @param {String} targetId The target drag drop object
57232          * @param {event} e The raw browser event
57233          */
57234         "dragdrop" : true,
57235         /**
57236          * @event dragover
57237          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57238          * @param {Grid} this
57239          * @param {Roo.GridDD} dd The drag drop object
57240          * @param {String} targetId The target drag drop object
57241          * @param {event} e The raw browser event
57242          */
57243         "dragover" : true,
57244         /**
57245          * @event dragenter
57246          *  Fires when the dragged row(s) first cross another DD target while being dragged
57247          * @param {Grid} this
57248          * @param {Roo.GridDD} dd The drag drop object
57249          * @param {String} targetId The target drag drop object
57250          * @param {event} e The raw browser event
57251          */
57252         "dragenter" : true,
57253         /**
57254          * @event dragout
57255          * Fires when the dragged row(s) leave another DD target while being dragged
57256          * @param {Grid} this
57257          * @param {Roo.GridDD} dd The drag drop object
57258          * @param {String} targetId The target drag drop object
57259          * @param {event} e The raw browser event
57260          */
57261         "dragout" : true,
57262         /**
57263          * @event rowclass
57264          * Fires when a row is rendered, so you can change add a style to it.
57265          * @param {GridView} gridview   The grid view
57266          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57267          */
57268         'rowclass' : true,
57269
57270         /**
57271          * @event render
57272          * Fires when the grid is rendered
57273          * @param {Grid} grid
57274          */
57275         'render' : true
57276     });
57277
57278     Roo.grid.Grid.superclass.constructor.call(this);
57279 };
57280 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
57281     
57282     /**
57283          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
57284          */
57285         /**
57286          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
57287          */
57288         /**
57289          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
57290          */
57291         /**
57292          * @cfg {Roo.grid.Store} ds The data store for the grid
57293          */
57294         /**
57295          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
57296          */
57297         /**
57298      * @cfg {String} ddGroup - drag drop group.
57299      */
57300       /**
57301      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
57302      */
57303
57304     /**
57305      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
57306      */
57307     minColumnWidth : 25,
57308
57309     /**
57310      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
57311      * <b>on initial render.</b> It is more efficient to explicitly size the columns
57312      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
57313      */
57314     autoSizeColumns : false,
57315
57316     /**
57317      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
57318      */
57319     autoSizeHeaders : true,
57320
57321     /**
57322      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
57323      */
57324     monitorWindowResize : true,
57325
57326     /**
57327      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
57328      * rows measured to get a columns size. Default is 0 (all rows).
57329      */
57330     maxRowsToMeasure : 0,
57331
57332     /**
57333      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
57334      */
57335     trackMouseOver : true,
57336
57337     /**
57338     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
57339     */
57340       /**
57341     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
57342     */
57343     
57344     /**
57345     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
57346     */
57347     enableDragDrop : false,
57348     
57349     /**
57350     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
57351     */
57352     enableColumnMove : true,
57353     
57354     /**
57355     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
57356     */
57357     enableColumnHide : true,
57358     
57359     /**
57360     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
57361     */
57362     enableRowHeightSync : false,
57363     
57364     /**
57365     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
57366     */
57367     stripeRows : true,
57368     
57369     /**
57370     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
57371     */
57372     autoHeight : false,
57373
57374     /**
57375      * @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.
57376      */
57377     autoExpandColumn : false,
57378
57379     /**
57380     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
57381     * Default is 50.
57382     */
57383     autoExpandMin : 50,
57384
57385     /**
57386     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
57387     */
57388     autoExpandMax : 1000,
57389
57390     /**
57391     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
57392     */
57393     view : null,
57394
57395     /**
57396     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
57397     */
57398     loadMask : false,
57399     /**
57400     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
57401     */
57402     dropTarget: false,
57403     
57404    
57405     
57406     // private
57407     rendered : false,
57408
57409     /**
57410     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
57411     * of a fixed width. Default is false.
57412     */
57413     /**
57414     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
57415     */
57416     
57417     
57418     /**
57419     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
57420     * %0 is replaced with the number of selected rows.
57421     */
57422     ddText : "{0} selected row{1}",
57423     
57424     
57425     /**
57426      * Called once after all setup has been completed and the grid is ready to be rendered.
57427      * @return {Roo.grid.Grid} this
57428      */
57429     render : function()
57430     {
57431         var c = this.container;
57432         // try to detect autoHeight/width mode
57433         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
57434             this.autoHeight = true;
57435         }
57436         var view = this.getView();
57437         view.init(this);
57438
57439         c.on("click", this.onClick, this);
57440         c.on("dblclick", this.onDblClick, this);
57441         c.on("contextmenu", this.onContextMenu, this);
57442         c.on("keydown", this.onKeyDown, this);
57443         if (Roo.isTouch) {
57444             c.on("touchstart", this.onTouchStart, this);
57445         }
57446
57447         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
57448
57449         this.getSelectionModel().init(this);
57450
57451         view.render();
57452
57453         if(this.loadMask){
57454             this.loadMask = new Roo.LoadMask(this.container,
57455                     Roo.apply({store:this.dataSource}, this.loadMask));
57456         }
57457         
57458         
57459         if (this.toolbar && this.toolbar.xtype) {
57460             this.toolbar.container = this.getView().getHeaderPanel(true);
57461             this.toolbar = new Roo.Toolbar(this.toolbar);
57462         }
57463         if (this.footer && this.footer.xtype) {
57464             this.footer.dataSource = this.getDataSource();
57465             this.footer.container = this.getView().getFooterPanel(true);
57466             this.footer = Roo.factory(this.footer, Roo);
57467         }
57468         if (this.dropTarget && this.dropTarget.xtype) {
57469             delete this.dropTarget.xtype;
57470             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
57471         }
57472         
57473         
57474         this.rendered = true;
57475         this.fireEvent('render', this);
57476         return this;
57477     },
57478
57479     /**
57480      * Reconfigures the grid to use a different Store and Column Model.
57481      * The View will be bound to the new objects and refreshed.
57482      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
57483      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
57484      */
57485     reconfigure : function(dataSource, colModel){
57486         if(this.loadMask){
57487             this.loadMask.destroy();
57488             this.loadMask = new Roo.LoadMask(this.container,
57489                     Roo.apply({store:dataSource}, this.loadMask));
57490         }
57491         this.view.bind(dataSource, colModel);
57492         this.dataSource = dataSource;
57493         this.colModel = colModel;
57494         this.view.refresh(true);
57495     },
57496     /**
57497      * addColumns
57498      * Add's a column, default at the end..
57499      
57500      * @param {int} position to add (default end)
57501      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
57502      */
57503     addColumns : function(pos, ar)
57504     {
57505         
57506         for (var i =0;i< ar.length;i++) {
57507             var cfg = ar[i];
57508             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
57509             this.cm.lookup[cfg.id] = cfg;
57510         }
57511         
57512         
57513         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
57514             pos = this.cm.config.length; //this.cm.config.push(cfg);
57515         } 
57516         pos = Math.max(0,pos);
57517         ar.unshift(0);
57518         ar.unshift(pos);
57519         this.cm.config.splice.apply(this.cm.config, ar);
57520         
57521         
57522         
57523         this.view.generateRules(this.cm);
57524         this.view.refresh(true);
57525         
57526     },
57527     
57528     
57529     
57530     
57531     // private
57532     onKeyDown : function(e){
57533         this.fireEvent("keydown", e);
57534     },
57535
57536     /**
57537      * Destroy this grid.
57538      * @param {Boolean} removeEl True to remove the element
57539      */
57540     destroy : function(removeEl, keepListeners){
57541         if(this.loadMask){
57542             this.loadMask.destroy();
57543         }
57544         var c = this.container;
57545         c.removeAllListeners();
57546         this.view.destroy();
57547         this.colModel.purgeListeners();
57548         if(!keepListeners){
57549             this.purgeListeners();
57550         }
57551         c.update("");
57552         if(removeEl === true){
57553             c.remove();
57554         }
57555     },
57556
57557     // private
57558     processEvent : function(name, e){
57559         // does this fire select???
57560         //Roo.log('grid:processEvent '  + name);
57561         
57562         if (name != 'touchstart' ) {
57563             this.fireEvent(name, e);    
57564         }
57565         
57566         var t = e.getTarget();
57567         var v = this.view;
57568         var header = v.findHeaderIndex(t);
57569         if(header !== false){
57570             var ename = name == 'touchstart' ? 'click' : name;
57571              
57572             this.fireEvent("header" + ename, this, header, e);
57573         }else{
57574             var row = v.findRowIndex(t);
57575             var cell = v.findCellIndex(t);
57576             if (name == 'touchstart') {
57577                 // first touch is always a click.
57578                 // hopefull this happens after selection is updated.?
57579                 name = false;
57580                 
57581                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
57582                     var cs = this.selModel.getSelectedCell();
57583                     if (row == cs[0] && cell == cs[1]){
57584                         name = 'dblclick';
57585                     }
57586                 }
57587                 if (typeof(this.selModel.getSelections) != 'undefined') {
57588                     var cs = this.selModel.getSelections();
57589                     var ds = this.dataSource;
57590                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
57591                         name = 'dblclick';
57592                     }
57593                 }
57594                 if (!name) {
57595                     return;
57596                 }
57597             }
57598             
57599             
57600             if(row !== false){
57601                 this.fireEvent("row" + name, this, row, e);
57602                 if(cell !== false){
57603                     this.fireEvent("cell" + name, this, row, cell, e);
57604                 }
57605             }
57606         }
57607     },
57608
57609     // private
57610     onClick : function(e){
57611         this.processEvent("click", e);
57612     },
57613    // private
57614     onTouchStart : function(e){
57615         this.processEvent("touchstart", e);
57616     },
57617
57618     // private
57619     onContextMenu : function(e, t){
57620         this.processEvent("contextmenu", e);
57621     },
57622
57623     // private
57624     onDblClick : function(e){
57625         this.processEvent("dblclick", e);
57626     },
57627
57628     // private
57629     walkCells : function(row, col, step, fn, scope){
57630         var cm = this.colModel, clen = cm.getColumnCount();
57631         var ds = this.dataSource, rlen = ds.getCount(), first = true;
57632         if(step < 0){
57633             if(col < 0){
57634                 row--;
57635                 first = false;
57636             }
57637             while(row >= 0){
57638                 if(!first){
57639                     col = clen-1;
57640                 }
57641                 first = false;
57642                 while(col >= 0){
57643                     if(fn.call(scope || this, row, col, cm) === true){
57644                         return [row, col];
57645                     }
57646                     col--;
57647                 }
57648                 row--;
57649             }
57650         } else {
57651             if(col >= clen){
57652                 row++;
57653                 first = false;
57654             }
57655             while(row < rlen){
57656                 if(!first){
57657                     col = 0;
57658                 }
57659                 first = false;
57660                 while(col < clen){
57661                     if(fn.call(scope || this, row, col, cm) === true){
57662                         return [row, col];
57663                     }
57664                     col++;
57665                 }
57666                 row++;
57667             }
57668         }
57669         return null;
57670     },
57671
57672     // private
57673     getSelections : function(){
57674         return this.selModel.getSelections();
57675     },
57676
57677     /**
57678      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
57679      * but if manual update is required this method will initiate it.
57680      */
57681     autoSize : function(){
57682         if(this.rendered){
57683             this.view.layout();
57684             if(this.view.adjustForScroll){
57685                 this.view.adjustForScroll();
57686             }
57687         }
57688     },
57689
57690     /**
57691      * Returns the grid's underlying element.
57692      * @return {Element} The element
57693      */
57694     getGridEl : function(){
57695         return this.container;
57696     },
57697
57698     // private for compatibility, overridden by editor grid
57699     stopEditing : function(){},
57700
57701     /**
57702      * Returns the grid's SelectionModel.
57703      * @return {SelectionModel}
57704      */
57705     getSelectionModel : function(){
57706         if(!this.selModel){
57707             this.selModel = new Roo.grid.RowSelectionModel();
57708         }
57709         return this.selModel;
57710     },
57711
57712     /**
57713      * Returns the grid's DataSource.
57714      * @return {DataSource}
57715      */
57716     getDataSource : function(){
57717         return this.dataSource;
57718     },
57719
57720     /**
57721      * Returns the grid's ColumnModel.
57722      * @return {ColumnModel}
57723      */
57724     getColumnModel : function(){
57725         return this.colModel;
57726     },
57727
57728     /**
57729      * Returns the grid's GridView object.
57730      * @return {GridView}
57731      */
57732     getView : function(){
57733         if(!this.view){
57734             this.view = new Roo.grid.GridView(this.viewConfig);
57735             this.relayEvents(this.view, [
57736                 "beforerowremoved", "beforerowsinserted",
57737                 "beforerefresh", "rowremoved",
57738                 "rowsinserted", "rowupdated" ,"refresh"
57739             ]);
57740         }
57741         return this.view;
57742     },
57743     /**
57744      * Called to get grid's drag proxy text, by default returns this.ddText.
57745      * Override this to put something different in the dragged text.
57746      * @return {String}
57747      */
57748     getDragDropText : function(){
57749         var count = this.selModel.getCount();
57750         return String.format(this.ddText, count, count == 1 ? '' : 's');
57751     }
57752 });
57753 /*
57754  * Based on:
57755  * Ext JS Library 1.1.1
57756  * Copyright(c) 2006-2007, Ext JS, LLC.
57757  *
57758  * Originally Released Under LGPL - original licence link has changed is not relivant.
57759  *
57760  * Fork - LGPL
57761  * <script type="text/javascript">
57762  */
57763  /**
57764  * @class Roo.grid.AbstractGridView
57765  * @extends Roo.util.Observable
57766  * @abstract
57767  * Abstract base class for grid Views
57768  * @constructor
57769  */
57770 Roo.grid.AbstractGridView = function(){
57771         this.grid = null;
57772         
57773         this.events = {
57774             "beforerowremoved" : true,
57775             "beforerowsinserted" : true,
57776             "beforerefresh" : true,
57777             "rowremoved" : true,
57778             "rowsinserted" : true,
57779             "rowupdated" : true,
57780             "refresh" : true
57781         };
57782     Roo.grid.AbstractGridView.superclass.constructor.call(this);
57783 };
57784
57785 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
57786     rowClass : "x-grid-row",
57787     cellClass : "x-grid-cell",
57788     tdClass : "x-grid-td",
57789     hdClass : "x-grid-hd",
57790     splitClass : "x-grid-hd-split",
57791     
57792     init: function(grid){
57793         this.grid = grid;
57794                 var cid = this.grid.getGridEl().id;
57795         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
57796         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
57797         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
57798         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
57799         },
57800         
57801     getColumnRenderers : function(){
57802         var renderers = [];
57803         var cm = this.grid.colModel;
57804         var colCount = cm.getColumnCount();
57805         for(var i = 0; i < colCount; i++){
57806             renderers[i] = cm.getRenderer(i);
57807         }
57808         return renderers;
57809     },
57810     
57811     getColumnIds : function(){
57812         var ids = [];
57813         var cm = this.grid.colModel;
57814         var colCount = cm.getColumnCount();
57815         for(var i = 0; i < colCount; i++){
57816             ids[i] = cm.getColumnId(i);
57817         }
57818         return ids;
57819     },
57820     
57821     getDataIndexes : function(){
57822         if(!this.indexMap){
57823             this.indexMap = this.buildIndexMap();
57824         }
57825         return this.indexMap.colToData;
57826     },
57827     
57828     getColumnIndexByDataIndex : function(dataIndex){
57829         if(!this.indexMap){
57830             this.indexMap = this.buildIndexMap();
57831         }
57832         return this.indexMap.dataToCol[dataIndex];
57833     },
57834     
57835     /**
57836      * Set a css style for a column dynamically. 
57837      * @param {Number} colIndex The index of the column
57838      * @param {String} name The css property name
57839      * @param {String} value The css value
57840      */
57841     setCSSStyle : function(colIndex, name, value){
57842         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
57843         Roo.util.CSS.updateRule(selector, name, value);
57844     },
57845     
57846     generateRules : function(cm){
57847         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
57848         Roo.util.CSS.removeStyleSheet(rulesId);
57849         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
57850             var cid = cm.getColumnId(i);
57851             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
57852                          this.tdSelector, cid, " {\n}\n",
57853                          this.hdSelector, cid, " {\n}\n",
57854                          this.splitSelector, cid, " {\n}\n");
57855         }
57856         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
57857     }
57858 });/*
57859  * Based on:
57860  * Ext JS Library 1.1.1
57861  * Copyright(c) 2006-2007, Ext JS, LLC.
57862  *
57863  * Originally Released Under LGPL - original licence link has changed is not relivant.
57864  *
57865  * Fork - LGPL
57866  * <script type="text/javascript">
57867  */
57868
57869 // private
57870 // This is a support class used internally by the Grid components
57871 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
57872     this.grid = grid;
57873     this.view = grid.getView();
57874     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57875     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
57876     if(hd2){
57877         this.setHandleElId(Roo.id(hd));
57878         this.setOuterHandleElId(Roo.id(hd2));
57879     }
57880     this.scroll = false;
57881 };
57882 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
57883     maxDragWidth: 120,
57884     getDragData : function(e){
57885         var t = Roo.lib.Event.getTarget(e);
57886         var h = this.view.findHeaderCell(t);
57887         if(h){
57888             return {ddel: h.firstChild, header:h};
57889         }
57890         return false;
57891     },
57892
57893     onInitDrag : function(e){
57894         this.view.headersDisabled = true;
57895         var clone = this.dragData.ddel.cloneNode(true);
57896         clone.id = Roo.id();
57897         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
57898         this.proxy.update(clone);
57899         return true;
57900     },
57901
57902     afterValidDrop : function(){
57903         var v = this.view;
57904         setTimeout(function(){
57905             v.headersDisabled = false;
57906         }, 50);
57907     },
57908
57909     afterInvalidDrop : function(){
57910         var v = this.view;
57911         setTimeout(function(){
57912             v.headersDisabled = false;
57913         }, 50);
57914     }
57915 });
57916 /*
57917  * Based on:
57918  * Ext JS Library 1.1.1
57919  * Copyright(c) 2006-2007, Ext JS, LLC.
57920  *
57921  * Originally Released Under LGPL - original licence link has changed is not relivant.
57922  *
57923  * Fork - LGPL
57924  * <script type="text/javascript">
57925  */
57926 // private
57927 // This is a support class used internally by the Grid components
57928 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
57929     this.grid = grid;
57930     this.view = grid.getView();
57931     // split the proxies so they don't interfere with mouse events
57932     this.proxyTop = Roo.DomHelper.append(document.body, {
57933         cls:"col-move-top", html:"&#160;"
57934     }, true);
57935     this.proxyBottom = Roo.DomHelper.append(document.body, {
57936         cls:"col-move-bottom", html:"&#160;"
57937     }, true);
57938     this.proxyTop.hide = this.proxyBottom.hide = function(){
57939         this.setLeftTop(-100,-100);
57940         this.setStyle("visibility", "hidden");
57941     };
57942     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
57943     // temporarily disabled
57944     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
57945     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
57946 };
57947 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
57948     proxyOffsets : [-4, -9],
57949     fly: Roo.Element.fly,
57950
57951     getTargetFromEvent : function(e){
57952         var t = Roo.lib.Event.getTarget(e);
57953         var cindex = this.view.findCellIndex(t);
57954         if(cindex !== false){
57955             return this.view.getHeaderCell(cindex);
57956         }
57957         return null;
57958     },
57959
57960     nextVisible : function(h){
57961         var v = this.view, cm = this.grid.colModel;
57962         h = h.nextSibling;
57963         while(h){
57964             if(!cm.isHidden(v.getCellIndex(h))){
57965                 return h;
57966             }
57967             h = h.nextSibling;
57968         }
57969         return null;
57970     },
57971
57972     prevVisible : function(h){
57973         var v = this.view, cm = this.grid.colModel;
57974         h = h.prevSibling;
57975         while(h){
57976             if(!cm.isHidden(v.getCellIndex(h))){
57977                 return h;
57978             }
57979             h = h.prevSibling;
57980         }
57981         return null;
57982     },
57983
57984     positionIndicator : function(h, n, e){
57985         var x = Roo.lib.Event.getPageX(e);
57986         var r = Roo.lib.Dom.getRegion(n.firstChild);
57987         var px, pt, py = r.top + this.proxyOffsets[1];
57988         if((r.right - x) <= (r.right-r.left)/2){
57989             px = r.right+this.view.borderWidth;
57990             pt = "after";
57991         }else{
57992             px = r.left;
57993             pt = "before";
57994         }
57995         var oldIndex = this.view.getCellIndex(h);
57996         var newIndex = this.view.getCellIndex(n);
57997
57998         if(this.grid.colModel.isFixed(newIndex)){
57999             return false;
58000         }
58001
58002         var locked = this.grid.colModel.isLocked(newIndex);
58003
58004         if(pt == "after"){
58005             newIndex++;
58006         }
58007         if(oldIndex < newIndex){
58008             newIndex--;
58009         }
58010         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58011             return false;
58012         }
58013         px +=  this.proxyOffsets[0];
58014         this.proxyTop.setLeftTop(px, py);
58015         this.proxyTop.show();
58016         if(!this.bottomOffset){
58017             this.bottomOffset = this.view.mainHd.getHeight();
58018         }
58019         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
58020         this.proxyBottom.show();
58021         return pt;
58022     },
58023
58024     onNodeEnter : function(n, dd, e, data){
58025         if(data.header != n){
58026             this.positionIndicator(data.header, n, e);
58027         }
58028     },
58029
58030     onNodeOver : function(n, dd, e, data){
58031         var result = false;
58032         if(data.header != n){
58033             result = this.positionIndicator(data.header, n, e);
58034         }
58035         if(!result){
58036             this.proxyTop.hide();
58037             this.proxyBottom.hide();
58038         }
58039         return result ? this.dropAllowed : this.dropNotAllowed;
58040     },
58041
58042     onNodeOut : function(n, dd, e, data){
58043         this.proxyTop.hide();
58044         this.proxyBottom.hide();
58045     },
58046
58047     onNodeDrop : function(n, dd, e, data){
58048         var h = data.header;
58049         if(h != n){
58050             var cm = this.grid.colModel;
58051             var x = Roo.lib.Event.getPageX(e);
58052             var r = Roo.lib.Dom.getRegion(n.firstChild);
58053             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
58054             var oldIndex = this.view.getCellIndex(h);
58055             var newIndex = this.view.getCellIndex(n);
58056             var locked = cm.isLocked(newIndex);
58057             if(pt == "after"){
58058                 newIndex++;
58059             }
58060             if(oldIndex < newIndex){
58061                 newIndex--;
58062             }
58063             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
58064                 return false;
58065             }
58066             cm.setLocked(oldIndex, locked, true);
58067             cm.moveColumn(oldIndex, newIndex);
58068             this.grid.fireEvent("columnmove", oldIndex, newIndex);
58069             return true;
58070         }
58071         return false;
58072     }
58073 });
58074 /*
58075  * Based on:
58076  * Ext JS Library 1.1.1
58077  * Copyright(c) 2006-2007, Ext JS, LLC.
58078  *
58079  * Originally Released Under LGPL - original licence link has changed is not relivant.
58080  *
58081  * Fork - LGPL
58082  * <script type="text/javascript">
58083  */
58084   
58085 /**
58086  * @class Roo.grid.GridView
58087  * @extends Roo.util.Observable
58088  *
58089  * @constructor
58090  * @param {Object} config
58091  */
58092 Roo.grid.GridView = function(config){
58093     Roo.grid.GridView.superclass.constructor.call(this);
58094     this.el = null;
58095
58096     Roo.apply(this, config);
58097 };
58098
58099 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
58100
58101     unselectable :  'unselectable="on"',
58102     unselectableCls :  'x-unselectable',
58103     
58104     
58105     rowClass : "x-grid-row",
58106
58107     cellClass : "x-grid-col",
58108
58109     tdClass : "x-grid-td",
58110
58111     hdClass : "x-grid-hd",
58112
58113     splitClass : "x-grid-split",
58114
58115     sortClasses : ["sort-asc", "sort-desc"],
58116
58117     enableMoveAnim : false,
58118
58119     hlColor: "C3DAF9",
58120
58121     dh : Roo.DomHelper,
58122
58123     fly : Roo.Element.fly,
58124
58125     css : Roo.util.CSS,
58126
58127     borderWidth: 1,
58128
58129     splitOffset: 3,
58130
58131     scrollIncrement : 22,
58132
58133     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
58134
58135     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
58136
58137     bind : function(ds, cm){
58138         if(this.ds){
58139             this.ds.un("load", this.onLoad, this);
58140             this.ds.un("datachanged", this.onDataChange, this);
58141             this.ds.un("add", this.onAdd, this);
58142             this.ds.un("remove", this.onRemove, this);
58143             this.ds.un("update", this.onUpdate, this);
58144             this.ds.un("clear", this.onClear, this);
58145         }
58146         if(ds){
58147             ds.on("load", this.onLoad, this);
58148             ds.on("datachanged", this.onDataChange, this);
58149             ds.on("add", this.onAdd, this);
58150             ds.on("remove", this.onRemove, this);
58151             ds.on("update", this.onUpdate, this);
58152             ds.on("clear", this.onClear, this);
58153         }
58154         this.ds = ds;
58155
58156         if(this.cm){
58157             this.cm.un("widthchange", this.onColWidthChange, this);
58158             this.cm.un("headerchange", this.onHeaderChange, this);
58159             this.cm.un("hiddenchange", this.onHiddenChange, this);
58160             this.cm.un("columnmoved", this.onColumnMove, this);
58161             this.cm.un("columnlockchange", this.onColumnLock, this);
58162         }
58163         if(cm){
58164             this.generateRules(cm);
58165             cm.on("widthchange", this.onColWidthChange, this);
58166             cm.on("headerchange", this.onHeaderChange, this);
58167             cm.on("hiddenchange", this.onHiddenChange, this);
58168             cm.on("columnmoved", this.onColumnMove, this);
58169             cm.on("columnlockchange", this.onColumnLock, this);
58170         }
58171         this.cm = cm;
58172     },
58173
58174     init: function(grid){
58175         Roo.grid.GridView.superclass.init.call(this, grid);
58176
58177         this.bind(grid.dataSource, grid.colModel);
58178
58179         grid.on("headerclick", this.handleHeaderClick, this);
58180
58181         if(grid.trackMouseOver){
58182             grid.on("mouseover", this.onRowOver, this);
58183             grid.on("mouseout", this.onRowOut, this);
58184         }
58185         grid.cancelTextSelection = function(){};
58186         this.gridId = grid.id;
58187
58188         var tpls = this.templates || {};
58189
58190         if(!tpls.master){
58191             tpls.master = new Roo.Template(
58192                '<div class="x-grid" hidefocus="true">',
58193                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
58194                   '<div class="x-grid-topbar"></div>',
58195                   '<div class="x-grid-scroller"><div></div></div>',
58196                   '<div class="x-grid-locked">',
58197                       '<div class="x-grid-header">{lockedHeader}</div>',
58198                       '<div class="x-grid-body">{lockedBody}</div>',
58199                   "</div>",
58200                   '<div class="x-grid-viewport">',
58201                       '<div class="x-grid-header">{header}</div>',
58202                       '<div class="x-grid-body">{body}</div>',
58203                   "</div>",
58204                   '<div class="x-grid-bottombar"></div>',
58205                  
58206                   '<div class="x-grid-resize-proxy">&#160;</div>',
58207                "</div>"
58208             );
58209             tpls.master.disableformats = true;
58210         }
58211
58212         if(!tpls.header){
58213             tpls.header = new Roo.Template(
58214                '<table border="0" cellspacing="0" cellpadding="0">',
58215                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
58216                "</table>{splits}"
58217             );
58218             tpls.header.disableformats = true;
58219         }
58220         tpls.header.compile();
58221
58222         if(!tpls.hcell){
58223             tpls.hcell = new Roo.Template(
58224                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
58225                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
58226                 "</div></td>"
58227              );
58228              tpls.hcell.disableFormats = true;
58229         }
58230         tpls.hcell.compile();
58231
58232         if(!tpls.hsplit){
58233             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
58234                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
58235             tpls.hsplit.disableFormats = true;
58236         }
58237         tpls.hsplit.compile();
58238
58239         if(!tpls.body){
58240             tpls.body = new Roo.Template(
58241                '<table border="0" cellspacing="0" cellpadding="0">',
58242                "<tbody>{rows}</tbody>",
58243                "</table>"
58244             );
58245             tpls.body.disableFormats = true;
58246         }
58247         tpls.body.compile();
58248
58249         if(!tpls.row){
58250             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
58251             tpls.row.disableFormats = true;
58252         }
58253         tpls.row.compile();
58254
58255         if(!tpls.cell){
58256             tpls.cell = new Roo.Template(
58257                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
58258                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
58259                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
58260                 "</td>"
58261             );
58262             tpls.cell.disableFormats = true;
58263         }
58264         tpls.cell.compile();
58265
58266         this.templates = tpls;
58267     },
58268
58269     // remap these for backwards compat
58270     onColWidthChange : function(){
58271         this.updateColumns.apply(this, arguments);
58272     },
58273     onHeaderChange : function(){
58274         this.updateHeaders.apply(this, arguments);
58275     }, 
58276     onHiddenChange : function(){
58277         this.handleHiddenChange.apply(this, arguments);
58278     },
58279     onColumnMove : function(){
58280         this.handleColumnMove.apply(this, arguments);
58281     },
58282     onColumnLock : function(){
58283         this.handleLockChange.apply(this, arguments);
58284     },
58285
58286     onDataChange : function(){
58287         this.refresh();
58288         this.updateHeaderSortState();
58289     },
58290
58291     onClear : function(){
58292         this.refresh();
58293     },
58294
58295     onUpdate : function(ds, record){
58296         this.refreshRow(record);
58297     },
58298
58299     refreshRow : function(record){
58300         var ds = this.ds, index;
58301         if(typeof record == 'number'){
58302             index = record;
58303             record = ds.getAt(index);
58304         }else{
58305             index = ds.indexOf(record);
58306         }
58307         this.insertRows(ds, index, index, true);
58308         this.onRemove(ds, record, index+1, true);
58309         this.syncRowHeights(index, index);
58310         this.layout();
58311         this.fireEvent("rowupdated", this, index, record);
58312     },
58313
58314     onAdd : function(ds, records, index){
58315         this.insertRows(ds, index, index + (records.length-1));
58316     },
58317
58318     onRemove : function(ds, record, index, isUpdate){
58319         if(isUpdate !== true){
58320             this.fireEvent("beforerowremoved", this, index, record);
58321         }
58322         var bt = this.getBodyTable(), lt = this.getLockedTable();
58323         if(bt.rows[index]){
58324             bt.firstChild.removeChild(bt.rows[index]);
58325         }
58326         if(lt.rows[index]){
58327             lt.firstChild.removeChild(lt.rows[index]);
58328         }
58329         if(isUpdate !== true){
58330             this.stripeRows(index);
58331             this.syncRowHeights(index, index);
58332             this.layout();
58333             this.fireEvent("rowremoved", this, index, record);
58334         }
58335     },
58336
58337     onLoad : function(){
58338         this.scrollToTop();
58339     },
58340
58341     /**
58342      * Scrolls the grid to the top
58343      */
58344     scrollToTop : function(){
58345         if(this.scroller){
58346             this.scroller.dom.scrollTop = 0;
58347             this.syncScroll();
58348         }
58349     },
58350
58351     /**
58352      * Gets a panel in the header of the grid that can be used for toolbars etc.
58353      * After modifying the contents of this panel a call to grid.autoSize() may be
58354      * required to register any changes in size.
58355      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
58356      * @return Roo.Element
58357      */
58358     getHeaderPanel : function(doShow){
58359         if(doShow){
58360             this.headerPanel.show();
58361         }
58362         return this.headerPanel;
58363     },
58364
58365     /**
58366      * Gets a panel in the footer of the grid that can be used for toolbars etc.
58367      * After modifying the contents of this panel a call to grid.autoSize() may be
58368      * required to register any changes in size.
58369      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
58370      * @return Roo.Element
58371      */
58372     getFooterPanel : function(doShow){
58373         if(doShow){
58374             this.footerPanel.show();
58375         }
58376         return this.footerPanel;
58377     },
58378
58379     initElements : function(){
58380         var E = Roo.Element;
58381         var el = this.grid.getGridEl().dom.firstChild;
58382         var cs = el.childNodes;
58383
58384         this.el = new E(el);
58385         
58386          this.focusEl = new E(el.firstChild);
58387         this.focusEl.swallowEvent("click", true);
58388         
58389         this.headerPanel = new E(cs[1]);
58390         this.headerPanel.enableDisplayMode("block");
58391
58392         this.scroller = new E(cs[2]);
58393         this.scrollSizer = new E(this.scroller.dom.firstChild);
58394
58395         this.lockedWrap = new E(cs[3]);
58396         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
58397         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
58398
58399         this.mainWrap = new E(cs[4]);
58400         this.mainHd = new E(this.mainWrap.dom.firstChild);
58401         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
58402
58403         this.footerPanel = new E(cs[5]);
58404         this.footerPanel.enableDisplayMode("block");
58405
58406         this.resizeProxy = new E(cs[6]);
58407
58408         this.headerSelector = String.format(
58409            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
58410            this.lockedHd.id, this.mainHd.id
58411         );
58412
58413         this.splitterSelector = String.format(
58414            '#{0} div.x-grid-split, #{1} div.x-grid-split',
58415            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
58416         );
58417     },
58418     idToCssName : function(s)
58419     {
58420         return s.replace(/[^a-z0-9]+/ig, '-');
58421     },
58422
58423     getHeaderCell : function(index){
58424         return Roo.DomQuery.select(this.headerSelector)[index];
58425     },
58426
58427     getHeaderCellMeasure : function(index){
58428         return this.getHeaderCell(index).firstChild;
58429     },
58430
58431     getHeaderCellText : function(index){
58432         return this.getHeaderCell(index).firstChild.firstChild;
58433     },
58434
58435     getLockedTable : function(){
58436         return this.lockedBody.dom.firstChild;
58437     },
58438
58439     getBodyTable : function(){
58440         return this.mainBody.dom.firstChild;
58441     },
58442
58443     getLockedRow : function(index){
58444         return this.getLockedTable().rows[index];
58445     },
58446
58447     getRow : function(index){
58448         return this.getBodyTable().rows[index];
58449     },
58450
58451     getRowComposite : function(index){
58452         if(!this.rowEl){
58453             this.rowEl = new Roo.CompositeElementLite();
58454         }
58455         var els = [], lrow, mrow;
58456         if(lrow = this.getLockedRow(index)){
58457             els.push(lrow);
58458         }
58459         if(mrow = this.getRow(index)){
58460             els.push(mrow);
58461         }
58462         this.rowEl.elements = els;
58463         return this.rowEl;
58464     },
58465     /**
58466      * Gets the 'td' of the cell
58467      * 
58468      * @param {Integer} rowIndex row to select
58469      * @param {Integer} colIndex column to select
58470      * 
58471      * @return {Object} 
58472      */
58473     getCell : function(rowIndex, colIndex){
58474         var locked = this.cm.getLockedCount();
58475         var source;
58476         if(colIndex < locked){
58477             source = this.lockedBody.dom.firstChild;
58478         }else{
58479             source = this.mainBody.dom.firstChild;
58480             colIndex -= locked;
58481         }
58482         return source.rows[rowIndex].childNodes[colIndex];
58483     },
58484
58485     getCellText : function(rowIndex, colIndex){
58486         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
58487     },
58488
58489     getCellBox : function(cell){
58490         var b = this.fly(cell).getBox();
58491         if(Roo.isOpera){ // opera fails to report the Y
58492             b.y = cell.offsetTop + this.mainBody.getY();
58493         }
58494         return b;
58495     },
58496
58497     getCellIndex : function(cell){
58498         var id = String(cell.className).match(this.cellRE);
58499         if(id){
58500             return parseInt(id[1], 10);
58501         }
58502         return 0;
58503     },
58504
58505     findHeaderIndex : function(n){
58506         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
58507         return r ? this.getCellIndex(r) : false;
58508     },
58509
58510     findHeaderCell : function(n){
58511         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
58512         return r ? r : false;
58513     },
58514
58515     findRowIndex : function(n){
58516         if(!n){
58517             return false;
58518         }
58519         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
58520         return r ? r.rowIndex : false;
58521     },
58522
58523     findCellIndex : function(node){
58524         var stop = this.el.dom;
58525         while(node && node != stop){
58526             if(this.findRE.test(node.className)){
58527                 return this.getCellIndex(node);
58528             }
58529             node = node.parentNode;
58530         }
58531         return false;
58532     },
58533
58534     getColumnId : function(index){
58535         return this.cm.getColumnId(index);
58536     },
58537
58538     getSplitters : function()
58539     {
58540         if(this.splitterSelector){
58541            return Roo.DomQuery.select(this.splitterSelector);
58542         }else{
58543             return null;
58544       }
58545     },
58546
58547     getSplitter : function(index){
58548         return this.getSplitters()[index];
58549     },
58550
58551     onRowOver : function(e, t){
58552         var row;
58553         if((row = this.findRowIndex(t)) !== false){
58554             this.getRowComposite(row).addClass("x-grid-row-over");
58555         }
58556     },
58557
58558     onRowOut : function(e, t){
58559         var row;
58560         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
58561             this.getRowComposite(row).removeClass("x-grid-row-over");
58562         }
58563     },
58564
58565     renderHeaders : function(){
58566         var cm = this.cm;
58567         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
58568         var cb = [], lb = [], sb = [], lsb = [], p = {};
58569         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58570             p.cellId = "x-grid-hd-0-" + i;
58571             p.splitId = "x-grid-csplit-0-" + i;
58572             p.id = cm.getColumnId(i);
58573             p.value = cm.getColumnHeader(i) || "";
58574             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
58575             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
58576             if(!cm.isLocked(i)){
58577                 cb[cb.length] = ct.apply(p);
58578                 sb[sb.length] = st.apply(p);
58579             }else{
58580                 lb[lb.length] = ct.apply(p);
58581                 lsb[lsb.length] = st.apply(p);
58582             }
58583         }
58584         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
58585                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
58586     },
58587
58588     updateHeaders : function(){
58589         var html = this.renderHeaders();
58590         this.lockedHd.update(html[0]);
58591         this.mainHd.update(html[1]);
58592     },
58593
58594     /**
58595      * Focuses the specified row.
58596      * @param {Number} row The row index
58597      */
58598     focusRow : function(row)
58599     {
58600         //Roo.log('GridView.focusRow');
58601         var x = this.scroller.dom.scrollLeft;
58602         this.focusCell(row, 0, false);
58603         this.scroller.dom.scrollLeft = x;
58604     },
58605
58606     /**
58607      * Focuses the specified cell.
58608      * @param {Number} row The row index
58609      * @param {Number} col The column index
58610      * @param {Boolean} hscroll false to disable horizontal scrolling
58611      */
58612     focusCell : function(row, col, hscroll)
58613     {
58614         //Roo.log('GridView.focusCell');
58615         var el = this.ensureVisible(row, col, hscroll);
58616         this.focusEl.alignTo(el, "tl-tl");
58617         if(Roo.isGecko){
58618             this.focusEl.focus();
58619         }else{
58620             this.focusEl.focus.defer(1, this.focusEl);
58621         }
58622     },
58623
58624     /**
58625      * Scrolls the specified cell into view
58626      * @param {Number} row The row index
58627      * @param {Number} col The column index
58628      * @param {Boolean} hscroll false to disable horizontal scrolling
58629      */
58630     ensureVisible : function(row, col, hscroll)
58631     {
58632         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
58633         //return null; //disable for testing.
58634         if(typeof row != "number"){
58635             row = row.rowIndex;
58636         }
58637         if(row < 0 && row >= this.ds.getCount()){
58638             return  null;
58639         }
58640         col = (col !== undefined ? col : 0);
58641         var cm = this.grid.colModel;
58642         while(cm.isHidden(col)){
58643             col++;
58644         }
58645
58646         var el = this.getCell(row, col);
58647         if(!el){
58648             return null;
58649         }
58650         var c = this.scroller.dom;
58651
58652         var ctop = parseInt(el.offsetTop, 10);
58653         var cleft = parseInt(el.offsetLeft, 10);
58654         var cbot = ctop + el.offsetHeight;
58655         var cright = cleft + el.offsetWidth;
58656         
58657         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
58658         var stop = parseInt(c.scrollTop, 10);
58659         var sleft = parseInt(c.scrollLeft, 10);
58660         var sbot = stop + ch;
58661         var sright = sleft + c.clientWidth;
58662         /*
58663         Roo.log('GridView.ensureVisible:' +
58664                 ' ctop:' + ctop +
58665                 ' c.clientHeight:' + c.clientHeight +
58666                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
58667                 ' stop:' + stop +
58668                 ' cbot:' + cbot +
58669                 ' sbot:' + sbot +
58670                 ' ch:' + ch  
58671                 );
58672         */
58673         if(ctop < stop){
58674             c.scrollTop = ctop;
58675             //Roo.log("set scrolltop to ctop DISABLE?");
58676         }else if(cbot > sbot){
58677             //Roo.log("set scrolltop to cbot-ch");
58678             c.scrollTop = cbot-ch;
58679         }
58680         
58681         if(hscroll !== false){
58682             if(cleft < sleft){
58683                 c.scrollLeft = cleft;
58684             }else if(cright > sright){
58685                 c.scrollLeft = cright-c.clientWidth;
58686             }
58687         }
58688          
58689         return el;
58690     },
58691
58692     updateColumns : function(){
58693         this.grid.stopEditing();
58694         var cm = this.grid.colModel, colIds = this.getColumnIds();
58695         //var totalWidth = cm.getTotalWidth();
58696         var pos = 0;
58697         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58698             //if(cm.isHidden(i)) continue;
58699             var w = cm.getColumnWidth(i);
58700             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58701             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
58702         }
58703         this.updateSplitters();
58704     },
58705
58706     generateRules : function(cm){
58707         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
58708         Roo.util.CSS.removeStyleSheet(rulesId);
58709         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58710             var cid = cm.getColumnId(i);
58711             var align = '';
58712             if(cm.config[i].align){
58713                 align = 'text-align:'+cm.config[i].align+';';
58714             }
58715             var hidden = '';
58716             if(cm.isHidden(i)){
58717                 hidden = 'display:none;';
58718             }
58719             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
58720             ruleBuf.push(
58721                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
58722                     this.hdSelector, cid, " {\n", align, width, "}\n",
58723                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
58724                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
58725         }
58726         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58727     },
58728
58729     updateSplitters : function(){
58730         var cm = this.cm, s = this.getSplitters();
58731         if(s){ // splitters not created yet
58732             var pos = 0, locked = true;
58733             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58734                 if(cm.isHidden(i)) {
58735                     continue;
58736                 }
58737                 var w = cm.getColumnWidth(i); // make sure it's a number
58738                 if(!cm.isLocked(i) && locked){
58739                     pos = 0;
58740                     locked = false;
58741                 }
58742                 pos += w;
58743                 s[i].style.left = (pos-this.splitOffset) + "px";
58744             }
58745         }
58746     },
58747
58748     handleHiddenChange : function(colModel, colIndex, hidden){
58749         if(hidden){
58750             this.hideColumn(colIndex);
58751         }else{
58752             this.unhideColumn(colIndex);
58753         }
58754     },
58755
58756     hideColumn : function(colIndex){
58757         var cid = this.getColumnId(colIndex);
58758         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
58759         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
58760         if(Roo.isSafari){
58761             this.updateHeaders();
58762         }
58763         this.updateSplitters();
58764         this.layout();
58765     },
58766
58767     unhideColumn : function(colIndex){
58768         var cid = this.getColumnId(colIndex);
58769         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
58770         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
58771
58772         if(Roo.isSafari){
58773             this.updateHeaders();
58774         }
58775         this.updateSplitters();
58776         this.layout();
58777     },
58778
58779     insertRows : function(dm, firstRow, lastRow, isUpdate){
58780         if(firstRow == 0 && lastRow == dm.getCount()-1){
58781             this.refresh();
58782         }else{
58783             if(!isUpdate){
58784                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
58785             }
58786             var s = this.getScrollState();
58787             var markup = this.renderRows(firstRow, lastRow);
58788             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
58789             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
58790             this.restoreScroll(s);
58791             if(!isUpdate){
58792                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
58793                 this.syncRowHeights(firstRow, lastRow);
58794                 this.stripeRows(firstRow);
58795                 this.layout();
58796             }
58797         }
58798     },
58799
58800     bufferRows : function(markup, target, index){
58801         var before = null, trows = target.rows, tbody = target.tBodies[0];
58802         if(index < trows.length){
58803             before = trows[index];
58804         }
58805         var b = document.createElement("div");
58806         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
58807         var rows = b.firstChild.rows;
58808         for(var i = 0, len = rows.length; i < len; i++){
58809             if(before){
58810                 tbody.insertBefore(rows[0], before);
58811             }else{
58812                 tbody.appendChild(rows[0]);
58813             }
58814         }
58815         b.innerHTML = "";
58816         b = null;
58817     },
58818
58819     deleteRows : function(dm, firstRow, lastRow){
58820         if(dm.getRowCount()<1){
58821             this.fireEvent("beforerefresh", this);
58822             this.mainBody.update("");
58823             this.lockedBody.update("");
58824             this.fireEvent("refresh", this);
58825         }else{
58826             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
58827             var bt = this.getBodyTable();
58828             var tbody = bt.firstChild;
58829             var rows = bt.rows;
58830             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
58831                 tbody.removeChild(rows[firstRow]);
58832             }
58833             this.stripeRows(firstRow);
58834             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
58835         }
58836     },
58837
58838     updateRows : function(dataSource, firstRow, lastRow){
58839         var s = this.getScrollState();
58840         this.refresh();
58841         this.restoreScroll(s);
58842     },
58843
58844     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
58845         if(!noRefresh){
58846            this.refresh();
58847         }
58848         this.updateHeaderSortState();
58849     },
58850
58851     getScrollState : function(){
58852         
58853         var sb = this.scroller.dom;
58854         return {left: sb.scrollLeft, top: sb.scrollTop};
58855     },
58856
58857     stripeRows : function(startRow){
58858         if(!this.grid.stripeRows || this.ds.getCount() < 1){
58859             return;
58860         }
58861         startRow = startRow || 0;
58862         var rows = this.getBodyTable().rows;
58863         var lrows = this.getLockedTable().rows;
58864         var cls = ' x-grid-row-alt ';
58865         for(var i = startRow, len = rows.length; i < len; i++){
58866             var row = rows[i], lrow = lrows[i];
58867             var isAlt = ((i+1) % 2 == 0);
58868             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
58869             if(isAlt == hasAlt){
58870                 continue;
58871             }
58872             if(isAlt){
58873                 row.className += " x-grid-row-alt";
58874             }else{
58875                 row.className = row.className.replace("x-grid-row-alt", "");
58876             }
58877             if(lrow){
58878                 lrow.className = row.className;
58879             }
58880         }
58881     },
58882
58883     restoreScroll : function(state){
58884         //Roo.log('GridView.restoreScroll');
58885         var sb = this.scroller.dom;
58886         sb.scrollLeft = state.left;
58887         sb.scrollTop = state.top;
58888         this.syncScroll();
58889     },
58890
58891     syncScroll : function(){
58892         //Roo.log('GridView.syncScroll');
58893         var sb = this.scroller.dom;
58894         var sh = this.mainHd.dom;
58895         var bs = this.mainBody.dom;
58896         var lv = this.lockedBody.dom;
58897         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
58898         lv.scrollTop = bs.scrollTop = sb.scrollTop;
58899     },
58900
58901     handleScroll : function(e){
58902         this.syncScroll();
58903         var sb = this.scroller.dom;
58904         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
58905         e.stopEvent();
58906     },
58907
58908     handleWheel : function(e){
58909         var d = e.getWheelDelta();
58910         this.scroller.dom.scrollTop -= d*22;
58911         // set this here to prevent jumpy scrolling on large tables
58912         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
58913         e.stopEvent();
58914     },
58915
58916     renderRows : function(startRow, endRow){
58917         // pull in all the crap needed to render rows
58918         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
58919         var colCount = cm.getColumnCount();
58920
58921         if(ds.getCount() < 1){
58922             return ["", ""];
58923         }
58924
58925         // build a map for all the columns
58926         var cs = [];
58927         for(var i = 0; i < colCount; i++){
58928             var name = cm.getDataIndex(i);
58929             cs[i] = {
58930                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
58931                 renderer : cm.getRenderer(i),
58932                 id : cm.getColumnId(i),
58933                 locked : cm.isLocked(i),
58934                 has_editor : cm.isCellEditable(i)
58935             };
58936         }
58937
58938         startRow = startRow || 0;
58939         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
58940
58941         // records to render
58942         var rs = ds.getRange(startRow, endRow);
58943
58944         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
58945     },
58946
58947     // As much as I hate to duplicate code, this was branched because FireFox really hates
58948     // [].join("") on strings. The performance difference was substantial enough to
58949     // branch this function
58950     doRender : Roo.isGecko ?
58951             function(cs, rs, ds, startRow, colCount, stripe){
58952                 var ts = this.templates, ct = ts.cell, rt = ts.row;
58953                 // buffers
58954                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
58955                 
58956                 var hasListener = this.grid.hasListener('rowclass');
58957                 var rowcfg = {};
58958                 for(var j = 0, len = rs.length; j < len; j++){
58959                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
58960                     for(var i = 0; i < colCount; i++){
58961                         c = cs[i];
58962                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
58963                         p.id = c.id;
58964                         p.css = p.attr = "";
58965                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
58966                         if(p.value == undefined || p.value === "") {
58967                             p.value = "&#160;";
58968                         }
58969                         if(c.has_editor){
58970                             p.css += ' x-grid-editable-cell';
58971                         }
58972                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
58973                             p.css +=  ' x-grid-dirty-cell';
58974                         }
58975                         var markup = ct.apply(p);
58976                         if(!c.locked){
58977                             cb+= markup;
58978                         }else{
58979                             lcb+= markup;
58980                         }
58981                     }
58982                     var alt = [];
58983                     if(stripe && ((rowIndex+1) % 2 == 0)){
58984                         alt.push("x-grid-row-alt")
58985                     }
58986                     if(r.dirty){
58987                         alt.push(  " x-grid-dirty-row");
58988                     }
58989                     rp.cells = lcb;
58990                     if(this.getRowClass){
58991                         alt.push(this.getRowClass(r, rowIndex));
58992                     }
58993                     if (hasListener) {
58994                         rowcfg = {
58995                              
58996                             record: r,
58997                             rowIndex : rowIndex,
58998                             rowClass : ''
58999                         };
59000                         this.grid.fireEvent('rowclass', this, rowcfg);
59001                         alt.push(rowcfg.rowClass);
59002                     }
59003                     rp.alt = alt.join(" ");
59004                     lbuf+= rt.apply(rp);
59005                     rp.cells = cb;
59006                     buf+=  rt.apply(rp);
59007                 }
59008                 return [lbuf, buf];
59009             } :
59010             function(cs, rs, ds, startRow, colCount, stripe){
59011                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59012                 // buffers
59013                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59014                 var hasListener = this.grid.hasListener('rowclass');
59015  
59016                 var rowcfg = {};
59017                 for(var j = 0, len = rs.length; j < len; j++){
59018                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
59019                     for(var i = 0; i < colCount; i++){
59020                         c = cs[i];
59021                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59022                         p.id = c.id;
59023                         p.css = p.attr = "";
59024                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59025                         if(p.value == undefined || p.value === "") {
59026                             p.value = "&#160;";
59027                         }
59028                         //Roo.log(c);
59029                          if(c.has_editor){
59030                             p.css += ' x-grid-editable-cell';
59031                         }
59032                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
59033                             p.css += ' x-grid-dirty-cell' 
59034                         }
59035                         
59036                         var markup = ct.apply(p);
59037                         if(!c.locked){
59038                             cb[cb.length] = markup;
59039                         }else{
59040                             lcb[lcb.length] = markup;
59041                         }
59042                     }
59043                     var alt = [];
59044                     if(stripe && ((rowIndex+1) % 2 == 0)){
59045                         alt.push( "x-grid-row-alt");
59046                     }
59047                     if(r.dirty){
59048                         alt.push(" x-grid-dirty-row");
59049                     }
59050                     rp.cells = lcb;
59051                     if(this.getRowClass){
59052                         alt.push( this.getRowClass(r, rowIndex));
59053                     }
59054                     if (hasListener) {
59055                         rowcfg = {
59056                              
59057                             record: r,
59058                             rowIndex : rowIndex,
59059                             rowClass : ''
59060                         };
59061                         this.grid.fireEvent('rowclass', this, rowcfg);
59062                         alt.push(rowcfg.rowClass);
59063                     }
59064                     
59065                     rp.alt = alt.join(" ");
59066                     rp.cells = lcb.join("");
59067                     lbuf[lbuf.length] = rt.apply(rp);
59068                     rp.cells = cb.join("");
59069                     buf[buf.length] =  rt.apply(rp);
59070                 }
59071                 return [lbuf.join(""), buf.join("")];
59072             },
59073
59074     renderBody : function(){
59075         var markup = this.renderRows();
59076         var bt = this.templates.body;
59077         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
59078     },
59079
59080     /**
59081      * Refreshes the grid
59082      * @param {Boolean} headersToo
59083      */
59084     refresh : function(headersToo){
59085         this.fireEvent("beforerefresh", this);
59086         this.grid.stopEditing();
59087         var result = this.renderBody();
59088         this.lockedBody.update(result[0]);
59089         this.mainBody.update(result[1]);
59090         if(headersToo === true){
59091             this.updateHeaders();
59092             this.updateColumns();
59093             this.updateSplitters();
59094             this.updateHeaderSortState();
59095         }
59096         this.syncRowHeights();
59097         this.layout();
59098         this.fireEvent("refresh", this);
59099     },
59100
59101     handleColumnMove : function(cm, oldIndex, newIndex){
59102         this.indexMap = null;
59103         var s = this.getScrollState();
59104         this.refresh(true);
59105         this.restoreScroll(s);
59106         this.afterMove(newIndex);
59107     },
59108
59109     afterMove : function(colIndex){
59110         if(this.enableMoveAnim && Roo.enableFx){
59111             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
59112         }
59113         // if multisort - fix sortOrder, and reload..
59114         if (this.grid.dataSource.multiSort) {
59115             // the we can call sort again..
59116             var dm = this.grid.dataSource;
59117             var cm = this.grid.colModel;
59118             var so = [];
59119             for(var i = 0; i < cm.config.length; i++ ) {
59120                 
59121                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
59122                     continue; // dont' bother, it's not in sort list or being set.
59123                 }
59124                 
59125                 so.push(cm.config[i].dataIndex);
59126             };
59127             dm.sortOrder = so;
59128             dm.load(dm.lastOptions);
59129             
59130             
59131         }
59132         
59133     },
59134
59135     updateCell : function(dm, rowIndex, dataIndex){
59136         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
59137         if(typeof colIndex == "undefined"){ // not present in grid
59138             return;
59139         }
59140         var cm = this.grid.colModel;
59141         var cell = this.getCell(rowIndex, colIndex);
59142         var cellText = this.getCellText(rowIndex, colIndex);
59143
59144         var p = {
59145             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
59146             id : cm.getColumnId(colIndex),
59147             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
59148         };
59149         var renderer = cm.getRenderer(colIndex);
59150         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
59151         if(typeof val == "undefined" || val === "") {
59152             val = "&#160;";
59153         }
59154         cellText.innerHTML = val;
59155         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
59156         this.syncRowHeights(rowIndex, rowIndex);
59157     },
59158
59159     calcColumnWidth : function(colIndex, maxRowsToMeasure){
59160         var maxWidth = 0;
59161         if(this.grid.autoSizeHeaders){
59162             var h = this.getHeaderCellMeasure(colIndex);
59163             maxWidth = Math.max(maxWidth, h.scrollWidth);
59164         }
59165         var tb, index;
59166         if(this.cm.isLocked(colIndex)){
59167             tb = this.getLockedTable();
59168             index = colIndex;
59169         }else{
59170             tb = this.getBodyTable();
59171             index = colIndex - this.cm.getLockedCount();
59172         }
59173         if(tb && tb.rows){
59174             var rows = tb.rows;
59175             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
59176             for(var i = 0; i < stopIndex; i++){
59177                 var cell = rows[i].childNodes[index].firstChild;
59178                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
59179             }
59180         }
59181         return maxWidth + /*margin for error in IE*/ 5;
59182     },
59183     /**
59184      * Autofit a column to its content.
59185      * @param {Number} colIndex
59186      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
59187      */
59188      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
59189          if(this.cm.isHidden(colIndex)){
59190              return; // can't calc a hidden column
59191          }
59192         if(forceMinSize){
59193             var cid = this.cm.getColumnId(colIndex);
59194             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
59195            if(this.grid.autoSizeHeaders){
59196                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
59197            }
59198         }
59199         var newWidth = this.calcColumnWidth(colIndex);
59200         this.cm.setColumnWidth(colIndex,
59201             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
59202         if(!suppressEvent){
59203             this.grid.fireEvent("columnresize", colIndex, newWidth);
59204         }
59205     },
59206
59207     /**
59208      * Autofits all columns to their content and then expands to fit any extra space in the grid
59209      */
59210      autoSizeColumns : function(){
59211         var cm = this.grid.colModel;
59212         var colCount = cm.getColumnCount();
59213         for(var i = 0; i < colCount; i++){
59214             this.autoSizeColumn(i, true, true);
59215         }
59216         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
59217             this.fitColumns();
59218         }else{
59219             this.updateColumns();
59220             this.layout();
59221         }
59222     },
59223
59224     /**
59225      * Autofits all columns to the grid's width proportionate with their current size
59226      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
59227      */
59228     fitColumns : function(reserveScrollSpace){
59229         var cm = this.grid.colModel;
59230         var colCount = cm.getColumnCount();
59231         var cols = [];
59232         var width = 0;
59233         var i, w;
59234         for (i = 0; i < colCount; i++){
59235             if(!cm.isHidden(i) && !cm.isFixed(i)){
59236                 w = cm.getColumnWidth(i);
59237                 cols.push(i);
59238                 cols.push(w);
59239                 width += w;
59240             }
59241         }
59242         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
59243         if(reserveScrollSpace){
59244             avail -= 17;
59245         }
59246         var frac = (avail - cm.getTotalWidth())/width;
59247         while (cols.length){
59248             w = cols.pop();
59249             i = cols.pop();
59250             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
59251         }
59252         this.updateColumns();
59253         this.layout();
59254     },
59255
59256     onRowSelect : function(rowIndex){
59257         var row = this.getRowComposite(rowIndex);
59258         row.addClass("x-grid-row-selected");
59259     },
59260
59261     onRowDeselect : function(rowIndex){
59262         var row = this.getRowComposite(rowIndex);
59263         row.removeClass("x-grid-row-selected");
59264     },
59265
59266     onCellSelect : function(row, col){
59267         var cell = this.getCell(row, col);
59268         if(cell){
59269             Roo.fly(cell).addClass("x-grid-cell-selected");
59270         }
59271     },
59272
59273     onCellDeselect : function(row, col){
59274         var cell = this.getCell(row, col);
59275         if(cell){
59276             Roo.fly(cell).removeClass("x-grid-cell-selected");
59277         }
59278     },
59279
59280     updateHeaderSortState : function(){
59281         
59282         // sort state can be single { field: xxx, direction : yyy}
59283         // or   { xxx=>ASC , yyy : DESC ..... }
59284         
59285         var mstate = {};
59286         if (!this.ds.multiSort) { 
59287             var state = this.ds.getSortState();
59288             if(!state){
59289                 return;
59290             }
59291             mstate[state.field] = state.direction;
59292             // FIXME... - this is not used here.. but might be elsewhere..
59293             this.sortState = state;
59294             
59295         } else {
59296             mstate = this.ds.sortToggle;
59297         }
59298         //remove existing sort classes..
59299         
59300         var sc = this.sortClasses;
59301         var hds = this.el.select(this.headerSelector).removeClass(sc);
59302         
59303         for(var f in mstate) {
59304         
59305             var sortColumn = this.cm.findColumnIndex(f);
59306             
59307             if(sortColumn != -1){
59308                 var sortDir = mstate[f];        
59309                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
59310             }
59311         }
59312         
59313          
59314         
59315     },
59316
59317
59318     handleHeaderClick : function(g, index,e){
59319         
59320         Roo.log("header click");
59321         
59322         if (Roo.isTouch) {
59323             // touch events on header are handled by context
59324             this.handleHdCtx(g,index,e);
59325             return;
59326         }
59327         
59328         
59329         if(this.headersDisabled){
59330             return;
59331         }
59332         var dm = g.dataSource, cm = g.colModel;
59333         if(!cm.isSortable(index)){
59334             return;
59335         }
59336         g.stopEditing();
59337         
59338         if (dm.multiSort) {
59339             // update the sortOrder
59340             var so = [];
59341             for(var i = 0; i < cm.config.length; i++ ) {
59342                 
59343                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
59344                     continue; // dont' bother, it's not in sort list or being set.
59345                 }
59346                 
59347                 so.push(cm.config[i].dataIndex);
59348             };
59349             dm.sortOrder = so;
59350         }
59351         
59352         
59353         dm.sort(cm.getDataIndex(index));
59354     },
59355
59356
59357     destroy : function(){
59358         if(this.colMenu){
59359             this.colMenu.removeAll();
59360             Roo.menu.MenuMgr.unregister(this.colMenu);
59361             this.colMenu.getEl().remove();
59362             delete this.colMenu;
59363         }
59364         if(this.hmenu){
59365             this.hmenu.removeAll();
59366             Roo.menu.MenuMgr.unregister(this.hmenu);
59367             this.hmenu.getEl().remove();
59368             delete this.hmenu;
59369         }
59370         if(this.grid.enableColumnMove){
59371             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
59372             if(dds){
59373                 for(var dd in dds){
59374                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
59375                         var elid = dds[dd].dragElId;
59376                         dds[dd].unreg();
59377                         Roo.get(elid).remove();
59378                     } else if(dds[dd].config.isTarget){
59379                         dds[dd].proxyTop.remove();
59380                         dds[dd].proxyBottom.remove();
59381                         dds[dd].unreg();
59382                     }
59383                     if(Roo.dd.DDM.locationCache[dd]){
59384                         delete Roo.dd.DDM.locationCache[dd];
59385                     }
59386                 }
59387                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
59388             }
59389         }
59390         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
59391         this.bind(null, null);
59392         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
59393     },
59394
59395     handleLockChange : function(){
59396         this.refresh(true);
59397     },
59398
59399     onDenyColumnLock : function(){
59400
59401     },
59402
59403     onDenyColumnHide : function(){
59404
59405     },
59406
59407     handleHdMenuClick : function(item){
59408         var index = this.hdCtxIndex;
59409         var cm = this.cm, ds = this.ds;
59410         switch(item.id){
59411             case "asc":
59412                 ds.sort(cm.getDataIndex(index), "ASC");
59413                 break;
59414             case "desc":
59415                 ds.sort(cm.getDataIndex(index), "DESC");
59416                 break;
59417             case "lock":
59418                 var lc = cm.getLockedCount();
59419                 if(cm.getColumnCount(true) <= lc+1){
59420                     this.onDenyColumnLock();
59421                     return;
59422                 }
59423                 if(lc != index){
59424                     cm.setLocked(index, true, true);
59425                     cm.moveColumn(index, lc);
59426                     this.grid.fireEvent("columnmove", index, lc);
59427                 }else{
59428                     cm.setLocked(index, true);
59429                 }
59430             break;
59431             case "unlock":
59432                 var lc = cm.getLockedCount();
59433                 if((lc-1) != index){
59434                     cm.setLocked(index, false, true);
59435                     cm.moveColumn(index, lc-1);
59436                     this.grid.fireEvent("columnmove", index, lc-1);
59437                 }else{
59438                     cm.setLocked(index, false);
59439                 }
59440             break;
59441             case 'wider': // used to expand cols on touch..
59442             case 'narrow':
59443                 var cw = cm.getColumnWidth(index);
59444                 cw += (item.id == 'wider' ? 1 : -1) * 50;
59445                 cw = Math.max(0, cw);
59446                 cw = Math.min(cw,4000);
59447                 cm.setColumnWidth(index, cw);
59448                 break;
59449                 
59450             default:
59451                 index = cm.getIndexById(item.id.substr(4));
59452                 if(index != -1){
59453                     if(item.checked && cm.getColumnCount(true) <= 1){
59454                         this.onDenyColumnHide();
59455                         return false;
59456                     }
59457                     cm.setHidden(index, item.checked);
59458                 }
59459         }
59460         return true;
59461     },
59462
59463     beforeColMenuShow : function(){
59464         var cm = this.cm,  colCount = cm.getColumnCount();
59465         this.colMenu.removeAll();
59466         for(var i = 0; i < colCount; i++){
59467             this.colMenu.add(new Roo.menu.CheckItem({
59468                 id: "col-"+cm.getColumnId(i),
59469                 text: cm.getColumnHeader(i),
59470                 checked: !cm.isHidden(i),
59471                 hideOnClick:false
59472             }));
59473         }
59474     },
59475
59476     handleHdCtx : function(g, index, e){
59477         e.stopEvent();
59478         var hd = this.getHeaderCell(index);
59479         this.hdCtxIndex = index;
59480         var ms = this.hmenu.items, cm = this.cm;
59481         ms.get("asc").setDisabled(!cm.isSortable(index));
59482         ms.get("desc").setDisabled(!cm.isSortable(index));
59483         if(this.grid.enableColLock !== false){
59484             ms.get("lock").setDisabled(cm.isLocked(index));
59485             ms.get("unlock").setDisabled(!cm.isLocked(index));
59486         }
59487         this.hmenu.show(hd, "tl-bl");
59488     },
59489
59490     handleHdOver : function(e){
59491         var hd = this.findHeaderCell(e.getTarget());
59492         if(hd && !this.headersDisabled){
59493             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
59494                this.fly(hd).addClass("x-grid-hd-over");
59495             }
59496         }
59497     },
59498
59499     handleHdOut : function(e){
59500         var hd = this.findHeaderCell(e.getTarget());
59501         if(hd){
59502             this.fly(hd).removeClass("x-grid-hd-over");
59503         }
59504     },
59505
59506     handleSplitDblClick : function(e, t){
59507         var i = this.getCellIndex(t);
59508         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
59509             this.autoSizeColumn(i, true);
59510             this.layout();
59511         }
59512     },
59513
59514     render : function(){
59515
59516         var cm = this.cm;
59517         var colCount = cm.getColumnCount();
59518
59519         if(this.grid.monitorWindowResize === true){
59520             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
59521         }
59522         var header = this.renderHeaders();
59523         var body = this.templates.body.apply({rows:""});
59524         var html = this.templates.master.apply({
59525             lockedBody: body,
59526             body: body,
59527             lockedHeader: header[0],
59528             header: header[1]
59529         });
59530
59531         //this.updateColumns();
59532
59533         this.grid.getGridEl().dom.innerHTML = html;
59534
59535         this.initElements();
59536         
59537         // a kludge to fix the random scolling effect in webkit
59538         this.el.on("scroll", function() {
59539             this.el.dom.scrollTop=0; // hopefully not recursive..
59540         },this);
59541
59542         this.scroller.on("scroll", this.handleScroll, this);
59543         this.lockedBody.on("mousewheel", this.handleWheel, this);
59544         this.mainBody.on("mousewheel", this.handleWheel, this);
59545
59546         this.mainHd.on("mouseover", this.handleHdOver, this);
59547         this.mainHd.on("mouseout", this.handleHdOut, this);
59548         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
59549                 {delegate: "."+this.splitClass});
59550
59551         this.lockedHd.on("mouseover", this.handleHdOver, this);
59552         this.lockedHd.on("mouseout", this.handleHdOut, this);
59553         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
59554                 {delegate: "."+this.splitClass});
59555
59556         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
59557             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59558         }
59559
59560         this.updateSplitters();
59561
59562         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
59563             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59564             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
59565         }
59566
59567         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
59568             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
59569             this.hmenu.add(
59570                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
59571                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
59572             );
59573             if(this.grid.enableColLock !== false){
59574                 this.hmenu.add('-',
59575                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
59576                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
59577                 );
59578             }
59579             if (Roo.isTouch) {
59580                  this.hmenu.add('-',
59581                     {id:"wider", text: this.columnsWiderText},
59582                     {id:"narrow", text: this.columnsNarrowText }
59583                 );
59584                 
59585                  
59586             }
59587             
59588             if(this.grid.enableColumnHide !== false){
59589
59590                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
59591                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
59592                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
59593
59594                 this.hmenu.add('-',
59595                     {id:"columns", text: this.columnsText, menu: this.colMenu}
59596                 );
59597             }
59598             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
59599
59600             this.grid.on("headercontextmenu", this.handleHdCtx, this);
59601         }
59602
59603         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
59604             this.dd = new Roo.grid.GridDragZone(this.grid, {
59605                 ddGroup : this.grid.ddGroup || 'GridDD'
59606             });
59607             
59608         }
59609
59610         /*
59611         for(var i = 0; i < colCount; i++){
59612             if(cm.isHidden(i)){
59613                 this.hideColumn(i);
59614             }
59615             if(cm.config[i].align){
59616                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
59617                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
59618             }
59619         }*/
59620         
59621         this.updateHeaderSortState();
59622
59623         this.beforeInitialResize();
59624         this.layout(true);
59625
59626         // two part rendering gives faster view to the user
59627         this.renderPhase2.defer(1, this);
59628     },
59629
59630     renderPhase2 : function(){
59631         // render the rows now
59632         this.refresh();
59633         if(this.grid.autoSizeColumns){
59634             this.autoSizeColumns();
59635         }
59636     },
59637
59638     beforeInitialResize : function(){
59639
59640     },
59641
59642     onColumnSplitterMoved : function(i, w){
59643         this.userResized = true;
59644         var cm = this.grid.colModel;
59645         cm.setColumnWidth(i, w, true);
59646         var cid = cm.getColumnId(i);
59647         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59648         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
59649         this.updateSplitters();
59650         this.layout();
59651         this.grid.fireEvent("columnresize", i, w);
59652     },
59653
59654     syncRowHeights : function(startIndex, endIndex){
59655         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
59656             startIndex = startIndex || 0;
59657             var mrows = this.getBodyTable().rows;
59658             var lrows = this.getLockedTable().rows;
59659             var len = mrows.length-1;
59660             endIndex = Math.min(endIndex || len, len);
59661             for(var i = startIndex; i <= endIndex; i++){
59662                 var m = mrows[i], l = lrows[i];
59663                 var h = Math.max(m.offsetHeight, l.offsetHeight);
59664                 m.style.height = l.style.height = h + "px";
59665             }
59666         }
59667     },
59668
59669     layout : function(initialRender, is2ndPass)
59670     {
59671         var g = this.grid;
59672         var auto = g.autoHeight;
59673         var scrollOffset = 16;
59674         var c = g.getGridEl(), cm = this.cm,
59675                 expandCol = g.autoExpandColumn,
59676                 gv = this;
59677         //c.beginMeasure();
59678
59679         if(!c.dom.offsetWidth){ // display:none?
59680             if(initialRender){
59681                 this.lockedWrap.show();
59682                 this.mainWrap.show();
59683             }
59684             return;
59685         }
59686
59687         var hasLock = this.cm.isLocked(0);
59688
59689         var tbh = this.headerPanel.getHeight();
59690         var bbh = this.footerPanel.getHeight();
59691
59692         if(auto){
59693             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
59694             var newHeight = ch + c.getBorderWidth("tb");
59695             if(g.maxHeight){
59696                 newHeight = Math.min(g.maxHeight, newHeight);
59697             }
59698             c.setHeight(newHeight);
59699         }
59700
59701         if(g.autoWidth){
59702             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
59703         }
59704
59705         var s = this.scroller;
59706
59707         var csize = c.getSize(true);
59708
59709         this.el.setSize(csize.width, csize.height);
59710
59711         this.headerPanel.setWidth(csize.width);
59712         this.footerPanel.setWidth(csize.width);
59713
59714         var hdHeight = this.mainHd.getHeight();
59715         var vw = csize.width;
59716         var vh = csize.height - (tbh + bbh);
59717
59718         s.setSize(vw, vh);
59719
59720         var bt = this.getBodyTable();
59721         
59722         if(cm.getLockedCount() == cm.config.length){
59723             bt = this.getLockedTable();
59724         }
59725         
59726         var ltWidth = hasLock ?
59727                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
59728
59729         var scrollHeight = bt.offsetHeight;
59730         var scrollWidth = ltWidth + bt.offsetWidth;
59731         var vscroll = false, hscroll = false;
59732
59733         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
59734
59735         var lw = this.lockedWrap, mw = this.mainWrap;
59736         var lb = this.lockedBody, mb = this.mainBody;
59737
59738         setTimeout(function(){
59739             var t = s.dom.offsetTop;
59740             var w = s.dom.clientWidth,
59741                 h = s.dom.clientHeight;
59742
59743             lw.setTop(t);
59744             lw.setSize(ltWidth, h);
59745
59746             mw.setLeftTop(ltWidth, t);
59747             mw.setSize(w-ltWidth, h);
59748
59749             lb.setHeight(h-hdHeight);
59750             mb.setHeight(h-hdHeight);
59751
59752             if(is2ndPass !== true && !gv.userResized && expandCol){
59753                 // high speed resize without full column calculation
59754                 
59755                 var ci = cm.getIndexById(expandCol);
59756                 if (ci < 0) {
59757                     ci = cm.findColumnIndex(expandCol);
59758                 }
59759                 ci = Math.max(0, ci); // make sure it's got at least the first col.
59760                 var expandId = cm.getColumnId(ci);
59761                 var  tw = cm.getTotalWidth(false);
59762                 var currentWidth = cm.getColumnWidth(ci);
59763                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
59764                 if(currentWidth != cw){
59765                     cm.setColumnWidth(ci, cw, true);
59766                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59767                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
59768                     gv.updateSplitters();
59769                     gv.layout(false, true);
59770                 }
59771             }
59772
59773             if(initialRender){
59774                 lw.show();
59775                 mw.show();
59776             }
59777             //c.endMeasure();
59778         }, 10);
59779     },
59780
59781     onWindowResize : function(){
59782         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
59783             return;
59784         }
59785         this.layout();
59786     },
59787
59788     appendFooter : function(parentEl){
59789         return null;
59790     },
59791
59792     sortAscText : "Sort Ascending",
59793     sortDescText : "Sort Descending",
59794     lockText : "Lock Column",
59795     unlockText : "Unlock Column",
59796     columnsText : "Columns",
59797  
59798     columnsWiderText : "Wider",
59799     columnsNarrowText : "Thinner"
59800 });
59801
59802
59803 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
59804     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
59805     this.proxy.el.addClass('x-grid3-col-dd');
59806 };
59807
59808 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
59809     handleMouseDown : function(e){
59810
59811     },
59812
59813     callHandleMouseDown : function(e){
59814         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
59815     }
59816 });
59817 /*
59818  * Based on:
59819  * Ext JS Library 1.1.1
59820  * Copyright(c) 2006-2007, Ext JS, LLC.
59821  *
59822  * Originally Released Under LGPL - original licence link has changed is not relivant.
59823  *
59824  * Fork - LGPL
59825  * <script type="text/javascript">
59826  */
59827  /**
59828  * @extends Roo.dd.DDProxy
59829  * @class Roo.grid.SplitDragZone
59830  * Support for Column Header resizing
59831  * @constructor
59832  * @param {Object} config
59833  */
59834 // private
59835 // This is a support class used internally by the Grid components
59836 Roo.grid.SplitDragZone = function(grid, hd, hd2){
59837     this.grid = grid;
59838     this.view = grid.getView();
59839     this.proxy = this.view.resizeProxy;
59840     Roo.grid.SplitDragZone.superclass.constructor.call(
59841         this,
59842         hd, // ID
59843         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
59844         {  // CONFIG
59845             dragElId : Roo.id(this.proxy.dom),
59846             resizeFrame:false
59847         }
59848     );
59849     
59850     this.setHandleElId(Roo.id(hd));
59851     if (hd2 !== false) {
59852         this.setOuterHandleElId(Roo.id(hd2));
59853     }
59854     
59855     this.scroll = false;
59856 };
59857 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
59858     fly: Roo.Element.fly,
59859
59860     b4StartDrag : function(x, y){
59861         this.view.headersDisabled = true;
59862         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
59863                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
59864         );
59865         this.proxy.setHeight(h);
59866         
59867         // for old system colWidth really stored the actual width?
59868         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
59869         // which in reality did not work.. - it worked only for fixed sizes
59870         // for resizable we need to use actual sizes.
59871         var w = this.cm.getColumnWidth(this.cellIndex);
59872         if (!this.view.mainWrap) {
59873             // bootstrap.
59874             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
59875         }
59876         
59877         
59878         
59879         // this was w-this.grid.minColumnWidth;
59880         // doesnt really make sense? - w = thie curren width or the rendered one?
59881         var minw = Math.max(w-this.grid.minColumnWidth, 0);
59882         this.resetConstraints();
59883         this.setXConstraint(minw, 1000);
59884         this.setYConstraint(0, 0);
59885         this.minX = x - minw;
59886         this.maxX = x + 1000;
59887         this.startPos = x;
59888         if (!this.view.mainWrap) { // this is Bootstrap code..
59889             this.getDragEl().style.display='block';
59890         }
59891         
59892         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
59893     },
59894
59895
59896     handleMouseDown : function(e){
59897         ev = Roo.EventObject.setEvent(e);
59898         var t = this.fly(ev.getTarget());
59899         if(t.hasClass("x-grid-split")){
59900             this.cellIndex = this.view.getCellIndex(t.dom);
59901             this.split = t.dom;
59902             this.cm = this.grid.colModel;
59903             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
59904                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
59905             }
59906         }
59907     },
59908
59909     endDrag : function(e){
59910         this.view.headersDisabled = false;
59911         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
59912         var diff = endX - this.startPos;
59913         // 
59914         var w = this.cm.getColumnWidth(this.cellIndex);
59915         if (!this.view.mainWrap) {
59916             w = 0;
59917         }
59918         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
59919     },
59920
59921     autoOffset : function(){
59922         this.setDelta(0,0);
59923     }
59924 });/*
59925  * Based on:
59926  * Ext JS Library 1.1.1
59927  * Copyright(c) 2006-2007, Ext JS, LLC.
59928  *
59929  * Originally Released Under LGPL - original licence link has changed is not relivant.
59930  *
59931  * Fork - LGPL
59932  * <script type="text/javascript">
59933  */
59934  
59935 // private
59936 // This is a support class used internally by the Grid components
59937 Roo.grid.GridDragZone = function(grid, config){
59938     this.view = grid.getView();
59939     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
59940     if(this.view.lockedBody){
59941         this.setHandleElId(Roo.id(this.view.mainBody.dom));
59942         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
59943     }
59944     this.scroll = false;
59945     this.grid = grid;
59946     this.ddel = document.createElement('div');
59947     this.ddel.className = 'x-grid-dd-wrap';
59948 };
59949
59950 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
59951     ddGroup : "GridDD",
59952
59953     getDragData : function(e){
59954         var t = Roo.lib.Event.getTarget(e);
59955         var rowIndex = this.view.findRowIndex(t);
59956         var sm = this.grid.selModel;
59957             
59958         //Roo.log(rowIndex);
59959         
59960         if (sm.getSelectedCell) {
59961             // cell selection..
59962             if (!sm.getSelectedCell()) {
59963                 return false;
59964             }
59965             if (rowIndex != sm.getSelectedCell()[0]) {
59966                 return false;
59967             }
59968         
59969         }
59970         if (sm.getSelections && sm.getSelections().length < 1) {
59971             return false;
59972         }
59973         
59974         
59975         // before it used to all dragging of unseleted... - now we dont do that.
59976         if(rowIndex !== false){
59977             
59978             // if editorgrid.. 
59979             
59980             
59981             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
59982                
59983             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
59984               //  
59985             //}
59986             if (e.hasModifier()){
59987                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
59988             }
59989             
59990             Roo.log("getDragData");
59991             
59992             return {
59993                 grid: this.grid,
59994                 ddel: this.ddel,
59995                 rowIndex: rowIndex,
59996                 selections: sm.getSelections ? sm.getSelections() : (
59997                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
59998             };
59999         }
60000         return false;
60001     },
60002     
60003     
60004     onInitDrag : function(e){
60005         var data = this.dragData;
60006         this.ddel.innerHTML = this.grid.getDragDropText();
60007         this.proxy.update(this.ddel);
60008         // fire start drag?
60009     },
60010
60011     afterRepair : function(){
60012         this.dragging = false;
60013     },
60014
60015     getRepairXY : function(e, data){
60016         return false;
60017     },
60018
60019     onEndDrag : function(data, e){
60020         // fire end drag?
60021     },
60022
60023     onValidDrop : function(dd, e, id){
60024         // fire drag drop?
60025         this.hideProxy();
60026     },
60027
60028     beforeInvalidDrop : function(e, id){
60029
60030     }
60031 });/*
60032  * Based on:
60033  * Ext JS Library 1.1.1
60034  * Copyright(c) 2006-2007, Ext JS, LLC.
60035  *
60036  * Originally Released Under LGPL - original licence link has changed is not relivant.
60037  *
60038  * Fork - LGPL
60039  * <script type="text/javascript">
60040  */
60041  
60042
60043 /**
60044  * @class Roo.grid.ColumnModel
60045  * @extends Roo.util.Observable
60046  * This is the default implementation of a ColumnModel used by the Grid. It defines
60047  * the columns in the grid.
60048  * <br>Usage:<br>
60049  <pre><code>
60050  var colModel = new Roo.grid.ColumnModel([
60051         {header: "Ticker", width: 60, sortable: true, locked: true},
60052         {header: "Company Name", width: 150, sortable: true},
60053         {header: "Market Cap.", width: 100, sortable: true},
60054         {header: "$ Sales", width: 100, sortable: true, renderer: money},
60055         {header: "Employees", width: 100, sortable: true, resizable: false}
60056  ]);
60057  </code></pre>
60058  * <p>
60059  
60060  * The config options listed for this class are options which may appear in each
60061  * individual column definition.
60062  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
60063  * @constructor
60064  * @param {Object} config An Array of column config objects. See this class's
60065  * config objects for details.
60066 */
60067 Roo.grid.ColumnModel = function(config){
60068         /**
60069      * The config passed into the constructor
60070      */
60071     this.config = []; //config;
60072     this.lookup = {};
60073
60074     // if no id, create one
60075     // if the column does not have a dataIndex mapping,
60076     // map it to the order it is in the config
60077     for(var i = 0, len = config.length; i < len; i++){
60078         this.addColumn(config[i]);
60079         
60080     }
60081
60082     /**
60083      * The width of columns which have no width specified (defaults to 100)
60084      * @type Number
60085      */
60086     this.defaultWidth = 100;
60087
60088     /**
60089      * Default sortable of columns which have no sortable specified (defaults to false)
60090      * @type Boolean
60091      */
60092     this.defaultSortable = false;
60093
60094     this.addEvents({
60095         /**
60096              * @event widthchange
60097              * Fires when the width of a column changes.
60098              * @param {ColumnModel} this
60099              * @param {Number} columnIndex The column index
60100              * @param {Number} newWidth The new width
60101              */
60102             "widthchange": true,
60103         /**
60104              * @event headerchange
60105              * Fires when the text of a header changes.
60106              * @param {ColumnModel} this
60107              * @param {Number} columnIndex The column index
60108              * @param {Number} newText The new header text
60109              */
60110             "headerchange": true,
60111         /**
60112              * @event hiddenchange
60113              * Fires when a column is hidden or "unhidden".
60114              * @param {ColumnModel} this
60115              * @param {Number} columnIndex The column index
60116              * @param {Boolean} hidden true if hidden, false otherwise
60117              */
60118             "hiddenchange": true,
60119             /**
60120          * @event columnmoved
60121          * Fires when a column is moved.
60122          * @param {ColumnModel} this
60123          * @param {Number} oldIndex
60124          * @param {Number} newIndex
60125          */
60126         "columnmoved" : true,
60127         /**
60128          * @event columlockchange
60129          * Fires when a column's locked state is changed
60130          * @param {ColumnModel} this
60131          * @param {Number} colIndex
60132          * @param {Boolean} locked true if locked
60133          */
60134         "columnlockchange" : true
60135     });
60136     Roo.grid.ColumnModel.superclass.constructor.call(this);
60137 };
60138 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
60139     /**
60140      * @cfg {String} header The header text to display in the Grid view.
60141      */
60142         /**
60143      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
60144      */
60145         /**
60146      * @cfg {String} smHeader Header at Bootsrap Small width
60147      */
60148         /**
60149      * @cfg {String} mdHeader Header at Bootsrap Medium width
60150      */
60151         /**
60152      * @cfg {String} lgHeader Header at Bootsrap Large width
60153      */
60154         /**
60155      * @cfg {String} xlHeader Header at Bootsrap extra Large width
60156      */
60157     /**
60158      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
60159      * {@link Roo.data.Record} definition from which to draw the column's value. If not
60160      * specified, the column's index is used as an index into the Record's data Array.
60161      */
60162     /**
60163      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
60164      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
60165      */
60166     /**
60167      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
60168      * Defaults to the value of the {@link #defaultSortable} property.
60169      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
60170      */
60171     /**
60172      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
60173      */
60174     /**
60175      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
60176      */
60177     /**
60178      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
60179      */
60180     /**
60181      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
60182      */
60183     /**
60184      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
60185      * given the cell's data value. See {@link #setRenderer}. If not specified, the
60186      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
60187      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
60188      */
60189        /**
60190      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
60191      */
60192     /**
60193      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
60194      */
60195     /**
60196      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
60197      */
60198     /**
60199      * @cfg {String} cursor (Optional)
60200      */
60201     /**
60202      * @cfg {String} tooltip (Optional)
60203      */
60204     /**
60205      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
60206      */
60207     /**
60208      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
60209      */
60210     /**
60211      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
60212      */
60213     /**
60214      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
60215      */
60216         /**
60217      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
60218      */
60219     /**
60220      * Returns the id of the column at the specified index.
60221      * @param {Number} index The column index
60222      * @return {String} the id
60223      */
60224     getColumnId : function(index){
60225         return this.config[index].id;
60226     },
60227
60228     /**
60229      * Returns the column for a specified id.
60230      * @param {String} id The column id
60231      * @return {Object} the column
60232      */
60233     getColumnById : function(id){
60234         return this.lookup[id];
60235     },
60236
60237     
60238     /**
60239      * Returns the column Object for a specified dataIndex.
60240      * @param {String} dataIndex The column dataIndex
60241      * @return {Object|Boolean} the column or false if not found
60242      */
60243     getColumnByDataIndex: function(dataIndex){
60244         var index = this.findColumnIndex(dataIndex);
60245         return index > -1 ? this.config[index] : false;
60246     },
60247     
60248     /**
60249      * Returns the index for a specified column id.
60250      * @param {String} id The column id
60251      * @return {Number} the index, or -1 if not found
60252      */
60253     getIndexById : function(id){
60254         for(var i = 0, len = this.config.length; i < len; i++){
60255             if(this.config[i].id == id){
60256                 return i;
60257             }
60258         }
60259         return -1;
60260     },
60261     
60262     /**
60263      * Returns the index for a specified column dataIndex.
60264      * @param {String} dataIndex The column dataIndex
60265      * @return {Number} the index, or -1 if not found
60266      */
60267     
60268     findColumnIndex : function(dataIndex){
60269         for(var i = 0, len = this.config.length; i < len; i++){
60270             if(this.config[i].dataIndex == dataIndex){
60271                 return i;
60272             }
60273         }
60274         return -1;
60275     },
60276     
60277     
60278     moveColumn : function(oldIndex, newIndex){
60279         var c = this.config[oldIndex];
60280         this.config.splice(oldIndex, 1);
60281         this.config.splice(newIndex, 0, c);
60282         this.dataMap = null;
60283         this.fireEvent("columnmoved", this, oldIndex, newIndex);
60284     },
60285
60286     isLocked : function(colIndex){
60287         return this.config[colIndex].locked === true;
60288     },
60289
60290     setLocked : function(colIndex, value, suppressEvent){
60291         if(this.isLocked(colIndex) == value){
60292             return;
60293         }
60294         this.config[colIndex].locked = value;
60295         if(!suppressEvent){
60296             this.fireEvent("columnlockchange", this, colIndex, value);
60297         }
60298     },
60299
60300     getTotalLockedWidth : function(){
60301         var totalWidth = 0;
60302         for(var i = 0; i < this.config.length; i++){
60303             if(this.isLocked(i) && !this.isHidden(i)){
60304                 this.totalWidth += this.getColumnWidth(i);
60305             }
60306         }
60307         return totalWidth;
60308     },
60309
60310     getLockedCount : function(){
60311         for(var i = 0, len = this.config.length; i < len; i++){
60312             if(!this.isLocked(i)){
60313                 return i;
60314             }
60315         }
60316         
60317         return this.config.length;
60318     },
60319
60320     /**
60321      * Returns the number of columns.
60322      * @return {Number}
60323      */
60324     getColumnCount : function(visibleOnly){
60325         if(visibleOnly === true){
60326             var c = 0;
60327             for(var i = 0, len = this.config.length; i < len; i++){
60328                 if(!this.isHidden(i)){
60329                     c++;
60330                 }
60331             }
60332             return c;
60333         }
60334         return this.config.length;
60335     },
60336
60337     /**
60338      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
60339      * @param {Function} fn
60340      * @param {Object} scope (optional)
60341      * @return {Array} result
60342      */
60343     getColumnsBy : function(fn, scope){
60344         var r = [];
60345         for(var i = 0, len = this.config.length; i < len; i++){
60346             var c = this.config[i];
60347             if(fn.call(scope||this, c, i) === true){
60348                 r[r.length] = c;
60349             }
60350         }
60351         return r;
60352     },
60353
60354     /**
60355      * Returns true if the specified column is sortable.
60356      * @param {Number} col The column index
60357      * @return {Boolean}
60358      */
60359     isSortable : function(col){
60360         if(typeof this.config[col].sortable == "undefined"){
60361             return this.defaultSortable;
60362         }
60363         return this.config[col].sortable;
60364     },
60365
60366     /**
60367      * Returns the rendering (formatting) function defined for the column.
60368      * @param {Number} col The column index.
60369      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
60370      */
60371     getRenderer : function(col){
60372         if(!this.config[col].renderer){
60373             return Roo.grid.ColumnModel.defaultRenderer;
60374         }
60375         return this.config[col].renderer;
60376     },
60377
60378     /**
60379      * Sets the rendering (formatting) function for a column.
60380      * @param {Number} col The column index
60381      * @param {Function} fn The function to use to process the cell's raw data
60382      * to return HTML markup for the grid view. The render function is called with
60383      * the following parameters:<ul>
60384      * <li>Data value.</li>
60385      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
60386      * <li>css A CSS style string to apply to the table cell.</li>
60387      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
60388      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
60389      * <li>Row index</li>
60390      * <li>Column index</li>
60391      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
60392      */
60393     setRenderer : function(col, fn){
60394         this.config[col].renderer = fn;
60395     },
60396
60397     /**
60398      * Returns the width for the specified column.
60399      * @param {Number} col The column index
60400      * @param (optional) {String} gridSize bootstrap width size.
60401      * @return {Number}
60402      */
60403     getColumnWidth : function(col, gridSize)
60404         {
60405                 var cfg = this.config[col];
60406                 
60407                 if (typeof(gridSize) == 'undefined') {
60408                         return cfg.width * 1 || this.defaultWidth;
60409                 }
60410                 if (gridSize === false) { // if we set it..
60411                         return cfg.width || false;
60412                 }
60413                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
60414                 
60415                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
60416                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
60417                                 continue;
60418                         }
60419                         return cfg[ sizes[i] ];
60420                 }
60421                 return 1;
60422                 
60423     },
60424
60425     /**
60426      * Sets the width for a column.
60427      * @param {Number} col The column index
60428      * @param {Number} width The new width
60429      */
60430     setColumnWidth : function(col, width, suppressEvent){
60431         this.config[col].width = width;
60432         this.totalWidth = null;
60433         if(!suppressEvent){
60434              this.fireEvent("widthchange", this, col, width);
60435         }
60436     },
60437
60438     /**
60439      * Returns the total width of all columns.
60440      * @param {Boolean} includeHidden True to include hidden column widths
60441      * @return {Number}
60442      */
60443     getTotalWidth : function(includeHidden){
60444         if(!this.totalWidth){
60445             this.totalWidth = 0;
60446             for(var i = 0, len = this.config.length; i < len; i++){
60447                 if(includeHidden || !this.isHidden(i)){
60448                     this.totalWidth += this.getColumnWidth(i);
60449                 }
60450             }
60451         }
60452         return this.totalWidth;
60453     },
60454
60455     /**
60456      * Returns the header for the specified column.
60457      * @param {Number} col The column index
60458      * @return {String}
60459      */
60460     getColumnHeader : function(col){
60461         return this.config[col].header;
60462     },
60463
60464     /**
60465      * Sets the header for a column.
60466      * @param {Number} col The column index
60467      * @param {String} header The new header
60468      */
60469     setColumnHeader : function(col, header){
60470         this.config[col].header = header;
60471         this.fireEvent("headerchange", this, col, header);
60472     },
60473
60474     /**
60475      * Returns the tooltip for the specified column.
60476      * @param {Number} col The column index
60477      * @return {String}
60478      */
60479     getColumnTooltip : function(col){
60480             return this.config[col].tooltip;
60481     },
60482     /**
60483      * Sets the tooltip for a column.
60484      * @param {Number} col The column index
60485      * @param {String} tooltip The new tooltip
60486      */
60487     setColumnTooltip : function(col, tooltip){
60488             this.config[col].tooltip = tooltip;
60489     },
60490
60491     /**
60492      * Returns the dataIndex for the specified column.
60493      * @param {Number} col The column index
60494      * @return {Number}
60495      */
60496     getDataIndex : function(col){
60497         return this.config[col].dataIndex;
60498     },
60499
60500     /**
60501      * Sets the dataIndex for a column.
60502      * @param {Number} col The column index
60503      * @param {Number} dataIndex The new dataIndex
60504      */
60505     setDataIndex : function(col, dataIndex){
60506         this.config[col].dataIndex = dataIndex;
60507     },
60508
60509     
60510     
60511     /**
60512      * Returns true if the cell is editable.
60513      * @param {Number} colIndex The column index
60514      * @param {Number} rowIndex The row index - this is nto actually used..?
60515      * @return {Boolean}
60516      */
60517     isCellEditable : function(colIndex, rowIndex){
60518         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
60519     },
60520
60521     /**
60522      * Returns the editor defined for the cell/column.
60523      * return false or null to disable editing.
60524      * @param {Number} colIndex The column index
60525      * @param {Number} rowIndex The row index
60526      * @return {Object}
60527      */
60528     getCellEditor : function(colIndex, rowIndex){
60529         return this.config[colIndex].editor;
60530     },
60531
60532     /**
60533      * Sets if a column is editable.
60534      * @param {Number} col The column index
60535      * @param {Boolean} editable True if the column is editable
60536      */
60537     setEditable : function(col, editable){
60538         this.config[col].editable = editable;
60539     },
60540
60541
60542     /**
60543      * Returns true if the column is hidden.
60544      * @param {Number} colIndex The column index
60545      * @return {Boolean}
60546      */
60547     isHidden : function(colIndex){
60548         return this.config[colIndex].hidden;
60549     },
60550
60551
60552     /**
60553      * Returns true if the column width cannot be changed
60554      */
60555     isFixed : function(colIndex){
60556         return this.config[colIndex].fixed;
60557     },
60558
60559     /**
60560      * Returns true if the column can be resized
60561      * @return {Boolean}
60562      */
60563     isResizable : function(colIndex){
60564         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
60565     },
60566     /**
60567      * Sets if a column is hidden.
60568      * @param {Number} colIndex The column index
60569      * @param {Boolean} hidden True if the column is hidden
60570      */
60571     setHidden : function(colIndex, hidden){
60572         this.config[colIndex].hidden = hidden;
60573         this.totalWidth = null;
60574         this.fireEvent("hiddenchange", this, colIndex, hidden);
60575     },
60576
60577     /**
60578      * Sets the editor for a column.
60579      * @param {Number} col The column index
60580      * @param {Object} editor The editor object
60581      */
60582     setEditor : function(col, editor){
60583         this.config[col].editor = editor;
60584     },
60585     /**
60586      * Add a column (experimental...) - defaults to adding to the end..
60587      * @param {Object} config 
60588     */
60589     addColumn : function(c)
60590     {
60591     
60592         var i = this.config.length;
60593         this.config[i] = c;
60594         
60595         if(typeof c.dataIndex == "undefined"){
60596             c.dataIndex = i;
60597         }
60598         if(typeof c.renderer == "string"){
60599             c.renderer = Roo.util.Format[c.renderer];
60600         }
60601         if(typeof c.id == "undefined"){
60602             c.id = Roo.id();
60603         }
60604         if(c.editor && c.editor.xtype){
60605             c.editor  = Roo.factory(c.editor, Roo.grid);
60606         }
60607         if(c.editor && c.editor.isFormField){
60608             c.editor = new Roo.grid.GridEditor(c.editor);
60609         }
60610         this.lookup[c.id] = c;
60611     }
60612     
60613 });
60614
60615 Roo.grid.ColumnModel.defaultRenderer = function(value)
60616 {
60617     if(typeof value == "object") {
60618         return value;
60619     }
60620         if(typeof value == "string" && value.length < 1){
60621             return "&#160;";
60622         }
60623     
60624         return String.format("{0}", value);
60625 };
60626
60627 // Alias for backwards compatibility
60628 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
60629 /*
60630  * Based on:
60631  * Ext JS Library 1.1.1
60632  * Copyright(c) 2006-2007, Ext JS, LLC.
60633  *
60634  * Originally Released Under LGPL - original licence link has changed is not relivant.
60635  *
60636  * Fork - LGPL
60637  * <script type="text/javascript">
60638  */
60639
60640 /**
60641  * @class Roo.grid.AbstractSelectionModel
60642  * @extends Roo.util.Observable
60643  * @abstract
60644  * Abstract base class for grid SelectionModels.  It provides the interface that should be
60645  * implemented by descendant classes.  This class should not be directly instantiated.
60646  * @constructor
60647  */
60648 Roo.grid.AbstractSelectionModel = function(){
60649     this.locked = false;
60650     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
60651 };
60652
60653 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
60654     /** @ignore Called by the grid automatically. Do not call directly. */
60655     init : function(grid){
60656         this.grid = grid;
60657         this.initEvents();
60658     },
60659
60660     /**
60661      * Locks the selections.
60662      */
60663     lock : function(){
60664         this.locked = true;
60665     },
60666
60667     /**
60668      * Unlocks the selections.
60669      */
60670     unlock : function(){
60671         this.locked = false;
60672     },
60673
60674     /**
60675      * Returns true if the selections are locked.
60676      * @return {Boolean}
60677      */
60678     isLocked : function(){
60679         return this.locked;
60680     }
60681 });/*
60682  * Based on:
60683  * Ext JS Library 1.1.1
60684  * Copyright(c) 2006-2007, Ext JS, LLC.
60685  *
60686  * Originally Released Under LGPL - original licence link has changed is not relivant.
60687  *
60688  * Fork - LGPL
60689  * <script type="text/javascript">
60690  */
60691 /**
60692  * @extends Roo.grid.AbstractSelectionModel
60693  * @class Roo.grid.RowSelectionModel
60694  * The default SelectionModel used by {@link Roo.grid.Grid}.
60695  * It supports multiple selections and keyboard selection/navigation. 
60696  * @constructor
60697  * @param {Object} config
60698  */
60699 Roo.grid.RowSelectionModel = function(config){
60700     Roo.apply(this, config);
60701     this.selections = new Roo.util.MixedCollection(false, function(o){
60702         return o.id;
60703     });
60704
60705     this.last = false;
60706     this.lastActive = false;
60707
60708     this.addEvents({
60709         /**
60710         * @event selectionchange
60711         * Fires when the selection changes
60712         * @param {SelectionModel} this
60713         */
60714        "selectionchange" : true,
60715        /**
60716         * @event afterselectionchange
60717         * Fires after the selection changes (eg. by key press or clicking)
60718         * @param {SelectionModel} this
60719         */
60720        "afterselectionchange" : true,
60721        /**
60722         * @event beforerowselect
60723         * Fires when a row is selected being selected, return false to cancel.
60724         * @param {SelectionModel} this
60725         * @param {Number} rowIndex The selected index
60726         * @param {Boolean} keepExisting False if other selections will be cleared
60727         */
60728        "beforerowselect" : true,
60729        /**
60730         * @event rowselect
60731         * Fires when a row is selected.
60732         * @param {SelectionModel} this
60733         * @param {Number} rowIndex The selected index
60734         * @param {Roo.data.Record} r The record
60735         */
60736        "rowselect" : true,
60737        /**
60738         * @event rowdeselect
60739         * Fires when a row is deselected.
60740         * @param {SelectionModel} this
60741         * @param {Number} rowIndex The selected index
60742         */
60743         "rowdeselect" : true
60744     });
60745     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
60746     this.locked = false;
60747 };
60748
60749 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
60750     /**
60751      * @cfg {Boolean} singleSelect
60752      * True to allow selection of only one row at a time (defaults to false)
60753      */
60754     singleSelect : false,
60755
60756     // private
60757     initEvents : function(){
60758
60759         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
60760             this.grid.on("mousedown", this.handleMouseDown, this);
60761         }else{ // allow click to work like normal
60762             this.grid.on("rowclick", this.handleDragableRowClick, this);
60763         }
60764         // bootstrap does not have a view..
60765         var view = this.grid.view ? this.grid.view : this.grid;
60766         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
60767             "up" : function(e){
60768                 if(!e.shiftKey){
60769                     this.selectPrevious(e.shiftKey);
60770                 }else if(this.last !== false && this.lastActive !== false){
60771                     var last = this.last;
60772                     this.selectRange(this.last,  this.lastActive-1);
60773                     view.focusRow(this.lastActive);
60774                     if(last !== false){
60775                         this.last = last;
60776                     }
60777                 }else{
60778                     this.selectFirstRow();
60779                 }
60780                 this.fireEvent("afterselectionchange", this);
60781             },
60782             "down" : function(e){
60783                 if(!e.shiftKey){
60784                     this.selectNext(e.shiftKey);
60785                 }else if(this.last !== false && this.lastActive !== false){
60786                     var last = this.last;
60787                     this.selectRange(this.last,  this.lastActive+1);
60788                     view.focusRow(this.lastActive);
60789                     if(last !== false){
60790                         this.last = last;
60791                     }
60792                 }else{
60793                     this.selectFirstRow();
60794                 }
60795                 this.fireEvent("afterselectionchange", this);
60796             },
60797             scope: this
60798         });
60799
60800          
60801         view.on("refresh", this.onRefresh, this);
60802         view.on("rowupdated", this.onRowUpdated, this);
60803         view.on("rowremoved", this.onRemove, this);
60804     },
60805
60806     // private
60807     onRefresh : function(){
60808         var ds = this.grid.ds, i, v = this.grid.view;
60809         var s = this.selections;
60810         s.each(function(r){
60811             if((i = ds.indexOfId(r.id)) != -1){
60812                 v.onRowSelect(i);
60813                 s.add(ds.getAt(i)); // updating the selection relate data
60814             }else{
60815                 s.remove(r);
60816             }
60817         });
60818     },
60819
60820     // private
60821     onRemove : function(v, index, r){
60822         this.selections.remove(r);
60823     },
60824
60825     // private
60826     onRowUpdated : function(v, index, r){
60827         if(this.isSelected(r)){
60828             v.onRowSelect(index);
60829         }
60830     },
60831
60832     /**
60833      * Select records.
60834      * @param {Array} records The records to select
60835      * @param {Boolean} keepExisting (optional) True to keep existing selections
60836      */
60837     selectRecords : function(records, keepExisting){
60838         if(!keepExisting){
60839             this.clearSelections();
60840         }
60841         var ds = this.grid.ds;
60842         for(var i = 0, len = records.length; i < len; i++){
60843             this.selectRow(ds.indexOf(records[i]), true);
60844         }
60845     },
60846
60847     /**
60848      * Gets the number of selected rows.
60849      * @return {Number}
60850      */
60851     getCount : function(){
60852         return this.selections.length;
60853     },
60854
60855     /**
60856      * Selects the first row in the grid.
60857      */
60858     selectFirstRow : function(){
60859         this.selectRow(0);
60860     },
60861
60862     /**
60863      * Select the last row.
60864      * @param {Boolean} keepExisting (optional) True to keep existing selections
60865      */
60866     selectLastRow : function(keepExisting){
60867         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
60868     },
60869
60870     /**
60871      * Selects the row immediately following the last selected row.
60872      * @param {Boolean} keepExisting (optional) True to keep existing selections
60873      */
60874     selectNext : function(keepExisting){
60875         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
60876             this.selectRow(this.last+1, keepExisting);
60877             var view = this.grid.view ? this.grid.view : this.grid;
60878             view.focusRow(this.last);
60879         }
60880     },
60881
60882     /**
60883      * Selects the row that precedes the last selected row.
60884      * @param {Boolean} keepExisting (optional) True to keep existing selections
60885      */
60886     selectPrevious : function(keepExisting){
60887         if(this.last){
60888             this.selectRow(this.last-1, keepExisting);
60889             var view = this.grid.view ? this.grid.view : this.grid;
60890             view.focusRow(this.last);
60891         }
60892     },
60893
60894     /**
60895      * Returns the selected records
60896      * @return {Array} Array of selected records
60897      */
60898     getSelections : function(){
60899         return [].concat(this.selections.items);
60900     },
60901
60902     /**
60903      * Returns the first selected record.
60904      * @return {Record}
60905      */
60906     getSelected : function(){
60907         return this.selections.itemAt(0);
60908     },
60909
60910
60911     /**
60912      * Clears all selections.
60913      */
60914     clearSelections : function(fast){
60915         if(this.locked) {
60916             return;
60917         }
60918         if(fast !== true){
60919             var ds = this.grid.ds;
60920             var s = this.selections;
60921             s.each(function(r){
60922                 this.deselectRow(ds.indexOfId(r.id));
60923             }, this);
60924             s.clear();
60925         }else{
60926             this.selections.clear();
60927         }
60928         this.last = false;
60929     },
60930
60931
60932     /**
60933      * Selects all rows.
60934      */
60935     selectAll : function(){
60936         if(this.locked) {
60937             return;
60938         }
60939         this.selections.clear();
60940         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
60941             this.selectRow(i, true);
60942         }
60943     },
60944
60945     /**
60946      * Returns True if there is a selection.
60947      * @return {Boolean}
60948      */
60949     hasSelection : function(){
60950         return this.selections.length > 0;
60951     },
60952
60953     /**
60954      * Returns True if the specified row is selected.
60955      * @param {Number/Record} record The record or index of the record to check
60956      * @return {Boolean}
60957      */
60958     isSelected : function(index){
60959         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
60960         return (r && this.selections.key(r.id) ? true : false);
60961     },
60962
60963     /**
60964      * Returns True if the specified record id is selected.
60965      * @param {String} id The id of record to check
60966      * @return {Boolean}
60967      */
60968     isIdSelected : function(id){
60969         return (this.selections.key(id) ? true : false);
60970     },
60971
60972     // private
60973     handleMouseDown : function(e, t)
60974     {
60975         var view = this.grid.view ? this.grid.view : this.grid;
60976         var rowIndex;
60977         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
60978             return;
60979         };
60980         if(e.shiftKey && this.last !== false){
60981             var last = this.last;
60982             this.selectRange(last, rowIndex, e.ctrlKey);
60983             this.last = last; // reset the last
60984             view.focusRow(rowIndex);
60985         }else{
60986             var isSelected = this.isSelected(rowIndex);
60987             if(e.button !== 0 && isSelected){
60988                 view.focusRow(rowIndex);
60989             }else if(e.ctrlKey && isSelected){
60990                 this.deselectRow(rowIndex);
60991             }else if(!isSelected){
60992                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
60993                 view.focusRow(rowIndex);
60994             }
60995         }
60996         this.fireEvent("afterselectionchange", this);
60997     },
60998     // private
60999     handleDragableRowClick :  function(grid, rowIndex, e) 
61000     {
61001         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61002             this.selectRow(rowIndex, false);
61003             var view = this.grid.view ? this.grid.view : this.grid;
61004             view.focusRow(rowIndex);
61005              this.fireEvent("afterselectionchange", this);
61006         }
61007     },
61008     
61009     /**
61010      * Selects multiple rows.
61011      * @param {Array} rows Array of the indexes of the row to select
61012      * @param {Boolean} keepExisting (optional) True to keep existing selections
61013      */
61014     selectRows : function(rows, keepExisting){
61015         if(!keepExisting){
61016             this.clearSelections();
61017         }
61018         for(var i = 0, len = rows.length; i < len; i++){
61019             this.selectRow(rows[i], true);
61020         }
61021     },
61022
61023     /**
61024      * Selects a range of rows. All rows in between startRow and endRow are also selected.
61025      * @param {Number} startRow The index of the first row in the range
61026      * @param {Number} endRow The index of the last row in the range
61027      * @param {Boolean} keepExisting (optional) True to retain existing selections
61028      */
61029     selectRange : function(startRow, endRow, keepExisting){
61030         if(this.locked) {
61031             return;
61032         }
61033         if(!keepExisting){
61034             this.clearSelections();
61035         }
61036         if(startRow <= endRow){
61037             for(var i = startRow; i <= endRow; i++){
61038                 this.selectRow(i, true);
61039             }
61040         }else{
61041             for(var i = startRow; i >= endRow; i--){
61042                 this.selectRow(i, true);
61043             }
61044         }
61045     },
61046
61047     /**
61048      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
61049      * @param {Number} startRow The index of the first row in the range
61050      * @param {Number} endRow The index of the last row in the range
61051      */
61052     deselectRange : function(startRow, endRow, preventViewNotify){
61053         if(this.locked) {
61054             return;
61055         }
61056         for(var i = startRow; i <= endRow; i++){
61057             this.deselectRow(i, preventViewNotify);
61058         }
61059     },
61060
61061     /**
61062      * Selects a row.
61063      * @param {Number} row The index of the row to select
61064      * @param {Boolean} keepExisting (optional) True to keep existing selections
61065      */
61066     selectRow : function(index, keepExisting, preventViewNotify){
61067         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
61068             return;
61069         }
61070         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
61071             if(!keepExisting || this.singleSelect){
61072                 this.clearSelections();
61073             }
61074             var r = this.grid.ds.getAt(index);
61075             this.selections.add(r);
61076             this.last = this.lastActive = index;
61077             if(!preventViewNotify){
61078                 var view = this.grid.view ? this.grid.view : this.grid;
61079                 view.onRowSelect(index);
61080             }
61081             this.fireEvent("rowselect", this, index, r);
61082             this.fireEvent("selectionchange", this);
61083         }
61084     },
61085
61086     /**
61087      * Deselects a row.
61088      * @param {Number} row The index of the row to deselect
61089      */
61090     deselectRow : function(index, preventViewNotify){
61091         if(this.locked) {
61092             return;
61093         }
61094         if(this.last == index){
61095             this.last = false;
61096         }
61097         if(this.lastActive == index){
61098             this.lastActive = false;
61099         }
61100         var r = this.grid.ds.getAt(index);
61101         this.selections.remove(r);
61102         if(!preventViewNotify){
61103             var view = this.grid.view ? this.grid.view : this.grid;
61104             view.onRowDeselect(index);
61105         }
61106         this.fireEvent("rowdeselect", this, index);
61107         this.fireEvent("selectionchange", this);
61108     },
61109
61110     // private
61111     restoreLast : function(){
61112         if(this._last){
61113             this.last = this._last;
61114         }
61115     },
61116
61117     // private
61118     acceptsNav : function(row, col, cm){
61119         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61120     },
61121
61122     // private
61123     onEditorKey : function(field, e){
61124         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
61125         if(k == e.TAB){
61126             e.stopEvent();
61127             ed.completeEdit();
61128             if(e.shiftKey){
61129                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61130             }else{
61131                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61132             }
61133         }else if(k == e.ENTER && !e.ctrlKey){
61134             e.stopEvent();
61135             ed.completeEdit();
61136             if(e.shiftKey){
61137                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
61138             }else{
61139                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
61140             }
61141         }else if(k == e.ESC){
61142             ed.cancelEdit();
61143         }
61144         if(newCell){
61145             g.startEditing(newCell[0], newCell[1]);
61146         }
61147     }
61148 });/*
61149  * Based on:
61150  * Ext JS Library 1.1.1
61151  * Copyright(c) 2006-2007, Ext JS, LLC.
61152  *
61153  * Originally Released Under LGPL - original licence link has changed is not relivant.
61154  *
61155  * Fork - LGPL
61156  * <script type="text/javascript">
61157  */
61158 /**
61159  * @class Roo.grid.CellSelectionModel
61160  * @extends Roo.grid.AbstractSelectionModel
61161  * This class provides the basic implementation for cell selection in a grid.
61162  * @constructor
61163  * @param {Object} config The object containing the configuration of this model.
61164  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
61165  */
61166 Roo.grid.CellSelectionModel = function(config){
61167     Roo.apply(this, config);
61168
61169     this.selection = null;
61170
61171     this.addEvents({
61172         /**
61173              * @event beforerowselect
61174              * Fires before a cell is selected.
61175              * @param {SelectionModel} this
61176              * @param {Number} rowIndex The selected row index
61177              * @param {Number} colIndex The selected cell index
61178              */
61179             "beforecellselect" : true,
61180         /**
61181              * @event cellselect
61182              * Fires when a cell is selected.
61183              * @param {SelectionModel} this
61184              * @param {Number} rowIndex The selected row index
61185              * @param {Number} colIndex The selected cell index
61186              */
61187             "cellselect" : true,
61188         /**
61189              * @event selectionchange
61190              * Fires when the active selection changes.
61191              * @param {SelectionModel} this
61192              * @param {Object} selection null for no selection or an object (o) with two properties
61193                 <ul>
61194                 <li>o.record: the record object for the row the selection is in</li>
61195                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
61196                 </ul>
61197              */
61198             "selectionchange" : true,
61199         /**
61200              * @event tabend
61201              * Fires when the tab (or enter) was pressed on the last editable cell
61202              * You can use this to trigger add new row.
61203              * @param {SelectionModel} this
61204              */
61205             "tabend" : true,
61206          /**
61207              * @event beforeeditnext
61208              * Fires before the next editable sell is made active
61209              * You can use this to skip to another cell or fire the tabend
61210              *    if you set cell to false
61211              * @param {Object} eventdata object : { cell : [ row, col ] } 
61212              */
61213             "beforeeditnext" : true
61214     });
61215     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
61216 };
61217
61218 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
61219     
61220     enter_is_tab: false,
61221
61222     /** @ignore */
61223     initEvents : function(){
61224         this.grid.on("mousedown", this.handleMouseDown, this);
61225         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
61226         var view = this.grid.view;
61227         view.on("refresh", this.onViewChange, this);
61228         view.on("rowupdated", this.onRowUpdated, this);
61229         view.on("beforerowremoved", this.clearSelections, this);
61230         view.on("beforerowsinserted", this.clearSelections, this);
61231         if(this.grid.isEditor){
61232             this.grid.on("beforeedit", this.beforeEdit,  this);
61233         }
61234     },
61235
61236         //private
61237     beforeEdit : function(e){
61238         this.select(e.row, e.column, false, true, e.record);
61239     },
61240
61241         //private
61242     onRowUpdated : function(v, index, r){
61243         if(this.selection && this.selection.record == r){
61244             v.onCellSelect(index, this.selection.cell[1]);
61245         }
61246     },
61247
61248         //private
61249     onViewChange : function(){
61250         this.clearSelections(true);
61251     },
61252
61253         /**
61254          * Returns the currently selected cell,.
61255          * @return {Array} The selected cell (row, column) or null if none selected.
61256          */
61257     getSelectedCell : function(){
61258         return this.selection ? this.selection.cell : null;
61259     },
61260
61261     /**
61262      * Clears all selections.
61263      * @param {Boolean} true to prevent the gridview from being notified about the change.
61264      */
61265     clearSelections : function(preventNotify){
61266         var s = this.selection;
61267         if(s){
61268             if(preventNotify !== true){
61269                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
61270             }
61271             this.selection = null;
61272             this.fireEvent("selectionchange", this, null);
61273         }
61274     },
61275
61276     /**
61277      * Returns true if there is a selection.
61278      * @return {Boolean}
61279      */
61280     hasSelection : function(){
61281         return this.selection ? true : false;
61282     },
61283
61284     /** @ignore */
61285     handleMouseDown : function(e, t){
61286         var v = this.grid.getView();
61287         if(this.isLocked()){
61288             return;
61289         };
61290         var row = v.findRowIndex(t);
61291         var cell = v.findCellIndex(t);
61292         if(row !== false && cell !== false){
61293             this.select(row, cell);
61294         }
61295     },
61296
61297     /**
61298      * Selects a cell.
61299      * @param {Number} rowIndex
61300      * @param {Number} collIndex
61301      */
61302     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
61303         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
61304             this.clearSelections();
61305             r = r || this.grid.dataSource.getAt(rowIndex);
61306             this.selection = {
61307                 record : r,
61308                 cell : [rowIndex, colIndex]
61309             };
61310             if(!preventViewNotify){
61311                 var v = this.grid.getView();
61312                 v.onCellSelect(rowIndex, colIndex);
61313                 if(preventFocus !== true){
61314                     v.focusCell(rowIndex, colIndex);
61315                 }
61316             }
61317             this.fireEvent("cellselect", this, rowIndex, colIndex);
61318             this.fireEvent("selectionchange", this, this.selection);
61319         }
61320     },
61321
61322         //private
61323     isSelectable : function(rowIndex, colIndex, cm){
61324         return !cm.isHidden(colIndex);
61325     },
61326
61327     /** @ignore */
61328     handleKeyDown : function(e){
61329         //Roo.log('Cell Sel Model handleKeyDown');
61330         if(!e.isNavKeyPress()){
61331             return;
61332         }
61333         var g = this.grid, s = this.selection;
61334         if(!s){
61335             e.stopEvent();
61336             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
61337             if(cell){
61338                 this.select(cell[0], cell[1]);
61339             }
61340             return;
61341         }
61342         var sm = this;
61343         var walk = function(row, col, step){
61344             return g.walkCells(row, col, step, sm.isSelectable,  sm);
61345         };
61346         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
61347         var newCell;
61348
61349       
61350
61351         switch(k){
61352             case e.TAB:
61353                 // handled by onEditorKey
61354                 if (g.isEditor && g.editing) {
61355                     return;
61356                 }
61357                 if(e.shiftKey) {
61358                     newCell = walk(r, c-1, -1);
61359                 } else {
61360                     newCell = walk(r, c+1, 1);
61361                 }
61362                 break;
61363             
61364             case e.DOWN:
61365                newCell = walk(r+1, c, 1);
61366                 break;
61367             
61368             case e.UP:
61369                 newCell = walk(r-1, c, -1);
61370                 break;
61371             
61372             case e.RIGHT:
61373                 newCell = walk(r, c+1, 1);
61374                 break;
61375             
61376             case e.LEFT:
61377                 newCell = walk(r, c-1, -1);
61378                 break;
61379             
61380             case e.ENTER:
61381                 
61382                 if(g.isEditor && !g.editing){
61383                    g.startEditing(r, c);
61384                    e.stopEvent();
61385                    return;
61386                 }
61387                 
61388                 
61389              break;
61390         };
61391         if(newCell){
61392             this.select(newCell[0], newCell[1]);
61393             e.stopEvent();
61394             
61395         }
61396     },
61397
61398     acceptsNav : function(row, col, cm){
61399         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61400     },
61401     /**
61402      * Selects a cell.
61403      * @param {Number} field (not used) - as it's normally used as a listener
61404      * @param {Number} e - event - fake it by using
61405      *
61406      * var e = Roo.EventObjectImpl.prototype;
61407      * e.keyCode = e.TAB
61408      *
61409      * 
61410      */
61411     onEditorKey : function(field, e){
61412         
61413         var k = e.getKey(),
61414             newCell,
61415             g = this.grid,
61416             ed = g.activeEditor,
61417             forward = false;
61418         ///Roo.log('onEditorKey' + k);
61419         
61420         
61421         if (this.enter_is_tab && k == e.ENTER) {
61422             k = e.TAB;
61423         }
61424         
61425         if(k == e.TAB){
61426             if(e.shiftKey){
61427                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61428             }else{
61429                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61430                 forward = true;
61431             }
61432             
61433             e.stopEvent();
61434             
61435         } else if(k == e.ENTER &&  !e.ctrlKey){
61436             ed.completeEdit();
61437             e.stopEvent();
61438             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61439         
61440                 } else if(k == e.ESC){
61441             ed.cancelEdit();
61442         }
61443                 
61444         if (newCell) {
61445             var ecall = { cell : newCell, forward : forward };
61446             this.fireEvent('beforeeditnext', ecall );
61447             newCell = ecall.cell;
61448                         forward = ecall.forward;
61449         }
61450                 
61451         if(newCell){
61452             //Roo.log('next cell after edit');
61453             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
61454         } else if (forward) {
61455             // tabbed past last
61456             this.fireEvent.defer(100, this, ['tabend',this]);
61457         }
61458     }
61459 });/*
61460  * Based on:
61461  * Ext JS Library 1.1.1
61462  * Copyright(c) 2006-2007, Ext JS, LLC.
61463  *
61464  * Originally Released Under LGPL - original licence link has changed is not relivant.
61465  *
61466  * Fork - LGPL
61467  * <script type="text/javascript">
61468  */
61469  
61470 /**
61471  * @class Roo.grid.EditorGrid
61472  * @extends Roo.grid.Grid
61473  * Class for creating and editable grid.
61474  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
61475  * The container MUST have some type of size defined for the grid to fill. The container will be 
61476  * automatically set to position relative if it isn't already.
61477  * @param {Object} dataSource The data model to bind to
61478  * @param {Object} colModel The column model with info about this grid's columns
61479  */
61480 Roo.grid.EditorGrid = function(container, config){
61481     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
61482     this.getGridEl().addClass("xedit-grid");
61483
61484     if(!this.selModel){
61485         this.selModel = new Roo.grid.CellSelectionModel();
61486     }
61487
61488     this.activeEditor = null;
61489
61490         this.addEvents({
61491             /**
61492              * @event beforeedit
61493              * Fires before cell editing is triggered. The edit event object has the following properties <br />
61494              * <ul style="padding:5px;padding-left:16px;">
61495              * <li>grid - This grid</li>
61496              * <li>record - The record being edited</li>
61497              * <li>field - The field name being edited</li>
61498              * <li>value - The value for the field being edited.</li>
61499              * <li>row - The grid row index</li>
61500              * <li>column - The grid column index</li>
61501              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
61502              * </ul>
61503              * @param {Object} e An edit event (see above for description)
61504              */
61505             "beforeedit" : true,
61506             /**
61507              * @event afteredit
61508              * Fires after a cell is edited. <br />
61509              * <ul style="padding:5px;padding-left:16px;">
61510              * <li>grid - This grid</li>
61511              * <li>record - The record being edited</li>
61512              * <li>field - The field name being edited</li>
61513              * <li>value - The value being set</li>
61514              * <li>originalValue - The original value for the field, before the edit.</li>
61515              * <li>row - The grid row index</li>
61516              * <li>column - The grid column index</li>
61517              * </ul>
61518              * @param {Object} e An edit event (see above for description)
61519              */
61520             "afteredit" : true,
61521             /**
61522              * @event validateedit
61523              * Fires after a cell is edited, but before the value is set in the record. 
61524          * You can use this to modify the value being set in the field, Return false
61525              * to cancel the change. The edit event object has the following properties <br />
61526              * <ul style="padding:5px;padding-left:16px;">
61527          * <li>editor - This editor</li>
61528              * <li>grid - This grid</li>
61529              * <li>record - The record being edited</li>
61530              * <li>field - The field name being edited</li>
61531              * <li>value - The value being set</li>
61532              * <li>originalValue - The original value for the field, before the edit.</li>
61533              * <li>row - The grid row index</li>
61534              * <li>column - The grid column index</li>
61535              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
61536              * </ul>
61537              * @param {Object} e An edit event (see above for description)
61538              */
61539             "validateedit" : true
61540         });
61541     this.on("bodyscroll", this.stopEditing,  this);
61542     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
61543 };
61544
61545 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
61546     /**
61547      * @cfg {Number} clicksToEdit
61548      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
61549      */
61550     clicksToEdit: 2,
61551
61552     // private
61553     isEditor : true,
61554     // private
61555     trackMouseOver: false, // causes very odd FF errors
61556
61557     onCellDblClick : function(g, row, col){
61558         this.startEditing(row, col);
61559     },
61560
61561     onEditComplete : function(ed, value, startValue){
61562         this.editing = false;
61563         this.activeEditor = null;
61564         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
61565         var r = ed.record;
61566         var field = this.colModel.getDataIndex(ed.col);
61567         var e = {
61568             grid: this,
61569             record: r,
61570             field: field,
61571             originalValue: startValue,
61572             value: value,
61573             row: ed.row,
61574             column: ed.col,
61575             cancel:false,
61576             editor: ed
61577         };
61578         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
61579         cell.show();
61580           
61581         if(String(value) !== String(startValue)){
61582             
61583             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
61584                 r.set(field, e.value);
61585                 // if we are dealing with a combo box..
61586                 // then we also set the 'name' colum to be the displayField
61587                 if (ed.field.displayField && ed.field.name) {
61588                     r.set(ed.field.name, ed.field.el.dom.value);
61589                 }
61590                 
61591                 delete e.cancel; //?? why!!!
61592                 this.fireEvent("afteredit", e);
61593             }
61594         } else {
61595             this.fireEvent("afteredit", e); // always fire it!
61596         }
61597         this.view.focusCell(ed.row, ed.col);
61598     },
61599
61600     /**
61601      * Starts editing the specified for the specified row/column
61602      * @param {Number} rowIndex
61603      * @param {Number} colIndex
61604      */
61605     startEditing : function(row, col){
61606         this.stopEditing();
61607         if(this.colModel.isCellEditable(col, row)){
61608             this.view.ensureVisible(row, col, true);
61609           
61610             var r = this.dataSource.getAt(row);
61611             var field = this.colModel.getDataIndex(col);
61612             var cell = Roo.get(this.view.getCell(row,col));
61613             var e = {
61614                 grid: this,
61615                 record: r,
61616                 field: field,
61617                 value: r.data[field],
61618                 row: row,
61619                 column: col,
61620                 cancel:false 
61621             };
61622             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
61623                 this.editing = true;
61624                 var ed = this.colModel.getCellEditor(col, row);
61625                 
61626                 if (!ed) {
61627                     return;
61628                 }
61629                 if(!ed.rendered){
61630                     ed.render(ed.parentEl || document.body);
61631                 }
61632                 ed.field.reset();
61633                
61634                 cell.hide();
61635                 
61636                 (function(){ // complex but required for focus issues in safari, ie and opera
61637                     ed.row = row;
61638                     ed.col = col;
61639                     ed.record = r;
61640                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
61641                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
61642                     this.activeEditor = ed;
61643                     var v = r.data[field];
61644                     ed.startEdit(this.view.getCell(row, col), v);
61645                     // combo's with 'displayField and name set
61646                     if (ed.field.displayField && ed.field.name) {
61647                         ed.field.el.dom.value = r.data[ed.field.name];
61648                     }
61649                     
61650                     
61651                 }).defer(50, this);
61652             }
61653         }
61654     },
61655         
61656     /**
61657      * Stops any active editing
61658      */
61659     stopEditing : function(){
61660         if(this.activeEditor){
61661             this.activeEditor.completeEdit();
61662         }
61663         this.activeEditor = null;
61664     },
61665         
61666          /**
61667      * Called to get grid's drag proxy text, by default returns this.ddText.
61668      * @return {String}
61669      */
61670     getDragDropText : function(){
61671         var count = this.selModel.getSelectedCell() ? 1 : 0;
61672         return String.format(this.ddText, count, count == 1 ? '' : 's');
61673     }
61674         
61675 });/*
61676  * Based on:
61677  * Ext JS Library 1.1.1
61678  * Copyright(c) 2006-2007, Ext JS, LLC.
61679  *
61680  * Originally Released Under LGPL - original licence link has changed is not relivant.
61681  *
61682  * Fork - LGPL
61683  * <script type="text/javascript">
61684  */
61685
61686 // private - not really -- you end up using it !
61687 // This is a support class used internally by the Grid components
61688
61689 /**
61690  * @class Roo.grid.GridEditor
61691  * @extends Roo.Editor
61692  * Class for creating and editable grid elements.
61693  * @param {Object} config any settings (must include field)
61694  */
61695 Roo.grid.GridEditor = function(field, config){
61696     if (!config && field.field) {
61697         config = field;
61698         field = Roo.factory(config.field, Roo.form);
61699     }
61700     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
61701     field.monitorTab = false;
61702 };
61703
61704 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
61705     
61706     /**
61707      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
61708      */
61709     
61710     alignment: "tl-tl",
61711     autoSize: "width",
61712     hideEl : false,
61713     cls: "x-small-editor x-grid-editor",
61714     shim:false,
61715     shadow:"frame"
61716 });/*
61717  * Based on:
61718  * Ext JS Library 1.1.1
61719  * Copyright(c) 2006-2007, Ext JS, LLC.
61720  *
61721  * Originally Released Under LGPL - original licence link has changed is not relivant.
61722  *
61723  * Fork - LGPL
61724  * <script type="text/javascript">
61725  */
61726   
61727
61728   
61729 Roo.grid.PropertyRecord = Roo.data.Record.create([
61730     {name:'name',type:'string'},  'value'
61731 ]);
61732
61733
61734 Roo.grid.PropertyStore = function(grid, source){
61735     this.grid = grid;
61736     this.store = new Roo.data.Store({
61737         recordType : Roo.grid.PropertyRecord
61738     });
61739     this.store.on('update', this.onUpdate,  this);
61740     if(source){
61741         this.setSource(source);
61742     }
61743     Roo.grid.PropertyStore.superclass.constructor.call(this);
61744 };
61745
61746
61747
61748 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
61749     setSource : function(o){
61750         this.source = o;
61751         this.store.removeAll();
61752         var data = [];
61753         for(var k in o){
61754             if(this.isEditableValue(o[k])){
61755                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
61756             }
61757         }
61758         this.store.loadRecords({records: data}, {}, true);
61759     },
61760
61761     onUpdate : function(ds, record, type){
61762         if(type == Roo.data.Record.EDIT){
61763             var v = record.data['value'];
61764             var oldValue = record.modified['value'];
61765             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
61766                 this.source[record.id] = v;
61767                 record.commit();
61768                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
61769             }else{
61770                 record.reject();
61771             }
61772         }
61773     },
61774
61775     getProperty : function(row){
61776        return this.store.getAt(row);
61777     },
61778
61779     isEditableValue: function(val){
61780         if(val && val instanceof Date){
61781             return true;
61782         }else if(typeof val == 'object' || typeof val == 'function'){
61783             return false;
61784         }
61785         return true;
61786     },
61787
61788     setValue : function(prop, value){
61789         this.source[prop] = value;
61790         this.store.getById(prop).set('value', value);
61791     },
61792
61793     getSource : function(){
61794         return this.source;
61795     }
61796 });
61797
61798 Roo.grid.PropertyColumnModel = function(grid, store){
61799     this.grid = grid;
61800     var g = Roo.grid;
61801     g.PropertyColumnModel.superclass.constructor.call(this, [
61802         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
61803         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
61804     ]);
61805     this.store = store;
61806     this.bselect = Roo.DomHelper.append(document.body, {
61807         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
61808             {tag: 'option', value: 'true', html: 'true'},
61809             {tag: 'option', value: 'false', html: 'false'}
61810         ]
61811     });
61812     Roo.id(this.bselect);
61813     var f = Roo.form;
61814     this.editors = {
61815         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
61816         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
61817         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
61818         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
61819         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
61820     };
61821     this.renderCellDelegate = this.renderCell.createDelegate(this);
61822     this.renderPropDelegate = this.renderProp.createDelegate(this);
61823 };
61824
61825 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
61826     
61827     
61828     nameText : 'Name',
61829     valueText : 'Value',
61830     
61831     dateFormat : 'm/j/Y',
61832     
61833     
61834     renderDate : function(dateVal){
61835         return dateVal.dateFormat(this.dateFormat);
61836     },
61837
61838     renderBool : function(bVal){
61839         return bVal ? 'true' : 'false';
61840     },
61841
61842     isCellEditable : function(colIndex, rowIndex){
61843         return colIndex == 1;
61844     },
61845
61846     getRenderer : function(col){
61847         return col == 1 ?
61848             this.renderCellDelegate : this.renderPropDelegate;
61849     },
61850
61851     renderProp : function(v){
61852         return this.getPropertyName(v);
61853     },
61854
61855     renderCell : function(val){
61856         var rv = val;
61857         if(val instanceof Date){
61858             rv = this.renderDate(val);
61859         }else if(typeof val == 'boolean'){
61860             rv = this.renderBool(val);
61861         }
61862         return Roo.util.Format.htmlEncode(rv);
61863     },
61864
61865     getPropertyName : function(name){
61866         var pn = this.grid.propertyNames;
61867         return pn && pn[name] ? pn[name] : name;
61868     },
61869
61870     getCellEditor : function(colIndex, rowIndex){
61871         var p = this.store.getProperty(rowIndex);
61872         var n = p.data['name'], val = p.data['value'];
61873         
61874         if(typeof(this.grid.customEditors[n]) == 'string'){
61875             return this.editors[this.grid.customEditors[n]];
61876         }
61877         if(typeof(this.grid.customEditors[n]) != 'undefined'){
61878             return this.grid.customEditors[n];
61879         }
61880         if(val instanceof Date){
61881             return this.editors['date'];
61882         }else if(typeof val == 'number'){
61883             return this.editors['number'];
61884         }else if(typeof val == 'boolean'){
61885             return this.editors['boolean'];
61886         }else{
61887             return this.editors['string'];
61888         }
61889     }
61890 });
61891
61892 /**
61893  * @class Roo.grid.PropertyGrid
61894  * @extends Roo.grid.EditorGrid
61895  * This class represents the  interface of a component based property grid control.
61896  * <br><br>Usage:<pre><code>
61897  var grid = new Roo.grid.PropertyGrid("my-container-id", {
61898       
61899  });
61900  // set any options
61901  grid.render();
61902  * </code></pre>
61903   
61904  * @constructor
61905  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
61906  * The container MUST have some type of size defined for the grid to fill. The container will be
61907  * automatically set to position relative if it isn't already.
61908  * @param {Object} config A config object that sets properties on this grid.
61909  */
61910 Roo.grid.PropertyGrid = function(container, config){
61911     config = config || {};
61912     var store = new Roo.grid.PropertyStore(this);
61913     this.store = store;
61914     var cm = new Roo.grid.PropertyColumnModel(this, store);
61915     store.store.sort('name', 'ASC');
61916     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
61917         ds: store.store,
61918         cm: cm,
61919         enableColLock:false,
61920         enableColumnMove:false,
61921         stripeRows:false,
61922         trackMouseOver: false,
61923         clicksToEdit:1
61924     }, config));
61925     this.getGridEl().addClass('x-props-grid');
61926     this.lastEditRow = null;
61927     this.on('columnresize', this.onColumnResize, this);
61928     this.addEvents({
61929          /**
61930              * @event beforepropertychange
61931              * Fires before a property changes (return false to stop?)
61932              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61933              * @param {String} id Record Id
61934              * @param {String} newval New Value
61935          * @param {String} oldval Old Value
61936              */
61937         "beforepropertychange": true,
61938         /**
61939              * @event propertychange
61940              * Fires after a property changes
61941              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
61942              * @param {String} id Record Id
61943              * @param {String} newval New Value
61944          * @param {String} oldval Old Value
61945              */
61946         "propertychange": true
61947     });
61948     this.customEditors = this.customEditors || {};
61949 };
61950 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
61951     
61952      /**
61953      * @cfg {Object} customEditors map of colnames=> custom editors.
61954      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
61955      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
61956      * false disables editing of the field.
61957          */
61958     
61959       /**
61960      * @cfg {Object} propertyNames map of property Names to their displayed value
61961          */
61962     
61963     render : function(){
61964         Roo.grid.PropertyGrid.superclass.render.call(this);
61965         this.autoSize.defer(100, this);
61966     },
61967
61968     autoSize : function(){
61969         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
61970         if(this.view){
61971             this.view.fitColumns();
61972         }
61973     },
61974
61975     onColumnResize : function(){
61976         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
61977         this.autoSize();
61978     },
61979     /**
61980      * Sets the data for the Grid
61981      * accepts a Key => Value object of all the elements avaiable.
61982      * @param {Object} data  to appear in grid.
61983      */
61984     setSource : function(source){
61985         this.store.setSource(source);
61986         //this.autoSize();
61987     },
61988     /**
61989      * Gets all the data from the grid.
61990      * @return {Object} data  data stored in grid
61991      */
61992     getSource : function(){
61993         return this.store.getSource();
61994     }
61995 });/*
61996   
61997  * Licence LGPL
61998  
61999  */
62000  
62001 /**
62002  * @class Roo.grid.Calendar
62003  * @extends Roo.grid.Grid
62004  * This class extends the Grid to provide a calendar widget
62005  * <br><br>Usage:<pre><code>
62006  var grid = new Roo.grid.Calendar("my-container-id", {
62007      ds: myDataStore,
62008      cm: myColModel,
62009      selModel: mySelectionModel,
62010      autoSizeColumns: true,
62011      monitorWindowResize: false,
62012      trackMouseOver: true
62013      eventstore : real data store..
62014  });
62015  // set any options
62016  grid.render();
62017   
62018   * @constructor
62019  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62020  * The container MUST have some type of size defined for the grid to fill. The container will be
62021  * automatically set to position relative if it isn't already.
62022  * @param {Object} config A config object that sets properties on this grid.
62023  */
62024 Roo.grid.Calendar = function(container, config){
62025         // initialize the container
62026         this.container = Roo.get(container);
62027         this.container.update("");
62028         this.container.setStyle("overflow", "hidden");
62029     this.container.addClass('x-grid-container');
62030
62031     this.id = this.container.id;
62032
62033     Roo.apply(this, config);
62034     // check and correct shorthanded configs
62035     
62036     var rows = [];
62037     var d =1;
62038     for (var r = 0;r < 6;r++) {
62039         
62040         rows[r]=[];
62041         for (var c =0;c < 7;c++) {
62042             rows[r][c]= '';
62043         }
62044     }
62045     if (this.eventStore) {
62046         this.eventStore= Roo.factory(this.eventStore, Roo.data);
62047         this.eventStore.on('load',this.onLoad, this);
62048         this.eventStore.on('beforeload',this.clearEvents, this);
62049          
62050     }
62051     
62052     this.dataSource = new Roo.data.Store({
62053             proxy: new Roo.data.MemoryProxy(rows),
62054             reader: new Roo.data.ArrayReader({}, [
62055                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
62056     });
62057
62058     this.dataSource.load();
62059     this.ds = this.dataSource;
62060     this.ds.xmodule = this.xmodule || false;
62061     
62062     
62063     var cellRender = function(v,x,r)
62064     {
62065         return String.format(
62066             '<div class="fc-day  fc-widget-content"><div>' +
62067                 '<div class="fc-event-container"></div>' +
62068                 '<div class="fc-day-number">{0}</div>'+
62069                 
62070                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
62071             '</div></div>', v);
62072     
62073     }
62074     
62075     
62076     this.colModel = new Roo.grid.ColumnModel( [
62077         {
62078             xtype: 'ColumnModel',
62079             xns: Roo.grid,
62080             dataIndex : 'weekday0',
62081             header : 'Sunday',
62082             renderer : cellRender
62083         },
62084         {
62085             xtype: 'ColumnModel',
62086             xns: Roo.grid,
62087             dataIndex : 'weekday1',
62088             header : 'Monday',
62089             renderer : cellRender
62090         },
62091         {
62092             xtype: 'ColumnModel',
62093             xns: Roo.grid,
62094             dataIndex : 'weekday2',
62095             header : 'Tuesday',
62096             renderer : cellRender
62097         },
62098         {
62099             xtype: 'ColumnModel',
62100             xns: Roo.grid,
62101             dataIndex : 'weekday3',
62102             header : 'Wednesday',
62103             renderer : cellRender
62104         },
62105         {
62106             xtype: 'ColumnModel',
62107             xns: Roo.grid,
62108             dataIndex : 'weekday4',
62109             header : 'Thursday',
62110             renderer : cellRender
62111         },
62112         {
62113             xtype: 'ColumnModel',
62114             xns: Roo.grid,
62115             dataIndex : 'weekday5',
62116             header : 'Friday',
62117             renderer : cellRender
62118         },
62119         {
62120             xtype: 'ColumnModel',
62121             xns: Roo.grid,
62122             dataIndex : 'weekday6',
62123             header : 'Saturday',
62124             renderer : cellRender
62125         }
62126     ]);
62127     this.cm = this.colModel;
62128     this.cm.xmodule = this.xmodule || false;
62129  
62130         
62131           
62132     //this.selModel = new Roo.grid.CellSelectionModel();
62133     //this.sm = this.selModel;
62134     //this.selModel.init(this);
62135     
62136     
62137     if(this.width){
62138         this.container.setWidth(this.width);
62139     }
62140
62141     if(this.height){
62142         this.container.setHeight(this.height);
62143     }
62144     /** @private */
62145         this.addEvents({
62146         // raw events
62147         /**
62148          * @event click
62149          * The raw click event for the entire grid.
62150          * @param {Roo.EventObject} e
62151          */
62152         "click" : true,
62153         /**
62154          * @event dblclick
62155          * The raw dblclick event for the entire grid.
62156          * @param {Roo.EventObject} e
62157          */
62158         "dblclick" : true,
62159         /**
62160          * @event contextmenu
62161          * The raw contextmenu event for the entire grid.
62162          * @param {Roo.EventObject} e
62163          */
62164         "contextmenu" : true,
62165         /**
62166          * @event mousedown
62167          * The raw mousedown event for the entire grid.
62168          * @param {Roo.EventObject} e
62169          */
62170         "mousedown" : true,
62171         /**
62172          * @event mouseup
62173          * The raw mouseup event for the entire grid.
62174          * @param {Roo.EventObject} e
62175          */
62176         "mouseup" : true,
62177         /**
62178          * @event mouseover
62179          * The raw mouseover event for the entire grid.
62180          * @param {Roo.EventObject} e
62181          */
62182         "mouseover" : true,
62183         /**
62184          * @event mouseout
62185          * The raw mouseout event for the entire grid.
62186          * @param {Roo.EventObject} e
62187          */
62188         "mouseout" : true,
62189         /**
62190          * @event keypress
62191          * The raw keypress event for the entire grid.
62192          * @param {Roo.EventObject} e
62193          */
62194         "keypress" : true,
62195         /**
62196          * @event keydown
62197          * The raw keydown event for the entire grid.
62198          * @param {Roo.EventObject} e
62199          */
62200         "keydown" : true,
62201
62202         // custom events
62203
62204         /**
62205          * @event cellclick
62206          * Fires when a cell is clicked
62207          * @param {Grid} this
62208          * @param {Number} rowIndex
62209          * @param {Number} columnIndex
62210          * @param {Roo.EventObject} e
62211          */
62212         "cellclick" : true,
62213         /**
62214          * @event celldblclick
62215          * Fires when a cell is double clicked
62216          * @param {Grid} this
62217          * @param {Number} rowIndex
62218          * @param {Number} columnIndex
62219          * @param {Roo.EventObject} e
62220          */
62221         "celldblclick" : true,
62222         /**
62223          * @event rowclick
62224          * Fires when a row is clicked
62225          * @param {Grid} this
62226          * @param {Number} rowIndex
62227          * @param {Roo.EventObject} e
62228          */
62229         "rowclick" : true,
62230         /**
62231          * @event rowdblclick
62232          * Fires when a row is double clicked
62233          * @param {Grid} this
62234          * @param {Number} rowIndex
62235          * @param {Roo.EventObject} e
62236          */
62237         "rowdblclick" : true,
62238         /**
62239          * @event headerclick
62240          * Fires when a header is clicked
62241          * @param {Grid} this
62242          * @param {Number} columnIndex
62243          * @param {Roo.EventObject} e
62244          */
62245         "headerclick" : true,
62246         /**
62247          * @event headerdblclick
62248          * Fires when a header cell is double clicked
62249          * @param {Grid} this
62250          * @param {Number} columnIndex
62251          * @param {Roo.EventObject} e
62252          */
62253         "headerdblclick" : true,
62254         /**
62255          * @event rowcontextmenu
62256          * Fires when a row is right clicked
62257          * @param {Grid} this
62258          * @param {Number} rowIndex
62259          * @param {Roo.EventObject} e
62260          */
62261         "rowcontextmenu" : true,
62262         /**
62263          * @event cellcontextmenu
62264          * Fires when a cell is right clicked
62265          * @param {Grid} this
62266          * @param {Number} rowIndex
62267          * @param {Number} cellIndex
62268          * @param {Roo.EventObject} e
62269          */
62270          "cellcontextmenu" : true,
62271         /**
62272          * @event headercontextmenu
62273          * Fires when a header is right clicked
62274          * @param {Grid} this
62275          * @param {Number} columnIndex
62276          * @param {Roo.EventObject} e
62277          */
62278         "headercontextmenu" : true,
62279         /**
62280          * @event bodyscroll
62281          * Fires when the body element is scrolled
62282          * @param {Number} scrollLeft
62283          * @param {Number} scrollTop
62284          */
62285         "bodyscroll" : true,
62286         /**
62287          * @event columnresize
62288          * Fires when the user resizes a column
62289          * @param {Number} columnIndex
62290          * @param {Number} newSize
62291          */
62292         "columnresize" : true,
62293         /**
62294          * @event columnmove
62295          * Fires when the user moves a column
62296          * @param {Number} oldIndex
62297          * @param {Number} newIndex
62298          */
62299         "columnmove" : true,
62300         /**
62301          * @event startdrag
62302          * Fires when row(s) start being dragged
62303          * @param {Grid} this
62304          * @param {Roo.GridDD} dd The drag drop object
62305          * @param {event} e The raw browser event
62306          */
62307         "startdrag" : true,
62308         /**
62309          * @event enddrag
62310          * Fires when a drag operation is complete
62311          * @param {Grid} this
62312          * @param {Roo.GridDD} dd The drag drop object
62313          * @param {event} e The raw browser event
62314          */
62315         "enddrag" : true,
62316         /**
62317          * @event dragdrop
62318          * Fires when dragged row(s) are dropped on a valid DD target
62319          * @param {Grid} this
62320          * @param {Roo.GridDD} dd The drag drop object
62321          * @param {String} targetId The target drag drop object
62322          * @param {event} e The raw browser event
62323          */
62324         "dragdrop" : true,
62325         /**
62326          * @event dragover
62327          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
62328          * @param {Grid} this
62329          * @param {Roo.GridDD} dd The drag drop object
62330          * @param {String} targetId The target drag drop object
62331          * @param {event} e The raw browser event
62332          */
62333         "dragover" : true,
62334         /**
62335          * @event dragenter
62336          *  Fires when the dragged row(s) first cross another DD target while being dragged
62337          * @param {Grid} this
62338          * @param {Roo.GridDD} dd The drag drop object
62339          * @param {String} targetId The target drag drop object
62340          * @param {event} e The raw browser event
62341          */
62342         "dragenter" : true,
62343         /**
62344          * @event dragout
62345          * Fires when the dragged row(s) leave another DD target while being dragged
62346          * @param {Grid} this
62347          * @param {Roo.GridDD} dd The drag drop object
62348          * @param {String} targetId The target drag drop object
62349          * @param {event} e The raw browser event
62350          */
62351         "dragout" : true,
62352         /**
62353          * @event rowclass
62354          * Fires when a row is rendered, so you can change add a style to it.
62355          * @param {GridView} gridview   The grid view
62356          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
62357          */
62358         'rowclass' : true,
62359
62360         /**
62361          * @event render
62362          * Fires when the grid is rendered
62363          * @param {Grid} grid
62364          */
62365         'render' : true,
62366             /**
62367              * @event select
62368              * Fires when a date is selected
62369              * @param {DatePicker} this
62370              * @param {Date} date The selected date
62371              */
62372         'select': true,
62373         /**
62374              * @event monthchange
62375              * Fires when the displayed month changes 
62376              * @param {DatePicker} this
62377              * @param {Date} date The selected month
62378              */
62379         'monthchange': true,
62380         /**
62381              * @event evententer
62382              * Fires when mouse over an event
62383              * @param {Calendar} this
62384              * @param {event} Event
62385              */
62386         'evententer': true,
62387         /**
62388              * @event eventleave
62389              * Fires when the mouse leaves an
62390              * @param {Calendar} this
62391              * @param {event}
62392              */
62393         'eventleave': true,
62394         /**
62395              * @event eventclick
62396              * Fires when the mouse click an
62397              * @param {Calendar} this
62398              * @param {event}
62399              */
62400         'eventclick': true,
62401         /**
62402              * @event eventrender
62403              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
62404              * @param {Calendar} this
62405              * @param {data} data to be modified
62406              */
62407         'eventrender': true
62408         
62409     });
62410
62411     Roo.grid.Grid.superclass.constructor.call(this);
62412     this.on('render', function() {
62413         this.view.el.addClass('x-grid-cal'); 
62414         
62415         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
62416
62417     },this);
62418     
62419     if (!Roo.grid.Calendar.style) {
62420         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
62421             
62422             
62423             '.x-grid-cal .x-grid-col' :  {
62424                 height: 'auto !important',
62425                 'vertical-align': 'top'
62426             },
62427             '.x-grid-cal  .fc-event-hori' : {
62428                 height: '14px'
62429             }
62430              
62431             
62432         }, Roo.id());
62433     }
62434
62435     
62436     
62437 };
62438 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
62439     /**
62440      * @cfg {Store} eventStore The store that loads events.
62441      */
62442     eventStore : 25,
62443
62444      
62445     activeDate : false,
62446     startDay : 0,
62447     autoWidth : true,
62448     monitorWindowResize : false,
62449
62450     
62451     resizeColumns : function() {
62452         var col = (this.view.el.getWidth() / 7) - 3;
62453         // loop through cols, and setWidth
62454         for(var i =0 ; i < 7 ; i++){
62455             this.cm.setColumnWidth(i, col);
62456         }
62457     },
62458      setDate :function(date) {
62459         
62460         Roo.log('setDate?');
62461         
62462         this.resizeColumns();
62463         var vd = this.activeDate;
62464         this.activeDate = date;
62465 //        if(vd && this.el){
62466 //            var t = date.getTime();
62467 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
62468 //                Roo.log('using add remove');
62469 //                
62470 //                this.fireEvent('monthchange', this, date);
62471 //                
62472 //                this.cells.removeClass("fc-state-highlight");
62473 //                this.cells.each(function(c){
62474 //                   if(c.dateValue == t){
62475 //                       c.addClass("fc-state-highlight");
62476 //                       setTimeout(function(){
62477 //                            try{c.dom.firstChild.focus();}catch(e){}
62478 //                       }, 50);
62479 //                       return false;
62480 //                   }
62481 //                   return true;
62482 //                });
62483 //                return;
62484 //            }
62485 //        }
62486         
62487         var days = date.getDaysInMonth();
62488         
62489         var firstOfMonth = date.getFirstDateOfMonth();
62490         var startingPos = firstOfMonth.getDay()-this.startDay;
62491         
62492         if(startingPos < this.startDay){
62493             startingPos += 7;
62494         }
62495         
62496         var pm = date.add(Date.MONTH, -1);
62497         var prevStart = pm.getDaysInMonth()-startingPos;
62498 //        
62499         
62500         
62501         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62502         
62503         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
62504         //this.cells.addClassOnOver('fc-state-hover');
62505         
62506         var cells = this.cells.elements;
62507         var textEls = this.textNodes;
62508         
62509         //Roo.each(cells, function(cell){
62510         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
62511         //});
62512         
62513         days += startingPos;
62514
62515         // convert everything to numbers so it's fast
62516         var day = 86400000;
62517         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
62518         //Roo.log(d);
62519         //Roo.log(pm);
62520         //Roo.log(prevStart);
62521         
62522         var today = new Date().clearTime().getTime();
62523         var sel = date.clearTime().getTime();
62524         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
62525         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
62526         var ddMatch = this.disabledDatesRE;
62527         var ddText = this.disabledDatesText;
62528         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
62529         var ddaysText = this.disabledDaysText;
62530         var format = this.format;
62531         
62532         var setCellClass = function(cal, cell){
62533             
62534             //Roo.log('set Cell Class');
62535             cell.title = "";
62536             var t = d.getTime();
62537             
62538             //Roo.log(d);
62539             
62540             
62541             cell.dateValue = t;
62542             if(t == today){
62543                 cell.className += " fc-today";
62544                 cell.className += " fc-state-highlight";
62545                 cell.title = cal.todayText;
62546             }
62547             if(t == sel){
62548                 // disable highlight in other month..
62549                 cell.className += " fc-state-highlight";
62550                 
62551             }
62552             // disabling
62553             if(t < min) {
62554                 //cell.className = " fc-state-disabled";
62555                 cell.title = cal.minText;
62556                 return;
62557             }
62558             if(t > max) {
62559                 //cell.className = " fc-state-disabled";
62560                 cell.title = cal.maxText;
62561                 return;
62562             }
62563             if(ddays){
62564                 if(ddays.indexOf(d.getDay()) != -1){
62565                     // cell.title = ddaysText;
62566                    // cell.className = " fc-state-disabled";
62567                 }
62568             }
62569             if(ddMatch && format){
62570                 var fvalue = d.dateFormat(format);
62571                 if(ddMatch.test(fvalue)){
62572                     cell.title = ddText.replace("%0", fvalue);
62573                    cell.className = " fc-state-disabled";
62574                 }
62575             }
62576             
62577             if (!cell.initialClassName) {
62578                 cell.initialClassName = cell.dom.className;
62579             }
62580             
62581             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
62582         };
62583
62584         var i = 0;
62585         
62586         for(; i < startingPos; i++) {
62587             cells[i].dayName =  (++prevStart);
62588             Roo.log(textEls[i]);
62589             d.setDate(d.getDate()+1);
62590             
62591             //cells[i].className = "fc-past fc-other-month";
62592             setCellClass(this, cells[i]);
62593         }
62594         
62595         var intDay = 0;
62596         
62597         for(; i < days; i++){
62598             intDay = i - startingPos + 1;
62599             cells[i].dayName =  (intDay);
62600             d.setDate(d.getDate()+1);
62601             
62602             cells[i].className = ''; // "x-date-active";
62603             setCellClass(this, cells[i]);
62604         }
62605         var extraDays = 0;
62606         
62607         for(; i < 42; i++) {
62608             //textEls[i].innerHTML = (++extraDays);
62609             
62610             d.setDate(d.getDate()+1);
62611             cells[i].dayName = (++extraDays);
62612             cells[i].className = "fc-future fc-other-month";
62613             setCellClass(this, cells[i]);
62614         }
62615         
62616         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
62617         
62618         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
62619         
62620         // this will cause all the cells to mis
62621         var rows= [];
62622         var i =0;
62623         for (var r = 0;r < 6;r++) {
62624             for (var c =0;c < 7;c++) {
62625                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
62626             }    
62627         }
62628         
62629         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
62630         for(i=0;i<cells.length;i++) {
62631             
62632             this.cells.elements[i].dayName = cells[i].dayName ;
62633             this.cells.elements[i].className = cells[i].className;
62634             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
62635             this.cells.elements[i].title = cells[i].title ;
62636             this.cells.elements[i].dateValue = cells[i].dateValue ;
62637         }
62638         
62639         
62640         
62641         
62642         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
62643         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
62644         
62645         ////if(totalRows != 6){
62646             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
62647            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
62648        // }
62649         
62650         this.fireEvent('monthchange', this, date);
62651         
62652         
62653     },
62654  /**
62655      * Returns the grid's SelectionModel.
62656      * @return {SelectionModel}
62657      */
62658     getSelectionModel : function(){
62659         if(!this.selModel){
62660             this.selModel = new Roo.grid.CellSelectionModel();
62661         }
62662         return this.selModel;
62663     },
62664
62665     load: function() {
62666         this.eventStore.load()
62667         
62668         
62669         
62670     },
62671     
62672     findCell : function(dt) {
62673         dt = dt.clearTime().getTime();
62674         var ret = false;
62675         this.cells.each(function(c){
62676             //Roo.log("check " +c.dateValue + '?=' + dt);
62677             if(c.dateValue == dt){
62678                 ret = c;
62679                 return false;
62680             }
62681             return true;
62682         });
62683         
62684         return ret;
62685     },
62686     
62687     findCells : function(rec) {
62688         var s = rec.data.start_dt.clone().clearTime().getTime();
62689        // Roo.log(s);
62690         var e= rec.data.end_dt.clone().clearTime().getTime();
62691        // Roo.log(e);
62692         var ret = [];
62693         this.cells.each(function(c){
62694              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
62695             
62696             if(c.dateValue > e){
62697                 return ;
62698             }
62699             if(c.dateValue < s){
62700                 return ;
62701             }
62702             ret.push(c);
62703         });
62704         
62705         return ret;    
62706     },
62707     
62708     findBestRow: function(cells)
62709     {
62710         var ret = 0;
62711         
62712         for (var i =0 ; i < cells.length;i++) {
62713             ret  = Math.max(cells[i].rows || 0,ret);
62714         }
62715         return ret;
62716         
62717     },
62718     
62719     
62720     addItem : function(rec)
62721     {
62722         // look for vertical location slot in
62723         var cells = this.findCells(rec);
62724         
62725         rec.row = this.findBestRow(cells);
62726         
62727         // work out the location.
62728         
62729         var crow = false;
62730         var rows = [];
62731         for(var i =0; i < cells.length; i++) {
62732             if (!crow) {
62733                 crow = {
62734                     start : cells[i],
62735                     end :  cells[i]
62736                 };
62737                 continue;
62738             }
62739             if (crow.start.getY() == cells[i].getY()) {
62740                 // on same row.
62741                 crow.end = cells[i];
62742                 continue;
62743             }
62744             // different row.
62745             rows.push(crow);
62746             crow = {
62747                 start: cells[i],
62748                 end : cells[i]
62749             };
62750             
62751         }
62752         
62753         rows.push(crow);
62754         rec.els = [];
62755         rec.rows = rows;
62756         rec.cells = cells;
62757         for (var i = 0; i < cells.length;i++) {
62758             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
62759             
62760         }
62761         
62762         
62763     },
62764     
62765     clearEvents: function() {
62766         
62767         if (!this.eventStore.getCount()) {
62768             return;
62769         }
62770         // reset number of rows in cells.
62771         Roo.each(this.cells.elements, function(c){
62772             c.rows = 0;
62773         });
62774         
62775         this.eventStore.each(function(e) {
62776             this.clearEvent(e);
62777         },this);
62778         
62779     },
62780     
62781     clearEvent : function(ev)
62782     {
62783         if (ev.els) {
62784             Roo.each(ev.els, function(el) {
62785                 el.un('mouseenter' ,this.onEventEnter, this);
62786                 el.un('mouseleave' ,this.onEventLeave, this);
62787                 el.remove();
62788             },this);
62789             ev.els = [];
62790         }
62791     },
62792     
62793     
62794     renderEvent : function(ev,ctr) {
62795         if (!ctr) {
62796              ctr = this.view.el.select('.fc-event-container',true).first();
62797         }
62798         
62799          
62800         this.clearEvent(ev);
62801             //code
62802        
62803         
62804         
62805         ev.els = [];
62806         var cells = ev.cells;
62807         var rows = ev.rows;
62808         this.fireEvent('eventrender', this, ev);
62809         
62810         for(var i =0; i < rows.length; i++) {
62811             
62812             cls = '';
62813             if (i == 0) {
62814                 cls += ' fc-event-start';
62815             }
62816             if ((i+1) == rows.length) {
62817                 cls += ' fc-event-end';
62818             }
62819             
62820             //Roo.log(ev.data);
62821             // how many rows should it span..
62822             var cg = this.eventTmpl.append(ctr,Roo.apply({
62823                 fccls : cls
62824                 
62825             }, ev.data) , true);
62826             
62827             
62828             cg.on('mouseenter' ,this.onEventEnter, this, ev);
62829             cg.on('mouseleave' ,this.onEventLeave, this, ev);
62830             cg.on('click', this.onEventClick, this, ev);
62831             
62832             ev.els.push(cg);
62833             
62834             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
62835             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
62836             //Roo.log(cg);
62837              
62838             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
62839             cg.setWidth(ebox.right - sbox.x -2);
62840         }
62841     },
62842     
62843     renderEvents: function()
62844     {   
62845         // first make sure there is enough space..
62846         
62847         if (!this.eventTmpl) {
62848             this.eventTmpl = new Roo.Template(
62849                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
62850                     '<div class="fc-event-inner">' +
62851                         '<span class="fc-event-time">{time}</span>' +
62852                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
62853                     '</div>' +
62854                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
62855                 '</div>'
62856             );
62857                 
62858         }
62859                
62860         
62861         
62862         this.cells.each(function(c) {
62863             //Roo.log(c.select('.fc-day-content div',true).first());
62864             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
62865         });
62866         
62867         var ctr = this.view.el.select('.fc-event-container',true).first();
62868         
62869         var cls;
62870         this.eventStore.each(function(ev){
62871             
62872             this.renderEvent(ev);
62873              
62874              
62875         }, this);
62876         this.view.layout();
62877         
62878     },
62879     
62880     onEventEnter: function (e, el,event,d) {
62881         this.fireEvent('evententer', this, el, event);
62882     },
62883     
62884     onEventLeave: function (e, el,event,d) {
62885         this.fireEvent('eventleave', this, el, event);
62886     },
62887     
62888     onEventClick: function (e, el,event,d) {
62889         this.fireEvent('eventclick', this, el, event);
62890     },
62891     
62892     onMonthChange: function () {
62893         this.store.load();
62894     },
62895     
62896     onLoad: function () {
62897         
62898         //Roo.log('calendar onload');
62899 //         
62900         if(this.eventStore.getCount() > 0){
62901             
62902            
62903             
62904             this.eventStore.each(function(d){
62905                 
62906                 
62907                 // FIXME..
62908                 var add =   d.data;
62909                 if (typeof(add.end_dt) == 'undefined')  {
62910                     Roo.log("Missing End time in calendar data: ");
62911                     Roo.log(d);
62912                     return;
62913                 }
62914                 if (typeof(add.start_dt) == 'undefined')  {
62915                     Roo.log("Missing Start time in calendar data: ");
62916                     Roo.log(d);
62917                     return;
62918                 }
62919                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
62920                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
62921                 add.id = add.id || d.id;
62922                 add.title = add.title || '??';
62923                 
62924                 this.addItem(d);
62925                 
62926              
62927             },this);
62928         }
62929         
62930         this.renderEvents();
62931     }
62932     
62933
62934 });
62935 /*
62936  grid : {
62937                 xtype: 'Grid',
62938                 xns: Roo.grid,
62939                 listeners : {
62940                     render : function ()
62941                     {
62942                         _this.grid = this;
62943                         
62944                         if (!this.view.el.hasClass('course-timesheet')) {
62945                             this.view.el.addClass('course-timesheet');
62946                         }
62947                         if (this.tsStyle) {
62948                             this.ds.load({});
62949                             return; 
62950                         }
62951                         Roo.log('width');
62952                         Roo.log(_this.grid.view.el.getWidth());
62953                         
62954                         
62955                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
62956                             '.course-timesheet .x-grid-row' : {
62957                                 height: '80px'
62958                             },
62959                             '.x-grid-row td' : {
62960                                 'vertical-align' : 0
62961                             },
62962                             '.course-edit-link' : {
62963                                 'color' : 'blue',
62964                                 'text-overflow' : 'ellipsis',
62965                                 'overflow' : 'hidden',
62966                                 'white-space' : 'nowrap',
62967                                 'cursor' : 'pointer'
62968                             },
62969                             '.sub-link' : {
62970                                 'color' : 'green'
62971                             },
62972                             '.de-act-sup-link' : {
62973                                 'color' : 'purple',
62974                                 'text-decoration' : 'line-through'
62975                             },
62976                             '.de-act-link' : {
62977                                 'color' : 'red',
62978                                 'text-decoration' : 'line-through'
62979                             },
62980                             '.course-timesheet .course-highlight' : {
62981                                 'border-top-style': 'dashed !important',
62982                                 'border-bottom-bottom': 'dashed !important'
62983                             },
62984                             '.course-timesheet .course-item' : {
62985                                 'font-family'   : 'tahoma, arial, helvetica',
62986                                 'font-size'     : '11px',
62987                                 'overflow'      : 'hidden',
62988                                 'padding-left'  : '10px',
62989                                 'padding-right' : '10px',
62990                                 'padding-top' : '10px' 
62991                             }
62992                             
62993                         }, Roo.id());
62994                                 this.ds.load({});
62995                     }
62996                 },
62997                 autoWidth : true,
62998                 monitorWindowResize : false,
62999                 cellrenderer : function(v,x,r)
63000                 {
63001                     return v;
63002                 },
63003                 sm : {
63004                     xtype: 'CellSelectionModel',
63005                     xns: Roo.grid
63006                 },
63007                 dataSource : {
63008                     xtype: 'Store',
63009                     xns: Roo.data,
63010                     listeners : {
63011                         beforeload : function (_self, options)
63012                         {
63013                             options.params = options.params || {};
63014                             options.params._month = _this.monthField.getValue();
63015                             options.params.limit = 9999;
63016                             options.params['sort'] = 'when_dt';    
63017                             options.params['dir'] = 'ASC';    
63018                             this.proxy.loadResponse = this.loadResponse;
63019                             Roo.log("load?");
63020                             //this.addColumns();
63021                         },
63022                         load : function (_self, records, options)
63023                         {
63024                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
63025                                 // if you click on the translation.. you can edit it...
63026                                 var el = Roo.get(this);
63027                                 var id = el.dom.getAttribute('data-id');
63028                                 var d = el.dom.getAttribute('data-date');
63029                                 var t = el.dom.getAttribute('data-time');
63030                                 //var id = this.child('span').dom.textContent;
63031                                 
63032                                 //Roo.log(this);
63033                                 Pman.Dialog.CourseCalendar.show({
63034                                     id : id,
63035                                     when_d : d,
63036                                     when_t : t,
63037                                     productitem_active : id ? 1 : 0
63038                                 }, function() {
63039                                     _this.grid.ds.load({});
63040                                 });
63041                            
63042                            });
63043                            
63044                            _this.panel.fireEvent('resize', [ '', '' ]);
63045                         }
63046                     },
63047                     loadResponse : function(o, success, response){
63048                             // this is overridden on before load..
63049                             
63050                             Roo.log("our code?");       
63051                             //Roo.log(success);
63052                             //Roo.log(response)
63053                             delete this.activeRequest;
63054                             if(!success){
63055                                 this.fireEvent("loadexception", this, o, response);
63056                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63057                                 return;
63058                             }
63059                             var result;
63060                             try {
63061                                 result = o.reader.read(response);
63062                             }catch(e){
63063                                 Roo.log("load exception?");
63064                                 this.fireEvent("loadexception", this, o, response, e);
63065                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63066                                 return;
63067                             }
63068                             Roo.log("ready...");        
63069                             // loop through result.records;
63070                             // and set this.tdate[date] = [] << array of records..
63071                             _this.tdata  = {};
63072                             Roo.each(result.records, function(r){
63073                                 //Roo.log(r.data);
63074                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
63075                                     _this.tdata[r.data.when_dt.format('j')] = [];
63076                                 }
63077                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
63078                             });
63079                             
63080                             //Roo.log(_this.tdata);
63081                             
63082                             result.records = [];
63083                             result.totalRecords = 6;
63084                     
63085                             // let's generate some duumy records for the rows.
63086                             //var st = _this.dateField.getValue();
63087                             
63088                             // work out monday..
63089                             //st = st.add(Date.DAY, -1 * st.format('w'));
63090                             
63091                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63092                             
63093                             var firstOfMonth = date.getFirstDayOfMonth();
63094                             var days = date.getDaysInMonth();
63095                             var d = 1;
63096                             var firstAdded = false;
63097                             for (var i = 0; i < result.totalRecords ; i++) {
63098                                 //var d= st.add(Date.DAY, i);
63099                                 var row = {};
63100                                 var added = 0;
63101                                 for(var w = 0 ; w < 7 ; w++){
63102                                     if(!firstAdded && firstOfMonth != w){
63103                                         continue;
63104                                     }
63105                                     if(d > days){
63106                                         continue;
63107                                     }
63108                                     firstAdded = true;
63109                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
63110                                     row['weekday'+w] = String.format(
63111                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
63112                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
63113                                                     d,
63114                                                     date.format('Y-m-')+dd
63115                                                 );
63116                                     added++;
63117                                     if(typeof(_this.tdata[d]) != 'undefined'){
63118                                         Roo.each(_this.tdata[d], function(r){
63119                                             var is_sub = '';
63120                                             var deactive = '';
63121                                             var id = r.id;
63122                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
63123                                             if(r.parent_id*1>0){
63124                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
63125                                                 id = r.parent_id;
63126                                             }
63127                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
63128                                                 deactive = 'de-act-link';
63129                                             }
63130                                             
63131                                             row['weekday'+w] += String.format(
63132                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
63133                                                     id, //0
63134                                                     r.product_id_name, //1
63135                                                     r.when_dt.format('h:ia'), //2
63136                                                     is_sub, //3
63137                                                     deactive, //4
63138                                                     desc // 5
63139                                             );
63140                                         });
63141                                     }
63142                                     d++;
63143                                 }
63144                                 
63145                                 // only do this if something added..
63146                                 if(added > 0){ 
63147                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
63148                                 }
63149                                 
63150                                 
63151                                 // push it twice. (second one with an hour..
63152                                 
63153                             }
63154                             //Roo.log(result);
63155                             this.fireEvent("load", this, o, o.request.arg);
63156                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
63157                         },
63158                     sortInfo : {field: 'when_dt', direction : 'ASC' },
63159                     proxy : {
63160                         xtype: 'HttpProxy',
63161                         xns: Roo.data,
63162                         method : 'GET',
63163                         url : baseURL + '/Roo/Shop_course.php'
63164                     },
63165                     reader : {
63166                         xtype: 'JsonReader',
63167                         xns: Roo.data,
63168                         id : 'id',
63169                         fields : [
63170                             {
63171                                 'name': 'id',
63172                                 'type': 'int'
63173                             },
63174                             {
63175                                 'name': 'when_dt',
63176                                 'type': 'string'
63177                             },
63178                             {
63179                                 'name': 'end_dt',
63180                                 'type': 'string'
63181                             },
63182                             {
63183                                 'name': 'parent_id',
63184                                 'type': 'int'
63185                             },
63186                             {
63187                                 'name': 'product_id',
63188                                 'type': 'int'
63189                             },
63190                             {
63191                                 'name': 'productitem_id',
63192                                 'type': 'int'
63193                             },
63194                             {
63195                                 'name': 'guid',
63196                                 'type': 'int'
63197                             }
63198                         ]
63199                     }
63200                 },
63201                 toolbar : {
63202                     xtype: 'Toolbar',
63203                     xns: Roo,
63204                     items : [
63205                         {
63206                             xtype: 'Button',
63207                             xns: Roo.Toolbar,
63208                             listeners : {
63209                                 click : function (_self, e)
63210                                 {
63211                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63212                                     sd.setMonth(sd.getMonth()-1);
63213                                     _this.monthField.setValue(sd.format('Y-m-d'));
63214                                     _this.grid.ds.load({});
63215                                 }
63216                             },
63217                             text : "Back"
63218                         },
63219                         {
63220                             xtype: 'Separator',
63221                             xns: Roo.Toolbar
63222                         },
63223                         {
63224                             xtype: 'MonthField',
63225                             xns: Roo.form,
63226                             listeners : {
63227                                 render : function (_self)
63228                                 {
63229                                     _this.monthField = _self;
63230                                    // _this.monthField.set  today
63231                                 },
63232                                 select : function (combo, date)
63233                                 {
63234                                     _this.grid.ds.load({});
63235                                 }
63236                             },
63237                             value : (function() { return new Date(); })()
63238                         },
63239                         {
63240                             xtype: 'Separator',
63241                             xns: Roo.Toolbar
63242                         },
63243                         {
63244                             xtype: 'TextItem',
63245                             xns: Roo.Toolbar,
63246                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
63247                         },
63248                         {
63249                             xtype: 'Fill',
63250                             xns: Roo.Toolbar
63251                         },
63252                         {
63253                             xtype: 'Button',
63254                             xns: Roo.Toolbar,
63255                             listeners : {
63256                                 click : function (_self, e)
63257                                 {
63258                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63259                                     sd.setMonth(sd.getMonth()+1);
63260                                     _this.monthField.setValue(sd.format('Y-m-d'));
63261                                     _this.grid.ds.load({});
63262                                 }
63263                             },
63264                             text : "Next"
63265                         }
63266                     ]
63267                 },
63268                  
63269             }
63270         };
63271         
63272         *//*
63273  * Based on:
63274  * Ext JS Library 1.1.1
63275  * Copyright(c) 2006-2007, Ext JS, LLC.
63276  *
63277  * Originally Released Under LGPL - original licence link has changed is not relivant.
63278  *
63279  * Fork - LGPL
63280  * <script type="text/javascript">
63281  */
63282  
63283 /**
63284  * @class Roo.LoadMask
63285  * A simple utility class for generically masking elements while loading data.  If the element being masked has
63286  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
63287  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
63288  * element's UpdateManager load indicator and will be destroyed after the initial load.
63289  * @constructor
63290  * Create a new LoadMask
63291  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
63292  * @param {Object} config The config object
63293  */
63294 Roo.LoadMask = function(el, config){
63295     this.el = Roo.get(el);
63296     Roo.apply(this, config);
63297     if(this.store){
63298         this.store.on('beforeload', this.onBeforeLoad, this);
63299         this.store.on('load', this.onLoad, this);
63300         this.store.on('loadexception', this.onLoadException, this);
63301         this.removeMask = false;
63302     }else{
63303         var um = this.el.getUpdateManager();
63304         um.showLoadIndicator = false; // disable the default indicator
63305         um.on('beforeupdate', this.onBeforeLoad, this);
63306         um.on('update', this.onLoad, this);
63307         um.on('failure', this.onLoad, this);
63308         this.removeMask = true;
63309     }
63310 };
63311
63312 Roo.LoadMask.prototype = {
63313     /**
63314      * @cfg {Boolean} removeMask
63315      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
63316      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
63317      */
63318     removeMask : false,
63319     /**
63320      * @cfg {String} msg
63321      * The text to display in a centered loading message box (defaults to 'Loading...')
63322      */
63323     msg : 'Loading...',
63324     /**
63325      * @cfg {String} msgCls
63326      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
63327      */
63328     msgCls : 'x-mask-loading',
63329
63330     /**
63331      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
63332      * @type Boolean
63333      */
63334     disabled: false,
63335
63336     /**
63337      * Disables the mask to prevent it from being displayed
63338      */
63339     disable : function(){
63340        this.disabled = true;
63341     },
63342
63343     /**
63344      * Enables the mask so that it can be displayed
63345      */
63346     enable : function(){
63347         this.disabled = false;
63348     },
63349     
63350     onLoadException : function()
63351     {
63352         Roo.log(arguments);
63353         
63354         if (typeof(arguments[3]) != 'undefined') {
63355             Roo.MessageBox.alert("Error loading",arguments[3]);
63356         } 
63357         /*
63358         try {
63359             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
63360                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
63361             }   
63362         } catch(e) {
63363             
63364         }
63365         */
63366     
63367         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
63368     },
63369     // private
63370     onLoad : function()
63371     {
63372         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
63373     },
63374
63375     // private
63376     onBeforeLoad : function(){
63377         if(!this.disabled){
63378             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
63379         }
63380     },
63381
63382     // private
63383     destroy : function(){
63384         if(this.store){
63385             this.store.un('beforeload', this.onBeforeLoad, this);
63386             this.store.un('load', this.onLoad, this);
63387             this.store.un('loadexception', this.onLoadException, this);
63388         }else{
63389             var um = this.el.getUpdateManager();
63390             um.un('beforeupdate', this.onBeforeLoad, this);
63391             um.un('update', this.onLoad, this);
63392             um.un('failure', this.onLoad, this);
63393         }
63394     }
63395 };/*
63396  * Based on:
63397  * Ext JS Library 1.1.1
63398  * Copyright(c) 2006-2007, Ext JS, LLC.
63399  *
63400  * Originally Released Under LGPL - original licence link has changed is not relivant.
63401  *
63402  * Fork - LGPL
63403  * <script type="text/javascript">
63404  */
63405
63406
63407 /**
63408  * @class Roo.XTemplate
63409  * @extends Roo.Template
63410  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
63411 <pre><code>
63412 var t = new Roo.XTemplate(
63413         '&lt;select name="{name}"&gt;',
63414                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
63415         '&lt;/select&gt;'
63416 );
63417  
63418 // then append, applying the master template values
63419  </code></pre>
63420  *
63421  * Supported features:
63422  *
63423  *  Tags:
63424
63425 <pre><code>
63426       {a_variable} - output encoded.
63427       {a_variable.format:("Y-m-d")} - call a method on the variable
63428       {a_variable:raw} - unencoded output
63429       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
63430       {a_variable:this.method_on_template(...)} - call a method on the template object.
63431  
63432 </code></pre>
63433  *  The tpl tag:
63434 <pre><code>
63435         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
63436         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
63437         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
63438         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
63439   
63440         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
63441         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
63442 </code></pre>
63443  *      
63444  */
63445 Roo.XTemplate = function()
63446 {
63447     Roo.XTemplate.superclass.constructor.apply(this, arguments);
63448     if (this.html) {
63449         this.compile();
63450     }
63451 };
63452
63453
63454 Roo.extend(Roo.XTemplate, Roo.Template, {
63455
63456     /**
63457      * The various sub templates
63458      */
63459     tpls : false,
63460     /**
63461      *
63462      * basic tag replacing syntax
63463      * WORD:WORD()
63464      *
63465      * // you can fake an object call by doing this
63466      *  x.t:(test,tesT) 
63467      * 
63468      */
63469     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
63470
63471     /**
63472      * compile the template
63473      *
63474      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
63475      *
63476      */
63477     compile: function()
63478     {
63479         var s = this.html;
63480      
63481         s = ['<tpl>', s, '</tpl>'].join('');
63482     
63483         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
63484             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
63485             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
63486             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
63487             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
63488             m,
63489             id     = 0,
63490             tpls   = [];
63491     
63492         while(true == !!(m = s.match(re))){
63493             var forMatch   = m[0].match(nameRe),
63494                 ifMatch   = m[0].match(ifRe),
63495                 execMatch   = m[0].match(execRe),
63496                 namedMatch   = m[0].match(namedRe),
63497                 
63498                 exp  = null, 
63499                 fn   = null,
63500                 exec = null,
63501                 name = forMatch && forMatch[1] ? forMatch[1] : '';
63502                 
63503             if (ifMatch) {
63504                 // if - puts fn into test..
63505                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
63506                 if(exp){
63507                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
63508                 }
63509             }
63510             
63511             if (execMatch) {
63512                 // exec - calls a function... returns empty if true is  returned.
63513                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
63514                 if(exp){
63515                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
63516                 }
63517             }
63518             
63519             
63520             if (name) {
63521                 // for = 
63522                 switch(name){
63523                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
63524                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
63525                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
63526                 }
63527             }
63528             var uid = namedMatch ? namedMatch[1] : id;
63529             
63530             
63531             tpls.push({
63532                 id:     namedMatch ? namedMatch[1] : id,
63533                 target: name,
63534                 exec:   exec,
63535                 test:   fn,
63536                 body:   m[1] || ''
63537             });
63538             if (namedMatch) {
63539                 s = s.replace(m[0], '');
63540             } else { 
63541                 s = s.replace(m[0], '{xtpl'+ id + '}');
63542             }
63543             ++id;
63544         }
63545         this.tpls = [];
63546         for(var i = tpls.length-1; i >= 0; --i){
63547             this.compileTpl(tpls[i]);
63548             this.tpls[tpls[i].id] = tpls[i];
63549         }
63550         this.master = tpls[tpls.length-1];
63551         return this;
63552     },
63553     /**
63554      * same as applyTemplate, except it's done to one of the subTemplates
63555      * when using named templates, you can do:
63556      *
63557      * var str = pl.applySubTemplate('your-name', values);
63558      *
63559      * 
63560      * @param {Number} id of the template
63561      * @param {Object} values to apply to template
63562      * @param {Object} parent (normaly the instance of this object)
63563      */
63564     applySubTemplate : function(id, values, parent)
63565     {
63566         
63567         
63568         var t = this.tpls[id];
63569         
63570         
63571         try { 
63572             if(t.test && !t.test.call(this, values, parent)){
63573                 return '';
63574             }
63575         } catch(e) {
63576             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
63577             Roo.log(e.toString());
63578             Roo.log(t.test);
63579             return ''
63580         }
63581         try { 
63582             
63583             if(t.exec && t.exec.call(this, values, parent)){
63584                 return '';
63585             }
63586         } catch(e) {
63587             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
63588             Roo.log(e.toString());
63589             Roo.log(t.exec);
63590             return ''
63591         }
63592         try {
63593             var vs = t.target ? t.target.call(this, values, parent) : values;
63594             parent = t.target ? values : parent;
63595             if(t.target && vs instanceof Array){
63596                 var buf = [];
63597                 for(var i = 0, len = vs.length; i < len; i++){
63598                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
63599                 }
63600                 return buf.join('');
63601             }
63602             return t.compiled.call(this, vs, parent);
63603         } catch (e) {
63604             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
63605             Roo.log(e.toString());
63606             Roo.log(t.compiled);
63607             return '';
63608         }
63609     },
63610
63611     compileTpl : function(tpl)
63612     {
63613         var fm = Roo.util.Format;
63614         var useF = this.disableFormats !== true;
63615         var sep = Roo.isGecko ? "+" : ",";
63616         var undef = function(str) {
63617             Roo.log("Property not found :"  + str);
63618             return '';
63619         };
63620         
63621         var fn = function(m, name, format, args)
63622         {
63623             //Roo.log(arguments);
63624             args = args ? args.replace(/\\'/g,"'") : args;
63625             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
63626             if (typeof(format) == 'undefined') {
63627                 format= 'htmlEncode';
63628             }
63629             if (format == 'raw' ) {
63630                 format = false;
63631             }
63632             
63633             if(name.substr(0, 4) == 'xtpl'){
63634                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
63635             }
63636             
63637             // build an array of options to determine if value is undefined..
63638             
63639             // basically get 'xxxx.yyyy' then do
63640             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
63641             //    (function () { Roo.log("Property not found"); return ''; })() :
63642             //    ......
63643             
63644             var udef_ar = [];
63645             var lookfor = '';
63646             Roo.each(name.split('.'), function(st) {
63647                 lookfor += (lookfor.length ? '.': '') + st;
63648                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
63649             });
63650             
63651             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
63652             
63653             
63654             if(format && useF){
63655                 
63656                 args = args ? ',' + args : "";
63657                  
63658                 if(format.substr(0, 5) != "this."){
63659                     format = "fm." + format + '(';
63660                 }else{
63661                     format = 'this.call("'+ format.substr(5) + '", ';
63662                     args = ", values";
63663                 }
63664                 
63665                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
63666             }
63667              
63668             if (args.length) {
63669                 // called with xxyx.yuu:(test,test)
63670                 // change to ()
63671                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
63672             }
63673             // raw.. - :raw modifier..
63674             return "'"+ sep + udef_st  + name + ")"+sep+"'";
63675             
63676         };
63677         var body;
63678         // branched to use + in gecko and [].join() in others
63679         if(Roo.isGecko){
63680             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
63681                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
63682                     "';};};";
63683         }else{
63684             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
63685             body.push(tpl.body.replace(/(\r\n|\n)/g,
63686                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
63687             body.push("'].join('');};};");
63688             body = body.join('');
63689         }
63690         
63691         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
63692        
63693         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
63694         eval(body);
63695         
63696         return this;
63697     },
63698
63699     applyTemplate : function(values){
63700         return this.master.compiled.call(this, values, {});
63701         //var s = this.subs;
63702     },
63703
63704     apply : function(){
63705         return this.applyTemplate.apply(this, arguments);
63706     }
63707
63708  });
63709
63710 Roo.XTemplate.from = function(el){
63711     el = Roo.getDom(el);
63712     return new Roo.XTemplate(el.value || el.innerHTML);
63713 };