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.rtf = {}; // namespace
44513 Roo.rtf.Hex = function(hex)
44514 {
44515     this.hexstr = hex;
44516 };
44517 Roo.rtf.Paragraph = function(opts)
44518 {
44519     this.content = []; ///??? is that used?
44520 };Roo.rtf.Span = function(opts)
44521 {
44522     this.value = opts.value;
44523 };
44524
44525 Roo.rtf.Group = function(parent)
44526 {
44527     // we dont want to acutally store parent - it will make debug a nightmare..
44528     this.content = [];
44529     this.cn  = [];
44530      
44531        
44532     
44533 };
44534
44535 Roo.rtf.Group.prototype = {
44536     ignorable : false,
44537     content: false,
44538     cn: false,
44539     addContent : function(node) {
44540         // could set styles...
44541         this.content.push(node);
44542     },
44543     addChild : function(cn)
44544     {
44545         this.cn.push(cn);
44546     },
44547     // only for images really...
44548     toDataURL : function()
44549     {
44550         var mimetype = false;
44551         switch(true) {
44552             case this.content.filter(function(a) { return a.value == 'pngblip' } ).length > 0: 
44553                 mimetype = "image/png";
44554                 break;
44555              case this.content.filter(function(a) { return a.value == 'jpegblip' } ).length > 0:
44556                 mimetype = "image/jpeg";
44557                 break;
44558             default :
44559                 return 'about:blank'; // ?? error?
44560         }
44561         
44562         
44563         var hexstring = this.content[this.content.length-1].value;
44564         
44565         return 'data:' + mimetype + ';base64,' + btoa(hexstring.match(/\w{2}/g).map(function(a) {
44566             return String.fromCharCode(parseInt(a, 16));
44567         }).join(""));
44568     }
44569     
44570 };
44571 // this looks like it's normally the {rtf{ .... }}
44572 Roo.rtf.Document = function()
44573 {
44574     // we dont want to acutally store parent - it will make debug a nightmare..
44575     this.rtlch  = [];
44576     this.content = [];
44577     this.cn = [];
44578     
44579 };
44580 Roo.extend(Roo.rtf.Document, Roo.rtf.Group, { 
44581     addChild : function(cn)
44582     {
44583         this.cn.push(cn);
44584         switch(cn.type) {
44585             case 'rtlch': // most content seems to be inside this??
44586             case 'listtext':
44587             case 'shpinst':
44588                 this.rtlch.push(cn);
44589                 return;
44590             default:
44591                 this[cn.type] = cn;
44592         }
44593         
44594     },
44595     
44596     getElementsByType : function(type)
44597     {
44598         var ret =  [];
44599         this._getElementsByType(type, ret, this.cn, 'rtf');
44600         return ret;
44601     },
44602     _getElementsByType : function (type, ret, search_array, path)
44603     {
44604         search_array.forEach(function(n,i) {
44605             if (n.type == type) {
44606                 n.path = path + '/' + n.type + ':' + i;
44607                 ret.push(n);
44608             }
44609             if (n.cn.length > 0) {
44610                 this._getElementsByType(type, ret, n.cn, path + '/' + n.type+':'+i);
44611             }
44612         },this);
44613     }
44614     
44615 });
44616  
44617 Roo.rtf.Ctrl = function(opts)
44618 {
44619     this.value = opts.value;
44620     this.param = opts.param;
44621 };
44622 /**
44623  *
44624  *
44625  * based on this https://github.com/iarna/rtf-parser
44626  * it's really only designed to extract pict from pasted RTF 
44627  *
44628  * usage:
44629  *
44630  *  var images = new Roo.rtf.Parser().parse(a_string).filter(function(g) { return g.type == 'pict'; });
44631  *  
44632  *
44633  */
44634
44635  
44636
44637
44638
44639 Roo.rtf.Parser = function(text) {
44640     //super({objectMode: true})
44641     this.text = '';
44642     this.parserState = this.parseText;
44643     
44644     // these are for interpeter...
44645     this.doc = {};
44646     ///this.parserState = this.parseTop
44647     this.groupStack = [];
44648     this.hexStore = [];
44649     this.doc = false;
44650     
44651     this.groups = []; // where we put the return.
44652     
44653     for (var ii = 0; ii < text.length; ++ii) {
44654         ++this.cpos;
44655         
44656         if (text[ii] === '\n') {
44657             ++this.row;
44658             this.col = 1;
44659         } else {
44660             ++this.col;
44661         }
44662         this.parserState(text[ii]);
44663     }
44664     
44665     
44666     
44667 };
44668 Roo.rtf.Parser.prototype = {
44669     text : '', // string being parsed..
44670     controlWord : '',
44671     controlWordParam :  '',
44672     hexChar : '',
44673     doc : false,
44674     group: false,
44675     groupStack : false,
44676     hexStore : false,
44677     
44678     
44679     cpos : 0, 
44680     row : 1, // reportin?
44681     col : 1, //
44682
44683      
44684     push : function (el)
44685     {
44686         var m = 'cmd'+ el.type;
44687         if (typeof(this[m]) == 'undefined') {
44688             Roo.log('invalid cmd:' + el.type);
44689             return;
44690         }
44691         this[m](el);
44692         //Roo.log(el);
44693     },
44694     flushHexStore : function()
44695     {
44696         if (this.hexStore.length < 1) {
44697             return;
44698         }
44699         var hexstr = this.hexStore.map(
44700             function(cmd) {
44701                 return cmd.value;
44702         }).join('');
44703         
44704         this.group.addContent( new Roo.rtf.Hex( hexstr ));
44705               
44706             
44707         this.hexStore.splice(0)
44708         
44709     },
44710     
44711     cmdgroupstart : function()
44712     {
44713         this.flushHexStore();
44714         if (this.group) {
44715             this.groupStack.push(this.group);
44716         }
44717          // parent..
44718         if (this.doc === false) {
44719             this.group = this.doc = new Roo.rtf.Document();
44720             return;
44721             
44722         }
44723         this.group = new Roo.rtf.Group(this.group);
44724     },
44725     cmdignorable : function()
44726     {
44727         this.flushHexStore();
44728         this.group.ignorable = true;
44729     },
44730     cmdendparagraph : function()
44731     {
44732         this.flushHexStore();
44733         this.group.addContent(new Roo.rtf.Paragraph());
44734     },
44735     cmdgroupend : function ()
44736     {
44737         this.flushHexStore();
44738         var endingGroup = this.group;
44739         
44740         
44741         this.group = this.groupStack.pop();
44742         if (this.group) {
44743             this.group.addChild(endingGroup);
44744         }
44745         
44746         
44747         
44748         var doc = this.group || this.doc;
44749         //if (endingGroup instanceof FontTable) {
44750         //  doc.fonts = endingGroup.table
44751         //} else if (endingGroup instanceof ColorTable) {
44752         //  doc.colors = endingGroup.table
44753         //} else if (endingGroup !== this.doc && !endingGroup.get('ignorable')) {
44754         if (endingGroup.ignorable === false) {
44755             //code
44756             this.groups.push(endingGroup);
44757            // Roo.log( endingGroup );
44758         }
44759             //Roo.each(endingGroup.content, function(item)) {
44760             //    doc.addContent(item);
44761             //}
44762             //process.emit('debug', 'GROUP END', endingGroup.type, endingGroup.get('ignorable'))
44763         //}
44764     },
44765     cmdtext : function (cmd)
44766     {
44767         this.flushHexStore();
44768         if (!this.group) { // an RTF fragment, missing the {\rtf1 header
44769             //this.group = this.doc
44770         }
44771         this.group.addContent(new Roo.rtf.Span(cmd));
44772     },
44773     cmdcontrolword : function (cmd)
44774     {
44775         this.flushHexStore();
44776         if (!this.group.type) {
44777             this.group.type = cmd.value;
44778             return;
44779         }
44780         this.group.addContent(new Roo.rtf.Ctrl(cmd));
44781         // we actually don't care about ctrl words...
44782         return ;
44783         /*
44784         var method = 'ctrl$' + cmd.value.replace(/-(.)/g, (_, char) => char.toUpperCase())
44785         if (this[method]) {
44786             this[method](cmd.param)
44787         } else {
44788             if (!this.group.get('ignorable')) process.emit('debug', method, cmd.param)
44789         }
44790         */
44791     },
44792     cmdhexchar : function(cmd) {
44793         this.hexStore.push(cmd);
44794     },
44795     cmderror : function(cmd) {
44796         throw new Exception (cmd.value);
44797     },
44798     
44799     /*
44800       _flush (done) {
44801         if (this.text !== '\u0000') this.emitText()
44802         done()
44803       }
44804       */
44805       
44806       
44807     parseText : function(c)
44808     {
44809         if (c === '\\') {
44810             this.parserState = this.parseEscapes;
44811         } else if (c === '{') {
44812             this.emitStartGroup();
44813         } else if (c === '}') {
44814             this.emitEndGroup();
44815         } else if (c === '\x0A' || c === '\x0D') {
44816             // cr/lf are noise chars
44817         } else {
44818             this.text += c;
44819         }
44820     },
44821     
44822     parseEscapes: function (c)
44823     {
44824         if (c === '\\' || c === '{' || c === '}') {
44825             this.text += c;
44826             this.parserState = this.parseText;
44827         } else {
44828             this.parserState = this.parseControlSymbol;
44829             this.parseControlSymbol(c);
44830         }
44831     },
44832     parseControlSymbol: function(c)
44833     {
44834         if (c === '~') {
44835             this.text += '\u00a0'; // nbsp
44836             this.parserState = this.parseText
44837         } else if (c === '-') {
44838              this.text += '\u00ad'; // soft hyphen
44839         } else if (c === '_') {
44840             this.text += '\u2011'; // non-breaking hyphen
44841         } else if (c === '*') {
44842             this.emitIgnorable();
44843             this.parserState = this.parseText;
44844         } else if (c === "'") {
44845             this.parserState = this.parseHexChar;
44846         } else if (c === '|') { // formula cacter
44847             this.emitFormula();
44848             this.parserState = this.parseText;
44849         } else if (c === ':') { // subentry in an index entry
44850             this.emitIndexSubEntry();
44851             this.parserState = this.parseText;
44852         } else if (c === '\x0a') {
44853             this.emitEndParagraph();
44854             this.parserState = this.parseText;
44855         } else if (c === '\x0d') {
44856             this.emitEndParagraph();
44857             this.parserState = this.parseText;
44858         } else {
44859             this.parserState = this.parseControlWord;
44860             this.parseControlWord(c);
44861         }
44862     },
44863     parseHexChar: function (c)
44864     {
44865         if (/^[A-Fa-f0-9]$/.test(c)) {
44866             this.hexChar += c;
44867             if (this.hexChar.length >= 2) {
44868               this.emitHexChar();
44869               this.parserState = this.parseText;
44870             }
44871             return;
44872         }
44873         this.emitError("Invalid character \"" + c + "\" in hex literal.");
44874         this.parserState = this.parseText;
44875         
44876     },
44877     parseControlWord : function(c)
44878     {
44879         if (c === ' ') {
44880             this.emitControlWord();
44881             this.parserState = this.parseText;
44882         } else if (/^[-\d]$/.test(c)) {
44883             this.parserState = this.parseControlWordParam;
44884             this.controlWordParam += c;
44885         } else if (/^[A-Za-z]$/.test(c)) {
44886           this.controlWord += c;
44887         } else {
44888           this.emitControlWord();
44889           this.parserState = this.parseText;
44890           this.parseText(c);
44891         }
44892     },
44893     parseControlWordParam : function (c) {
44894         if (/^\d$/.test(c)) {
44895           this.controlWordParam += c;
44896         } else if (c === ' ') {
44897           this.emitControlWord();
44898           this.parserState = this.parseText;
44899         } else {
44900           this.emitControlWord();
44901           this.parserState = this.parseText;
44902           this.parseText(c);
44903         }
44904     },
44905     
44906     
44907     
44908     
44909     emitText : function () {
44910         if (this.text === '') {
44911             return;
44912         }
44913         this.push({
44914             type: 'text',
44915             value: this.text,
44916             pos: this.cpos,
44917             row: this.row,
44918             col: this.col
44919         });
44920         this.text = ''
44921     },
44922     emitControlWord : function ()
44923     {
44924         this.emitText();
44925         if (this.controlWord === '') {
44926             this.emitError('empty control word');
44927         } else {
44928             this.push({
44929                   type: 'controlword',
44930                   value: this.controlWord,
44931                   param: this.controlWordParam !== '' && Number(this.controlWordParam),
44932                   pos: this.cpos,
44933                   row: this.row,
44934                   col: this.col
44935             });
44936         }
44937         this.controlWord = '';
44938         this.controlWordParam = '';
44939     },
44940     emitStartGroup : function ()
44941     {
44942         this.emitText();
44943         this.push({
44944             type: 'groupstart',
44945             pos: this.cpos,
44946             row: this.row,
44947             col: this.col
44948         });
44949     },
44950     emitEndGroup : function ()
44951     {
44952         this.emitText();
44953         this.push({
44954             type: 'groupend',
44955             pos: this.cpos,
44956             row: this.row,
44957             col: this.col
44958         });
44959     },
44960     emitIgnorable : function ()
44961     {
44962         this.emitText();
44963         this.push({
44964             type: 'ignorable',
44965             pos: this.cpos,
44966             row: this.row,
44967             col: this.col
44968         });
44969     },
44970     emitHexChar : function ()
44971     {
44972         this.emitText();
44973         this.push({
44974             type: 'hexchar',
44975             value: this.hexChar,
44976             pos: this.cpos,
44977             row: this.row,
44978             col: this.col
44979         });
44980         this.hexChar = ''
44981     },
44982     emitError : function (message)
44983     {
44984       this.emitText();
44985       this.push({
44986             type: 'error',
44987             value: message,
44988             row: this.row,
44989             col: this.col,
44990             char: this.cpos //,
44991             //stack: new Error().stack
44992         });
44993     },
44994     emitEndParagraph : function () {
44995         this.emitText();
44996         this.push({
44997             type: 'endparagraph',
44998             pos: this.cpos,
44999             row: this.row,
45000             col: this.col
45001         });
45002     }
45003      
45004 } ;Roo.htmleditor = {}; 
45005 /**
45006  * @class Roo.htmleditor.Filter
45007  * Base Class for filtering htmleditor stuff. - do not use this directly - extend it.
45008  * @cfg {DomElement} node The node to iterate and filter
45009  * @cfg {boolean|String|Array} tag Tags to replace 
45010  * @constructor
45011  * Create a new Filter.
45012  * @param {Object} config Configuration options
45013  */
45014
45015
45016
45017 Roo.htmleditor.Filter = function(cfg) {
45018     Roo.apply(this.cfg);
45019     // this does not actually call walk as it's really just a abstract class
45020 }
45021
45022
45023 Roo.htmleditor.Filter.prototype = {
45024     
45025     node: false,
45026     
45027     tag: false,
45028
45029     // overrride to do replace comments.
45030     replaceComment : false,
45031     
45032     // overrride to do replace or do stuff with tags..
45033     replaceTag : false,
45034     
45035     walk : function(dom)
45036     {
45037         Roo.each( Array.from(dom.childNodes), function( e ) {
45038             switch(true) {
45039                 
45040                 case e.nodeType == 8 && typeof(this.replaceComment) != 'undefined': // comment
45041                     this.replaceComment(e);
45042                     return;
45043                 
45044                 case e.nodeType != 1: //not a node.
45045                     return;
45046                 
45047                 case this.tag === true: // everything
45048                 case typeof(this.tag) == 'object' && this.tag.indexOf(e.tagName) > -1: // array and it matches.
45049                 case typeof(this.tag) == 'string' && this.tag == e.tagName: // array and it matches.
45050                     if (this.replaceTag && false === this.replaceTag(e)) {
45051                         return;
45052                     }
45053                     if (e.hasChildNodes()) {
45054                         this.walk(e);
45055                     }
45056                     return;
45057                 
45058                 default:    // tags .. that do not match.
45059                     if (e.hasChildNodes()) {
45060                         this.walk(e);
45061                     }
45062             }
45063             
45064         }, this);
45065         
45066     }
45067 }; 
45068
45069 /**
45070  * @class Roo.htmleditor.FilterAttributes
45071  * clean attributes and  styles including http:// etc.. in attribute
45072  * @constructor
45073 * Run a new Attribute Filter
45074 * @param {Object} config Configuration options
45075  */
45076 Roo.htmleditor.FilterAttributes = function(cfg)
45077 {
45078     Roo.apply(this, cfg);
45079     this.attrib_black = this.attrib_black || [];
45080     this.attrib_white = this.attrib_white || [];
45081
45082     this.attrib_clean = this.attrib_clean || [];
45083     this.style_white = this.style_white || [];
45084     this.style_black = this.style_black || [];
45085     this.walk(cfg.node);
45086 }
45087
45088 Roo.extend(Roo.htmleditor.FilterAttributes, Roo.htmleditor.Filter,
45089 {
45090     tag: true, // all tags
45091     
45092     attrib_black : false, // array
45093     attrib_clean : false,
45094     attrib_white : false,
45095
45096     style_white : false,
45097     style_black : false,
45098      
45099      
45100     replaceTag : function(node)
45101     {
45102         if (!node.attributes || !node.attributes.length) {
45103             return true;
45104         }
45105         
45106         for (var i = node.attributes.length-1; i > -1 ; i--) {
45107             var a = node.attributes[i];
45108             //console.log(a);
45109             if (this.attrib_white.length && this.attrib_white.indexOf(a.name.toLowerCase()) < 0) {
45110                 node.removeAttribute(a.name);
45111                 continue;
45112             }
45113             
45114             
45115             
45116             if (a.name.toLowerCase().substr(0,2)=='on')  {
45117                 node.removeAttribute(a.name);
45118                 continue;
45119             }
45120             
45121             
45122             if (this.attrib_black.indexOf(a.name.toLowerCase()) > -1) {
45123                 node.removeAttribute(a.name);
45124                 continue;
45125             }
45126             if (this.attrib_clean.indexOf(a.name.toLowerCase()) > -1) {
45127                 this.cleanAttr(node,a.name,a.value); // fixme..
45128                 continue;
45129             }
45130             if (a.name == 'style') {
45131                 this.cleanStyle(node,a.name,a.value);
45132                 continue;
45133             }
45134             /// clean up MS crap..
45135             // tecnically this should be a list of valid class'es..
45136             
45137             
45138             if (a.name == 'class') {
45139                 if (a.value.match(/^Mso/)) {
45140                     node.removeAttribute('class');
45141                 }
45142                 
45143                 if (a.value.match(/^body$/)) {
45144                     node.removeAttribute('class');
45145                 }
45146                 continue;
45147             }
45148             
45149             
45150             // style cleanup!?
45151             // class cleanup?
45152             
45153         }
45154         return true; // clean children
45155     },
45156         
45157     cleanAttr: function(node, n,v)
45158     {
45159         
45160         if (v.match(/^\./) || v.match(/^\//)) {
45161             return;
45162         }
45163         if (v.match(/^(http|https):\/\//)
45164             || v.match(/^mailto:/) 
45165             || v.match(/^ftp:/)
45166             || v.match(/^data:/)
45167             ) {
45168             return;
45169         }
45170         if (v.match(/^#/)) {
45171             return;
45172         }
45173         if (v.match(/^\{/)) { // allow template editing.
45174             return;
45175         }
45176 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
45177         node.removeAttribute(n);
45178         
45179     },
45180     cleanStyle : function(node,  n,v)
45181     {
45182         if (v.match(/expression/)) { //XSS?? should we even bother..
45183             node.removeAttribute(n);
45184             return;
45185         }
45186         
45187         var parts = v.split(/;/);
45188         var clean = [];
45189         
45190         Roo.each(parts, function(p) {
45191             p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
45192             if (!p.length) {
45193                 return true;
45194             }
45195             var l = p.split(':').shift().replace(/\s+/g,'');
45196             l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
45197             
45198             if ( this.style_black.length && (this.style_black.indexOf(l) > -1 || this.style_black.indexOf(l.toLowerCase()) > -1)) {
45199                 return true;
45200             }
45201             //Roo.log()
45202             // only allow 'c whitelisted system attributes'
45203             if ( this.style_white.length &&  style_white.indexOf(l) < 0 && style_white.indexOf(l.toLowerCase()) < 0 ) {
45204                 return true;
45205             }
45206             
45207             
45208             clean.push(p);
45209             return true;
45210         },this);
45211         if (clean.length) { 
45212             node.setAttribute(n, clean.join(';'));
45213         } else {
45214             node.removeAttribute(n);
45215         }
45216         
45217     }
45218         
45219         
45220         
45221     
45222 });/**
45223  * @class Roo.htmleditor.FilterBlack
45224  * remove blacklisted elements.
45225  * @constructor
45226  * Run a new Blacklisted Filter
45227  * @param {Object} config Configuration options
45228  */
45229
45230 Roo.htmleditor.FilterBlack = function(cfg)
45231 {
45232     Roo.apply(this, cfg);
45233     this.walk(cfg.node);
45234 }
45235
45236 Roo.extend(Roo.htmleditor.FilterBlack, Roo.htmleditor.Filter,
45237 {
45238     tag : true, // all elements.
45239    
45240     replace : function(n)
45241     {
45242         n.parentNode.removeChild(n);
45243     }
45244 });
45245 /**
45246  * @class Roo.htmleditor.FilterComment
45247  * remove comments.
45248  * @constructor
45249 * Run a new Comments Filter
45250 * @param {Object} config Configuration options
45251  */
45252 Roo.htmleditor.FilterComment = function(cfg)
45253 {
45254     this.walk(cfg.node);
45255 }
45256
45257 Roo.extend(Roo.htmleditor.FilterComment, Roo.htmleditor.Filter,
45258 {
45259   
45260     replaceComment : function(n)
45261     {
45262         n.parentNode.removeChild(n);
45263     }
45264 });/**
45265  * @class Roo.htmleditor.FilterKeepChildren
45266  * remove tags but keep children
45267  * @constructor
45268  * Run a new Keep Children Filter
45269  * @param {Object} config Configuration options
45270  */
45271
45272 Roo.htmleditor.FilterKeepChildren = function(cfg)
45273 {
45274     Roo.apply(this, cfg);
45275     if (this.tag === false) {
45276         return; // dont walk.. (you can use this to use this just to do a child removal on a single tag )
45277     }
45278     this.walk(cfg.node);
45279 }
45280
45281 Roo.extend(Roo.htmleditor.FilterKeepChildren, Roo.htmleditor.FilterBlack,
45282 {
45283     
45284   
45285     replaceTag : function(node)
45286     {
45287         // walk children...
45288         //Roo.log(node);
45289         var ar = Array.from(node.childNodes);
45290         //remove first..
45291         for (var i = 0; i < ar.length; i++) {
45292             if (ar[i].nodeType == 1) {
45293                 if (
45294                     (typeof(this.tag) == 'object' && this.tag.indexOf(ar[i].tagName) > -1)
45295                     || // array and it matches
45296                     (typeof(this.tag) == 'string' && this.tag == ar[i].tagName)
45297                 ) {
45298                     this.replaceTag(ar[i]); // child is blacklisted as well...
45299                     continue;
45300                 }
45301             }
45302         }  
45303         ar = Array.from(node.childNodes);
45304         for (var i = 0; i < ar.length; i++) {
45305          
45306             node.removeChild(ar[i]);
45307             // what if we need to walk these???
45308             node.parentNode.insertBefore(ar[i], node);
45309             if (this.tag !== false) {
45310                 this.walk(ar[i]);
45311                 
45312             }
45313         }
45314         node.parentNode.removeChild(node);
45315         return false; // don't walk children
45316         
45317         
45318     }
45319 });/**
45320  * @class Roo.htmleditor.FilterParagraph
45321  * paragraphs cause a nightmare for shared content - this filter is designed to be called ? at various points when editing
45322  * like on 'push' to remove the <p> tags and replace them with line breaks.
45323  * @constructor
45324  * Run a new Paragraph Filter
45325  * @param {Object} config Configuration options
45326  */
45327
45328 Roo.htmleditor.FilterParagraph = function(cfg)
45329 {
45330     // no need to apply config.
45331     this.walk(cfg.node);
45332 }
45333
45334 Roo.extend(Roo.htmleditor.FilterParagraph, Roo.htmleditor.Filter,
45335 {
45336     
45337      
45338     tag : 'P',
45339     
45340      
45341     replaceTag : function(node)
45342     {
45343         
45344         if (node.childNodes.length == 1 &&
45345             node.childNodes[0].nodeType == 3 &&
45346             node.childNodes[0].textContent.trim().length < 1
45347             ) {
45348             // remove and replace with '<BR>';
45349             node.parentNode.replaceChild(node.ownerDocument.createElement('BR'),node);
45350             return false; // no need to walk..
45351         }
45352         var ar = Array.from(node.childNodes);
45353         for (var i = 0; i < ar.length; i++) {
45354             node.removeChild(ar[i]);
45355             // what if we need to walk these???
45356             node.parentNode.insertBefore(ar[i], node);
45357         }
45358         // now what about this?
45359         // <p> &nbsp; </p>
45360         
45361         // double BR.
45362         node.parentNode.insertBefore(node.ownerDocument.createElement('BR'), node);
45363         node.parentNode.removeChild(node);
45364         
45365         return false;
45366
45367     }
45368     
45369 });/**
45370  * @class Roo.htmleditor.FilterSpan
45371  * filter span's with no attributes out..
45372  * @constructor
45373  * Run a new Span Filter
45374  * @param {Object} config Configuration options
45375  */
45376
45377 Roo.htmleditor.FilterSpan = function(cfg)
45378 {
45379     // no need to apply config.
45380     this.walk(cfg.node);
45381 }
45382
45383 Roo.extend(Roo.htmleditor.FilterSpan, Roo.htmleditor.FilterKeepChildren,
45384 {
45385      
45386     tag : 'SPAN',
45387      
45388  
45389     replaceTag : function(node)
45390     {
45391         if (node.attributes && node.attributes.length > 0) {
45392             return true; // walk if there are any.
45393         }
45394         Roo.htmleditor.FilterKeepChildren.prototype.replaceTag.call(this, node);
45395         return false;
45396      
45397     }
45398     
45399 });/**
45400  * @class Roo.htmleditor.FilterTableWidth
45401   try and remove table width data - as that frequently messes up other stuff.
45402  * 
45403  *      was cleanTableWidths.
45404  *
45405  * Quite often pasting from word etc.. results in tables with column and widths.
45406  * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
45407  *
45408  * @constructor
45409  * Run a new Table Filter
45410  * @param {Object} config Configuration options
45411  */
45412
45413 Roo.htmleditor.FilterTableWidth = function(cfg)
45414 {
45415     // no need to apply config.
45416     this.tag = ['TABLE', 'TD', 'TR', 'TH', 'THEAD', 'TBODY' ];
45417     this.walk(cfg.node);
45418 }
45419
45420 Roo.extend(Roo.htmleditor.FilterTableWidth, Roo.htmleditor.Filter,
45421 {
45422      
45423      
45424     
45425     replaceTag: function(node) {
45426         
45427         
45428       
45429         if (node.hasAttribute('width')) {
45430             node.removeAttribute('width');
45431         }
45432         
45433          
45434         if (node.hasAttribute("style")) {
45435             // pretty basic...
45436             
45437             var styles = node.getAttribute("style").split(";");
45438             var nstyle = [];
45439             Roo.each(styles, function(s) {
45440                 if (!s.match(/:/)) {
45441                     return;
45442                 }
45443                 var kv = s.split(":");
45444                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
45445                     return;
45446                 }
45447                 // what ever is left... we allow.
45448                 nstyle.push(s);
45449             });
45450             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45451             if (!nstyle.length) {
45452                 node.removeAttribute('style');
45453             }
45454         }
45455         
45456         return true; // continue doing children..
45457     }
45458 });/**
45459  * @class Roo.htmleditor.FilterWord
45460  * try and clean up all the mess that Word generates.
45461  * 
45462  * This is the 'nice version' - see 'Heavy' that white lists a very short list of elements, and multi-filters 
45463  
45464  * @constructor
45465  * Run a new Span Filter
45466  * @param {Object} config Configuration options
45467  */
45468
45469 Roo.htmleditor.FilterWord = function(cfg)
45470 {
45471     // no need to apply config.
45472     this.walk(cfg.node);
45473 }
45474
45475 Roo.extend(Roo.htmleditor.FilterWord, Roo.htmleditor.Filter,
45476 {
45477     tag: true,
45478      
45479     
45480     /**
45481      * Clean up MS wordisms...
45482      */
45483     replaceTag : function(node)
45484     {
45485          
45486         // no idea what this does - span with text, replaceds with just text.
45487         if(
45488                 node.nodeName == 'SPAN' &&
45489                 !node.hasAttributes() &&
45490                 node.childNodes.length == 1 &&
45491                 node.firstChild.nodeName == "#text"  
45492         ) {
45493             var textNode = node.firstChild;
45494             node.removeChild(textNode);
45495             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45496                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
45497             }
45498             node.parentNode.insertBefore(textNode, node);
45499             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
45500                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
45501             }
45502             
45503             node.parentNode.removeChild(node);
45504             return false; // dont do chidren - we have remove our node - so no need to do chdhilren?
45505         }
45506         
45507    
45508         
45509         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
45510             node.parentNode.removeChild(node);
45511             return false; // dont do chidlren
45512         }
45513         //Roo.log(node.tagName);
45514         // remove - but keep children..
45515         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
45516             //Roo.log('-- removed');
45517             while (node.childNodes.length) {
45518                 var cn = node.childNodes[0];
45519                 node.removeChild(cn);
45520                 node.parentNode.insertBefore(cn, node);
45521                 // move node to parent - and clean it..
45522                 this.replaceTag(cn);
45523             }
45524             node.parentNode.removeChild(node);
45525             /// no need to iterate chidlren = it's got none..
45526             //this.iterateChildren(node, this.cleanWord);
45527             return false; // no need to iterate children.
45528         }
45529         // clean styles
45530         if (node.className.length) {
45531             
45532             var cn = node.className.split(/\W+/);
45533             var cna = [];
45534             Roo.each(cn, function(cls) {
45535                 if (cls.match(/Mso[a-zA-Z]+/)) {
45536                     return;
45537                 }
45538                 cna.push(cls);
45539             });
45540             node.className = cna.length ? cna.join(' ') : '';
45541             if (!cna.length) {
45542                 node.removeAttribute("class");
45543             }
45544         }
45545         
45546         if (node.hasAttribute("lang")) {
45547             node.removeAttribute("lang");
45548         }
45549         
45550         if (node.hasAttribute("style")) {
45551             
45552             var styles = node.getAttribute("style").split(";");
45553             var nstyle = [];
45554             Roo.each(styles, function(s) {
45555                 if (!s.match(/:/)) {
45556                     return;
45557                 }
45558                 var kv = s.split(":");
45559                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
45560                     return;
45561                 }
45562                 // what ever is left... we allow.
45563                 nstyle.push(s);
45564             });
45565             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
45566             if (!nstyle.length) {
45567                 node.removeAttribute('style');
45568             }
45569         }
45570         return true; // do children
45571         
45572         
45573         
45574     }
45575 });
45576 /**
45577  * @class Roo.htmleditor.FilterStyleToTag
45578  * part of the word stuff... - certain 'styles' should be converted to tags.
45579  * eg.
45580  *   font-weight: bold -> bold
45581  *   ?? super / subscrit etc..
45582  * 
45583  * @constructor
45584 * Run a new style to tag filter.
45585 * @param {Object} config Configuration options
45586  */
45587 Roo.htmleditor.FilterStyleToTag = function(cfg)
45588 {
45589     
45590     this.tags = {
45591         B  : [ 'fontWeight' , 'bold'],
45592         I :  [ 'fontStyle' , 'italic'],
45593         //pre :  [ 'font-style' , 'italic'],
45594         // h1.. h6 ?? font-size?
45595         SUP : [ 'verticalAlign' , 'super' ],
45596         SUB : [ 'verticalAlign' , 'sub' ]
45597         
45598         
45599     };
45600     
45601     Roo.apply(this, cfg);
45602      
45603     
45604     this.walk(cfg.node);
45605     
45606     
45607     
45608 }
45609
45610
45611 Roo.extend(Roo.htmleditor.FilterStyleToTag, Roo.htmleditor.Filter,
45612 {
45613     tag: true, // all tags
45614     
45615     tags : false,
45616     
45617     
45618     replaceTag : function(node)
45619     {
45620         
45621         
45622         if (node.getAttribute("style") === null) {
45623             return true;
45624         }
45625         var inject = [];
45626         for (var k in this.tags) {
45627             if (node.style[this.tags[k][0]] == this.tags[k][1]) {
45628                 inject.push(k);
45629                 node.style.removeProperty(this.tags[k][0]);
45630             }
45631         }
45632         if (!inject.length) {
45633             return true; 
45634         }
45635         var cn = Array.from(node.childNodes);
45636         var nn = node;
45637         Roo.each(inject, function(t) {
45638             var nc = node.ownerDocument.createelement(t);
45639             nn.appendChild(nc);
45640             nn = nc;
45641         });
45642         for(var i = 0;i < cn.length;cn++) {
45643             node.removeChild(cn[i]);
45644             nn.appendChild(cn[i]);
45645         }
45646         return true /// iterate thru
45647     }
45648     
45649 })/**
45650  * @class Roo.htmleditor.FilterLongBr
45651  * BR/BR/BR - keep a maximum of 2...
45652  * @constructor
45653  * Run a new Long BR Filter
45654  * @param {Object} config Configuration options
45655  */
45656
45657 Roo.htmleditor.FilterLongBr = function(cfg)
45658 {
45659     // no need to apply config.
45660     this.walk(cfg.node);
45661 }
45662
45663 Roo.extend(Roo.htmleditor.FilterLongBr, Roo.htmleditor.Filter,
45664 {
45665     
45666      
45667     tag : 'BR',
45668     
45669      
45670     replaceTag : function(node)
45671     {
45672         
45673         var ps = node.nextSibling;
45674         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45675             ps = ps.nextSibling;
45676         }
45677         
45678         if (!ps &&  [ 'TD', 'TH', 'LI', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(node.parentNode.tagName) > -1) { 
45679             node.parentNode.removeChild(node); // remove last BR inside one fo these tags
45680             return false;
45681         }
45682         
45683         if (!ps || ps.nodeType != 1) {
45684             return false;
45685         }
45686         
45687         if (!ps || ps.tagName != 'BR') {
45688            
45689             return false;
45690         }
45691         
45692         
45693         
45694         
45695         
45696         if (!node.previousSibling) {
45697             return false;
45698         }
45699         var ps = node.previousSibling;
45700         
45701         while (ps && ps.nodeType == 3 && ps.nodeValue.trim().length < 1) {
45702             ps = ps.previousSibling;
45703         }
45704         if (!ps || ps.nodeType != 1) {
45705             return false;
45706         }
45707         // if header or BR before.. then it's a candidate for removal.. - as we only want '2' of these..
45708         if (!ps || [ 'BR', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].indexOf(ps.tagName) < 0) {
45709             return false;
45710         }
45711         
45712         node.parentNode.removeChild(node); // remove me...
45713         
45714         return false; // no need to do children
45715
45716     }
45717     
45718 });
45719 /**
45720  * @class Roo.htmleditor.Tidy
45721  * Tidy HTML 
45722  * @cfg {Roo.HtmlEditorCore} core the editor.
45723  * @constructor
45724  * Create a new Filter.
45725  * @param {Object} config Configuration options
45726  */
45727
45728
45729 Roo.htmleditor.Tidy = function(cfg) {
45730     Roo.apply(this, cfg);
45731     
45732     this.core.doc.body.innerHTML = this.tidy(this.core.doc.body, '');
45733      
45734 }
45735
45736 Roo.htmleditor.Tidy.toString = function(node)
45737 {
45738     return Roo.htmleditor.Tidy.prototype.tidy(node, '');
45739 }
45740
45741 Roo.htmleditor.Tidy.prototype = {
45742     
45743     
45744     wrap : function(s) {
45745         return s.replace(/\n/g, " ").replace(/(?![^\n]{1,80}$)([^\n]{1,80})\s/g, '$1\n');
45746     },
45747
45748     
45749     tidy : function(node, indent) {
45750      
45751         if  (node.nodeType == 3) {
45752             // text.
45753             
45754             
45755             return indent === false ? node.nodeValue : this.wrap(node.nodeValue.trim()).split("\n").join("\n" + indent);
45756                 
45757             
45758         }
45759         
45760         if  (node.nodeType != 1) {
45761             return '';
45762         }
45763         
45764         
45765         
45766         if (node.tagName == 'BODY') {
45767             
45768             return this.cn(node, '');
45769         }
45770              
45771              // Prints the node tagName, such as <A>, <IMG>, etc
45772         var ret = "<" + node.tagName +  this.attr(node) ;
45773         
45774         // elements with no children..
45775         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(node.tagName) > -1) {
45776                 return ret + '/>';
45777         }
45778         ret += '>';
45779         
45780         
45781         var cindent = indent === false ? '' : (indent + '  ');
45782         // tags where we will not pad the children.. (inline text tags etc..)
45783         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN', 'B', 'I', 'S'].indexOf(node.tagName) > -1) { // or code?
45784             cindent = false;
45785             
45786             
45787         }
45788         
45789         var cn = this.cn(node, cindent );
45790         
45791         return ret + cn  + '</' + node.tagName + '>';
45792         
45793     },
45794     cn: function(node, indent)
45795     {
45796         var ret = [];
45797         
45798         var ar = Array.from(node.childNodes);
45799         for (var i = 0 ; i < ar.length ; i++) {
45800             
45801             
45802             
45803             if (indent !== false   // indent==false preservies everything
45804                 && i > 0
45805                 && ar[i].nodeType == 3 
45806                 && ar[i].nodeValue.length > 0
45807                 && ar[i].nodeValue.match(/^\s+/)
45808             ) {
45809                 if (ret.length && ret[ret.length-1] == "\n" + indent) {
45810                     ret.pop(); // remove line break from last?
45811                 }
45812                 
45813                 ret.push(" "); // add a space if i'm a text item with a space at the front, as tidy will strip spaces.
45814             }
45815             if (indent !== false
45816                 && ar[i].nodeType == 1 // element - and indent is not set... 
45817             ) {
45818                 ret.push("\n" + indent); 
45819             }
45820             
45821             ret.push(this.tidy(ar[i], indent));
45822             // text + trailing indent 
45823             if (indent !== false
45824                 && ar[i].nodeType == 3
45825                 && ar[i].nodeValue.length > 0
45826                 && ar[i].nodeValue.match(/\s+$/)
45827             ){
45828                 ret.push("\n" + indent); 
45829             }
45830             
45831             
45832             
45833             
45834         }
45835         // what if all text?
45836         
45837         
45838         return ret.join('');
45839     },
45840     
45841          
45842         
45843     attr : function(node)
45844     {
45845         var attr = [];
45846         for(i = 0; i < node.attributes.length;i++) {
45847             
45848             // skip empty values?
45849             if (!node.attributes.item(i).value.length) {
45850                 continue;
45851             }
45852             attr.push(  node.attributes.item(i).name + '="' +
45853                     Roo.util.Format.htmlEncode(node.attributes.item(i).value) + '"'
45854             );
45855         }
45856         return attr.length ? (' ' + attr.join(' ') ) : '';
45857         
45858     }
45859     
45860     
45861     
45862 }
45863 /**
45864  * @class Roo.htmleditor.KeyEnter
45865  * Handle Enter press..
45866  * @cfg {Roo.HtmlEditorCore} core the editor.
45867  * @constructor
45868  * Create a new Filter.
45869  * @param {Object} config Configuration options
45870  */
45871
45872
45873
45874 Roo.htmleditor.KeyEnter = function(cfg) {
45875     Roo.apply(this, cfg);
45876     // this does not actually call walk as it's really just a abstract class
45877  
45878     Roo.get(this.core.doc.body).on('keypress', this.keypress, this);
45879 }
45880
45881
45882 Roo.htmleditor.KeyEnter.prototype = {
45883     
45884     core : false,
45885     
45886     keypress : function(e) {
45887         if (e.charCode != 13) {
45888             return true;
45889         }
45890         e.preventDefault();
45891         // https://stackoverflow.com/questions/18552336/prevent-contenteditable-adding-div-on-enter-chrome
45892         var doc = this.core.doc;
45893         
45894         var docFragment = doc.createDocumentFragment();
45895     
45896         //add a new line
45897         var newEle = doc.createTextNode('\n');
45898         docFragment.appendChild(newEle);
45899     
45900     
45901         var range = this.core.win.getSelection().getRangeAt(0);
45902         var n = range.commonAncestorContainer ;
45903         while (n && n.nodeType != 1) {
45904             n  = n.parentNode;
45905         }
45906         var li = false;
45907         if (n && n.tagName == 'UL') {
45908             li = doc.createElement('LI');
45909             n.appendChild(li);
45910             
45911         }
45912         if (n && n.tagName == 'LI') {
45913             li = doc.createElement('LI');
45914             if (n.nextSibling) {
45915                 n.parentNode.insertBefore(li, n.firstSibling);
45916                 
45917             } else {
45918                 n.parentNode.appendChild(li);
45919             }
45920         }
45921         if (li) {   
45922             range = doc.createRange();
45923             range.setStartAfter(li);
45924             range.collapse(true);
45925         
45926             //make the cursor there
45927             var sel = this.core.win.getSelection();
45928             sel.removeAllRanges();
45929             sel.addRange(range);
45930             return false;
45931             
45932             
45933         }
45934         //add the br, or p, or something else
45935         newEle = doc.createElement('br');
45936         docFragment.appendChild(newEle);
45937     
45938         //make the br replace selection
45939         
45940         range.deleteContents();
45941         
45942         range.insertNode(docFragment);
45943     
45944         //create a new range
45945         range = doc.createRange();
45946         range.setStartAfter(newEle);
45947         range.collapse(true);
45948     
45949         //make the cursor there
45950         var sel = this.core.win.getSelection();
45951         sel.removeAllRanges();
45952         sel.addRange(range);
45953     
45954         return false;
45955          
45956     }
45957 };
45958      
45959 /**
45960  * @class Roo.htmleditor.Block
45961  * Base class for html editor blocks - do not use it directly .. extend it..
45962  * @cfg {DomElement} node The node to apply stuff to.
45963  * @cfg {String} friendly_name the name that appears in the context bar about this block
45964  * @cfg {Object} Context menu - see Roo.form.HtmlEditor.ToolbarContext
45965  
45966  * @constructor
45967  * Create a new Filter.
45968  * @param {Object} config Configuration options
45969  */
45970
45971 Roo.htmleditor.Block  = function(cfg)
45972 {
45973     // do nothing .. should not be called really.
45974 }
45975
45976 Roo.htmleditor.Block.factory = function(node)
45977 {
45978     var cls = Roo.htmleditor['Block' + Roo.get(node).attr('data-block')];
45979     if (typeof(cls) == 'undefined') {
45980         Roo.log("OOps missing block : " + 'Block' + Roo.get(node).attr('data-block'));
45981         return false;
45982     }
45983     return new cls({ node: node });  /// should trigger update element
45984 }
45985
45986
45987 Roo.htmleditor.Block.prototype = {
45988     
45989      // used by context menu
45990     friendly_name : 'Image with caption',
45991     
45992     context : false,
45993     /**
45994      * Update a node with values from this object
45995      * @param {DomElement} node
45996      */
45997     updateElement : function(node)
45998     {
45999         Roo.DomHelper.update(node, this.toObject());
46000     },
46001      /**
46002      * convert to plain HTML for calling insertAtCursor..
46003      */
46004     toHTML : function()
46005     {
46006         return Roo.DomHelper.markup(this.toObject());
46007     },
46008     /**
46009      * used by readEleemnt to extract data from a node
46010      * may need improving as it's pretty basic
46011      
46012      * @param {DomElement} node
46013      * @param {String} tag - tag to find, eg. IMG ?? might be better to use DomQuery ?
46014      * @param {String} attribute (use html - for contents, or style for using next param as style)
46015      * @param {String} style the style property - eg. text-align
46016      */
46017     getVal : function(node, tag, attr, style)
46018     {
46019         var n = node;
46020         if (n.tagName != tag.toUpperCase()) {
46021             // in theory we could do figure[3] << 3rd figure? or some more complex search..?
46022             // but kiss for now.
46023             n = node.getElementsByTagName(tag).item(0);
46024         }
46025         if (attr == 'html') {
46026             return n.innerHTML;
46027         }
46028         if (attr == 'style') {
46029             return Roo.get(n).getStyle(style);
46030         }
46031         
46032         return Roo.get(n).attr(attr);
46033             
46034     },
46035     /**
46036      * create a DomHelper friendly object - for use with 
46037      * Roo.DomHelper.markup / overwrite / etc..
46038      * (override this)
46039      */
46040     toObject : function()
46041     {
46042         return {};
46043     },
46044       /**
46045      * Read a node that has a 'data-block' property - and extract the values from it.
46046      * @param {DomElement} node - the node
46047      */
46048     readElement : function(node)
46049     {
46050         
46051     } 
46052     
46053     
46054 }
46055
46056  
46057
46058 /**
46059  * @class Roo.htmleditor.BlockFigure
46060  * Block that has an image and a figcaption
46061  * @cfg {String} image_src the url for the image
46062  * @cfg {String} align (left|right) alignment for the block default left
46063  * @cfg {String} text_align (left|right) alignment for the text caption default left.
46064  * @cfg {String} caption the text to appear below  (and in the alt tag)
46065  * @cfg {String|number} image_width the width of the image number or %?
46066  * @cfg {String|number} image_height the height of the image number or %?
46067  * 
46068  * @constructor
46069  * Create a new Filter.
46070  * @param {Object} config Configuration options
46071  */
46072
46073 Roo.htmleditor.BlockFigure = function(cfg)
46074 {
46075     if (cfg.node) {
46076         this.readElement(cfg.node);
46077         this.updateElement(cfg.node);
46078     }
46079     Roo.apply(this, cfg);
46080 }
46081 Roo.extend(Roo.htmleditor.BlockFigure, Roo.htmleditor.Block, {
46082  
46083     
46084     // setable values.
46085     image_src: '',
46086     
46087     align: 'left',
46088     caption : '',
46089     text_align: 'left',
46090     
46091     width : '46%',
46092     margin: '2%',
46093     
46094     // used by context menu
46095     friendly_name : 'Image with caption',
46096     
46097     context : { // ?? static really
46098         width : {
46099             title: "Width",
46100             width: 40
46101             // ?? number
46102         },
46103         margin : {
46104             title: "Margin",
46105             width: 40
46106             // ?? number
46107         },
46108         align: {
46109             title: "Align",
46110             opts : [[ "left"],[ "right"]],
46111             width : 80
46112             
46113         },
46114         text_align: {
46115             title: "Caption Align",
46116             opts : [ [ "left"],[ "right"],[ "center"]],
46117             width : 80
46118         },
46119         
46120        
46121         image_src : {
46122             title: "Src",
46123             width: 220
46124         }
46125     },
46126     /**
46127      * create a DomHelper friendly object - for use with
46128      * Roo.DomHelper.markup / overwrite / etc..
46129      */
46130     toObject : function()
46131     {
46132         var d = document.createElement('div');
46133         d.innerHTML = this.caption;
46134         
46135         return {
46136             tag: 'figure',
46137             'data-block' : 'Figure',
46138             contenteditable : 'false',
46139             style : {
46140                 display: 'table',
46141                 float :  this.align ,
46142                 width :  this.width,
46143                 margin:  this.margin
46144             },
46145             cn : [
46146                 {
46147                     tag : 'img',
46148                     src : this.image_src,
46149                     alt : d.innerText.replace(/\n/g, " "), // removeHTML..
46150                     style: {
46151                         width: '100%'
46152                     }
46153                 },
46154                 {
46155                     tag: 'figcaption',
46156                     contenteditable : true,
46157                     style : {
46158                         'text-align': this.text_align
46159                     },
46160                     html : this.caption
46161                     
46162                 }
46163             ]
46164         };
46165     },
46166     
46167     readElement : function(node)
46168     {
46169         this.image_src = this.getVal(node, 'img', 'src');
46170         this.align = this.getVal(node, 'figure', 'style', 'float');
46171         this.caption = this.getVal(node, 'figcaption', 'html');
46172         this.text_align = this.getVal(node, 'figcaption', 'style','text-align');
46173         this.width = this.getVal(node, 'figure', 'style', 'width');
46174         this.margin = this.getVal(node, 'figure', 'style', 'margin');
46175         
46176     } 
46177     
46178   
46179    
46180      
46181     
46182     
46183     
46184     
46185 })
46186
46187 //<script type="text/javascript">
46188
46189 /*
46190  * Based  Ext JS Library 1.1.1
46191  * Copyright(c) 2006-2007, Ext JS, LLC.
46192  * LGPL
46193  *
46194  */
46195  
46196 /**
46197  * @class Roo.HtmlEditorCore
46198  * @extends Roo.Component
46199  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
46200  *
46201  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
46202  */
46203
46204 Roo.HtmlEditorCore = function(config){
46205     
46206     
46207     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
46208     
46209     
46210     this.addEvents({
46211         /**
46212          * @event initialize
46213          * Fires when the editor is fully initialized (including the iframe)
46214          * @param {Roo.HtmlEditorCore} this
46215          */
46216         initialize: true,
46217         /**
46218          * @event activate
46219          * Fires when the editor is first receives the focus. Any insertion must wait
46220          * until after this event.
46221          * @param {Roo.HtmlEditorCore} this
46222          */
46223         activate: true,
46224          /**
46225          * @event beforesync
46226          * Fires before the textarea is updated with content from the editor iframe. Return false
46227          * to cancel the sync.
46228          * @param {Roo.HtmlEditorCore} this
46229          * @param {String} html
46230          */
46231         beforesync: true,
46232          /**
46233          * @event beforepush
46234          * Fires before the iframe editor is updated with content from the textarea. Return false
46235          * to cancel the push.
46236          * @param {Roo.HtmlEditorCore} this
46237          * @param {String} html
46238          */
46239         beforepush: true,
46240          /**
46241          * @event sync
46242          * Fires when the textarea is updated with content from the editor iframe.
46243          * @param {Roo.HtmlEditorCore} this
46244          * @param {String} html
46245          */
46246         sync: true,
46247          /**
46248          * @event push
46249          * Fires when the iframe editor is updated with content from the textarea.
46250          * @param {Roo.HtmlEditorCore} this
46251          * @param {String} html
46252          */
46253         push: true,
46254         
46255         /**
46256          * @event editorevent
46257          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
46258          * @param {Roo.HtmlEditorCore} this
46259          */
46260         editorevent: true
46261         
46262     });
46263     
46264     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
46265     
46266     // defaults : white / black...
46267     this.applyBlacklists();
46268     
46269     
46270     
46271 };
46272
46273
46274 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
46275
46276
46277      /**
46278      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
46279      */
46280     
46281     owner : false,
46282     
46283      /**
46284      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
46285      *                        Roo.resizable.
46286      */
46287     resizable : false,
46288      /**
46289      * @cfg {Number} height (in pixels)
46290      */   
46291     height: 300,
46292    /**
46293      * @cfg {Number} width (in pixels)
46294      */   
46295     width: 500,
46296     
46297     /**
46298      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
46299      * 
46300      */
46301     stylesheets: false,
46302     
46303     /**
46304      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
46305      */
46306     allowComments: false,
46307     // id of frame..
46308     frameId: false,
46309     
46310     // private properties
46311     validationEvent : false,
46312     deferHeight: true,
46313     initialized : false,
46314     activated : false,
46315     sourceEditMode : false,
46316     onFocus : Roo.emptyFn,
46317     iframePad:3,
46318     hideMode:'offsets',
46319     
46320     clearUp: true,
46321     
46322     // blacklist + whitelisted elements..
46323     black: false,
46324     white: false,
46325      
46326     bodyCls : '',
46327
46328     /**
46329      * Protected method that will not generally be called directly. It
46330      * is called when the editor initializes the iframe with HTML contents. Override this method if you
46331      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
46332      */
46333     getDocMarkup : function(){
46334         // body styles..
46335         var st = '';
46336         
46337         // inherit styels from page...?? 
46338         if (this.stylesheets === false) {
46339             
46340             Roo.get(document.head).select('style').each(function(node) {
46341                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46342             });
46343             
46344             Roo.get(document.head).select('link').each(function(node) { 
46345                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
46346             });
46347             
46348         } else if (!this.stylesheets.length) {
46349                 // simple..
46350                 st = '<style type="text/css">' +
46351                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46352                    '</style>';
46353         } else {
46354             for (var i in this.stylesheets) {
46355                 if (typeof(this.stylesheets[i]) != 'string') {
46356                     continue;
46357                 }
46358                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
46359             }
46360             
46361         }
46362         
46363         st +=  '<style type="text/css">' +
46364             'IMG { cursor: pointer } ' +
46365         '</style>';
46366
46367         var cls = 'roo-htmleditor-body';
46368         
46369         if(this.bodyCls.length){
46370             cls += ' ' + this.bodyCls;
46371         }
46372         
46373         return '<html><head>' + st  +
46374             //<style type="text/css">' +
46375             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
46376             //'</style>' +
46377             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
46378     },
46379
46380     // private
46381     onRender : function(ct, position)
46382     {
46383         var _t = this;
46384         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
46385         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
46386         
46387         
46388         this.el.dom.style.border = '0 none';
46389         this.el.dom.setAttribute('tabIndex', -1);
46390         this.el.addClass('x-hidden hide');
46391         
46392         
46393         
46394         if(Roo.isIE){ // fix IE 1px bogus margin
46395             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
46396         }
46397        
46398         
46399         this.frameId = Roo.id();
46400         
46401          
46402         
46403         var iframe = this.owner.wrap.createChild({
46404             tag: 'iframe',
46405             cls: 'form-control', // bootstrap..
46406             id: this.frameId,
46407             name: this.frameId,
46408             frameBorder : 'no',
46409             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
46410         }, this.el
46411         );
46412         
46413         
46414         this.iframe = iframe.dom;
46415
46416         this.assignDocWin();
46417         
46418         this.doc.designMode = 'on';
46419        
46420         this.doc.open();
46421         this.doc.write(this.getDocMarkup());
46422         this.doc.close();
46423
46424         
46425         var task = { // must defer to wait for browser to be ready
46426             run : function(){
46427                 //console.log("run task?" + this.doc.readyState);
46428                 this.assignDocWin();
46429                 if(this.doc.body || this.doc.readyState == 'complete'){
46430                     try {
46431                         this.doc.designMode="on";
46432                     } catch (e) {
46433                         return;
46434                     }
46435                     Roo.TaskMgr.stop(task);
46436                     this.initEditor.defer(10, this);
46437                 }
46438             },
46439             interval : 10,
46440             duration: 10000,
46441             scope: this
46442         };
46443         Roo.TaskMgr.start(task);
46444
46445     },
46446
46447     // private
46448     onResize : function(w, h)
46449     {
46450          Roo.log('resize: ' +w + ',' + h );
46451         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
46452         if(!this.iframe){
46453             return;
46454         }
46455         if(typeof w == 'number'){
46456             
46457             this.iframe.style.width = w + 'px';
46458         }
46459         if(typeof h == 'number'){
46460             
46461             this.iframe.style.height = h + 'px';
46462             if(this.doc){
46463                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
46464             }
46465         }
46466         
46467     },
46468
46469     /**
46470      * Toggles the editor between standard and source edit mode.
46471      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
46472      */
46473     toggleSourceEdit : function(sourceEditMode){
46474         
46475         this.sourceEditMode = sourceEditMode === true;
46476         
46477         if(this.sourceEditMode){
46478  
46479             Roo.get(this.iframe).addClass(['x-hidden','hide', 'd-none']);     //FIXME - what's the BS styles for these
46480             
46481         }else{
46482             Roo.get(this.iframe).removeClass(['x-hidden','hide', 'd-none']);
46483             //this.iframe.className = '';
46484             this.deferFocus();
46485         }
46486         //this.setSize(this.owner.wrap.getSize());
46487         //this.fireEvent('editmodechange', this, this.sourceEditMode);
46488     },
46489
46490     
46491   
46492
46493     /**
46494      * Protected method that will not generally be called directly. If you need/want
46495      * custom HTML cleanup, this is the method you should override.
46496      * @param {String} html The HTML to be cleaned
46497      * return {String} The cleaned HTML
46498      */
46499     cleanHtml : function(html){
46500         html = String(html);
46501         if(html.length > 5){
46502             if(Roo.isSafari){ // strip safari nonsense
46503                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
46504             }
46505         }
46506         if(html == '&nbsp;'){
46507             html = '';
46508         }
46509         return html;
46510     },
46511
46512     /**
46513      * HTML Editor -> Textarea
46514      * Protected method that will not generally be called directly. Syncs the contents
46515      * of the editor iframe with the textarea.
46516      */
46517     syncValue : function()
46518     {
46519         Roo.log("HtmlEditorCore:syncValue (EDITOR->TEXT)");
46520         if(this.initialized){
46521             var bd = (this.doc.body || this.doc.documentElement);
46522             //this.cleanUpPaste(); -- this is done else where and causes havoc..
46523             
46524             // not sure if this is really the place for this
46525             // the blocks are synced occasionaly - since we currently dont add listeners on the blocks
46526             // this has to update attributes that get duped.. like alt and caption..
46527             
46528             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46529                  Roo.htmleditor.Block.factory(e);
46530             },this);
46531             
46532             
46533             var div = document.createElement('div');
46534             div.innerHTML = bd.innerHTML;
46535             // remove content editable. (blocks)
46536             
46537            
46538             
46539             new Roo.htmleditor.FilterAttributes({node : div, attrib_black: [ 'contenteditable' ] });
46540             //?? tidy?
46541             var html = div.innerHTML;
46542             if(Roo.isSafari){
46543                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
46544                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
46545                 if(m && m[1]){
46546                     html = '<div style="'+m[0]+'">' + html + '</div>';
46547                 }
46548             }
46549             html = this.cleanHtml(html);
46550             // fix up the special chars.. normaly like back quotes in word...
46551             // however we do not want to do this with chinese..
46552             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
46553                 
46554                 var cc = match.charCodeAt();
46555
46556                 // Get the character value, handling surrogate pairs
46557                 if (match.length == 2) {
46558                     // It's a surrogate pair, calculate the Unicode code point
46559                     var high = match.charCodeAt(0) - 0xD800;
46560                     var low  = match.charCodeAt(1) - 0xDC00;
46561                     cc = (high * 0x400) + low + 0x10000;
46562                 }  else if (
46563                     (cc >= 0x4E00 && cc < 0xA000 ) ||
46564                     (cc >= 0x3400 && cc < 0x4E00 ) ||
46565                     (cc >= 0xf900 && cc < 0xfb00 )
46566                 ) {
46567                         return match;
46568                 }  
46569          
46570                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
46571                 return "&#" + cc + ";";
46572                 
46573                 
46574             });
46575             
46576             
46577              
46578             if(this.owner.fireEvent('beforesync', this, html) !== false){
46579                 this.el.dom.value = html;
46580                 this.owner.fireEvent('sync', this, html);
46581             }
46582         }
46583     },
46584
46585     /**
46586      * TEXTAREA -> EDITABLE
46587      * Protected method that will not generally be called directly. Pushes the value of the textarea
46588      * into the iframe editor.
46589      */
46590     pushValue : function()
46591     {
46592         Roo.log("HtmlEditorCore:pushValue (TEXT->EDITOR)");
46593         if(this.initialized){
46594             var v = this.el.dom.value.trim();
46595             
46596             
46597             if(this.owner.fireEvent('beforepush', this, v) !== false){
46598                 var d = (this.doc.body || this.doc.documentElement);
46599                 d.innerHTML = v;
46600                  
46601                 this.el.dom.value = d.innerHTML;
46602                 this.owner.fireEvent('push', this, v);
46603             }
46604             
46605             Roo.each(Roo.get(this.doc.body).query('*[data-block]'), function(e) {
46606                 
46607                 Roo.htmleditor.Block.factory(e);
46608                 
46609             },this);
46610             var lc = this.doc.body.lastChild;
46611             if (lc && lc.nodeType == 1 && lc.getAttribute("contenteditable") == "false") {
46612                 // add an extra line at the end.
46613                 this.doc.body.appendChild(this.doc.createElement('br'));
46614             }
46615             
46616             
46617         }
46618     },
46619
46620     // private
46621     deferFocus : function(){
46622         this.focus.defer(10, this);
46623     },
46624
46625     // doc'ed in Field
46626     focus : function(){
46627         if(this.win && !this.sourceEditMode){
46628             this.win.focus();
46629         }else{
46630             this.el.focus();
46631         }
46632     },
46633     
46634     assignDocWin: function()
46635     {
46636         var iframe = this.iframe;
46637         
46638          if(Roo.isIE){
46639             this.doc = iframe.contentWindow.document;
46640             this.win = iframe.contentWindow;
46641         } else {
46642 //            if (!Roo.get(this.frameId)) {
46643 //                return;
46644 //            }
46645 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46646 //            this.win = Roo.get(this.frameId).dom.contentWindow;
46647             
46648             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
46649                 return;
46650             }
46651             
46652             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
46653             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
46654         }
46655     },
46656     
46657     // private
46658     initEditor : function(){
46659         //console.log("INIT EDITOR");
46660         this.assignDocWin();
46661         
46662         
46663         
46664         this.doc.designMode="on";
46665         this.doc.open();
46666         this.doc.write(this.getDocMarkup());
46667         this.doc.close();
46668         
46669         var dbody = (this.doc.body || this.doc.documentElement);
46670         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
46671         // this copies styles from the containing element into thsi one..
46672         // not sure why we need all of this..
46673         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
46674         
46675         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
46676         //ss['background-attachment'] = 'fixed'; // w3c
46677         dbody.bgProperties = 'fixed'; // ie
46678         //Roo.DomHelper.applyStyles(dbody, ss);
46679         Roo.EventManager.on(this.doc, {
46680             //'mousedown': this.onEditorEvent,
46681             'mouseup': this.onEditorEvent,
46682             'dblclick': this.onEditorEvent,
46683             'click': this.onEditorEvent,
46684             'keyup': this.onEditorEvent,
46685             
46686             buffer:100,
46687             scope: this
46688         });
46689         Roo.EventManager.on(this.doc, {
46690             'paste': this.onPasteEvent,
46691             scope : this
46692         });
46693         if(Roo.isGecko){
46694             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
46695         }
46696         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
46697             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
46698         }
46699         this.initialized = true;
46700
46701         
46702         // initialize special key events - enter
46703         new Roo.htmleditor.KeyEnter({core : this});
46704         
46705          
46706         
46707         this.owner.fireEvent('initialize', this);
46708         this.pushValue();
46709     },
46710     
46711     onPasteEvent : function(e,v)
46712     {
46713         // I think we better assume paste is going to be a dirty load of rubish from word..
46714         
46715         // even pasting into a 'email version' of this widget will have to clean up that mess.
46716         var cd = (e.browserEvent.clipboardData || window.clipboardData);
46717         
46718         // check what type of paste - if it's an image, then handle it differently.
46719         if (cd.files.length > 0) {
46720             // pasting images?
46721             var urlAPI = (window.createObjectURL && window) || 
46722                 (window.URL && URL.revokeObjectURL && URL) || 
46723                 (window.webkitURL && webkitURL);
46724     
46725             var url = urlAPI.createObjectURL( cd.files[0]);
46726             this.insertAtCursor('<img src=" + url + ">');
46727             return false;
46728         }
46729         
46730         var html = cd.getData('text/html'); // clipboard event
46731         var parser = new Roo.rtf.Parser(cd.getData('text/rtf'));
46732         var images = parser.doc.getElementsByType('pict');
46733         Roo.log(images);
46734         //Roo.log(imgs);
46735         // fixme..
46736         images = images.filter(function(g) { return !g.path.match(/^rtf\/(head|pgdsctbl|listtable)/); }) // ignore headers
46737                        .map(function(g) { return g.toDataURL(); });
46738         
46739         
46740         html = this.cleanWordChars(html);
46741         
46742         var d = (new DOMParser().parseFromString(html, 'text/html')).body;
46743         
46744         if (images.length > 0) {
46745             Roo.each(d.getElementsByTagName('img'), function(img, i) {
46746                 img.setAttribute('src', images[i]);
46747             });
46748         }
46749         
46750       
46751         new Roo.htmleditor.FilterStyleToTag({ node : d });
46752         new Roo.htmleditor.FilterAttributes({
46753             node : d,
46754             attrib_white : ['href', 'src', 'name', 'align'],
46755             attrib_clean : ['href', 'src' ] 
46756         });
46757         new Roo.htmleditor.FilterBlack({ node : d, tag : this.black});
46758         // should be fonts..
46759         new Roo.htmleditor.FilterKeepChildren({node : d, tag : [ 'FONT' ]} );
46760         new Roo.htmleditor.FilterParagraph({ node : d });
46761         new Roo.htmleditor.FilterSpan({ node : d });
46762         new Roo.htmleditor.FilterLongBr({ node : d });
46763         
46764         
46765         
46766         this.insertAtCursor(d.innerHTML);
46767         
46768         e.preventDefault();
46769         return false;
46770         // default behaveiour should be our local cleanup paste? (optional?)
46771         // for simple editor - we want to hammer the paste and get rid of everything... - so over-rideable..
46772         //this.owner.fireEvent('paste', e, v);
46773     },
46774     // private
46775     onDestroy : function(){
46776         
46777         
46778         
46779         if(this.rendered){
46780             
46781             //for (var i =0; i < this.toolbars.length;i++) {
46782             //    // fixme - ask toolbars for heights?
46783             //    this.toolbars[i].onDestroy();
46784            // }
46785             
46786             //this.wrap.dom.innerHTML = '';
46787             //this.wrap.remove();
46788         }
46789     },
46790
46791     // private
46792     onFirstFocus : function(){
46793         
46794         this.assignDocWin();
46795         
46796         
46797         this.activated = true;
46798          
46799     
46800         if(Roo.isGecko){ // prevent silly gecko errors
46801             this.win.focus();
46802             var s = this.win.getSelection();
46803             if(!s.focusNode || s.focusNode.nodeType != 3){
46804                 var r = s.getRangeAt(0);
46805                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
46806                 r.collapse(true);
46807                 this.deferFocus();
46808             }
46809             try{
46810                 this.execCmd('useCSS', true);
46811                 this.execCmd('styleWithCSS', false);
46812             }catch(e){}
46813         }
46814         this.owner.fireEvent('activate', this);
46815     },
46816
46817     // private
46818     adjustFont: function(btn){
46819         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
46820         //if(Roo.isSafari){ // safari
46821         //    adjust *= 2;
46822        // }
46823         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
46824         if(Roo.isSafari){ // safari
46825             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
46826             v =  (v < 10) ? 10 : v;
46827             v =  (v > 48) ? 48 : v;
46828             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
46829             
46830         }
46831         
46832         
46833         v = Math.max(1, v+adjust);
46834         
46835         this.execCmd('FontSize', v  );
46836     },
46837
46838     onEditorEvent : function(e)
46839     {
46840         this.owner.fireEvent('editorevent', this, e);
46841       //  this.updateToolbar();
46842         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
46843     },
46844
46845     insertTag : function(tg)
46846     {
46847         // could be a bit smarter... -> wrap the current selected tRoo..
46848         if (tg.toLowerCase() == 'span' ||
46849             tg.toLowerCase() == 'code' ||
46850             tg.toLowerCase() == 'sup' ||
46851             tg.toLowerCase() == 'sub' 
46852             ) {
46853             
46854             range = this.createRange(this.getSelection());
46855             var wrappingNode = this.doc.createElement(tg.toLowerCase());
46856             wrappingNode.appendChild(range.extractContents());
46857             range.insertNode(wrappingNode);
46858
46859             return;
46860             
46861             
46862             
46863         }
46864         this.execCmd("formatblock",   tg);
46865         
46866     },
46867     
46868     insertText : function(txt)
46869     {
46870         
46871         
46872         var range = this.createRange();
46873         range.deleteContents();
46874                //alert(Sender.getAttribute('label'));
46875                
46876         range.insertNode(this.doc.createTextNode(txt));
46877     } ,
46878     
46879      
46880
46881     /**
46882      * Executes a Midas editor command on the editor document and performs necessary focus and
46883      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
46884      * @param {String} cmd The Midas command
46885      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46886      */
46887     relayCmd : function(cmd, value){
46888         this.win.focus();
46889         this.execCmd(cmd, value);
46890         this.owner.fireEvent('editorevent', this);
46891         //this.updateToolbar();
46892         this.owner.deferFocus();
46893     },
46894
46895     /**
46896      * Executes a Midas editor command directly on the editor document.
46897      * For visual commands, you should use {@link #relayCmd} instead.
46898      * <b>This should only be called after the editor is initialized.</b>
46899      * @param {String} cmd The Midas command
46900      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
46901      */
46902     execCmd : function(cmd, value){
46903         this.doc.execCommand(cmd, false, value === undefined ? null : value);
46904         this.syncValue();
46905     },
46906  
46907  
46908    
46909     /**
46910      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
46911      * to insert tRoo.
46912      * @param {String} text | dom node.. 
46913      */
46914     insertAtCursor : function(text)
46915     {
46916         
46917         if(!this.activated){
46918             return;
46919         }
46920          
46921         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
46922             this.win.focus();
46923             
46924             
46925             // from jquery ui (MIT licenced)
46926             var range, node;
46927             var win = this.win;
46928             
46929             if (win.getSelection && win.getSelection().getRangeAt) {
46930                 
46931                 // delete the existing?
46932                 
46933                 this.createRange(this.getSelection()).deleteContents();
46934                 range = win.getSelection().getRangeAt(0);
46935                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
46936                 range.insertNode(node);
46937             } else if (win.document.selection && win.document.selection.createRange) {
46938                 // no firefox support
46939                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46940                 win.document.selection.createRange().pasteHTML(txt);
46941             } else {
46942                 // no firefox support
46943                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
46944                 this.execCmd('InsertHTML', txt);
46945             } 
46946             
46947             this.syncValue();
46948             
46949             this.deferFocus();
46950         }
46951     },
46952  // private
46953     mozKeyPress : function(e){
46954         if(e.ctrlKey){
46955             var c = e.getCharCode(), cmd;
46956           
46957             if(c > 0){
46958                 c = String.fromCharCode(c).toLowerCase();
46959                 switch(c){
46960                     case 'b':
46961                         cmd = 'bold';
46962                         break;
46963                     case 'i':
46964                         cmd = 'italic';
46965                         break;
46966                     
46967                     case 'u':
46968                         cmd = 'underline';
46969                         break;
46970                     
46971                     //case 'v':
46972                       //  this.cleanUpPaste.defer(100, this);
46973                       //  return;
46974                         
46975                 }
46976                 if(cmd){
46977                     this.win.focus();
46978                     this.execCmd(cmd);
46979                     this.deferFocus();
46980                     e.preventDefault();
46981                 }
46982                 
46983             }
46984         }
46985     },
46986
46987     // private
46988     fixKeys : function(){ // load time branching for fastest keydown performance
46989         if(Roo.isIE){
46990             return function(e){
46991                 var k = e.getKey(), r;
46992                 if(k == e.TAB){
46993                     e.stopEvent();
46994                     r = this.doc.selection.createRange();
46995                     if(r){
46996                         r.collapse(true);
46997                         r.pasteHTML('&#160;&#160;&#160;&#160;');
46998                         this.deferFocus();
46999                     }
47000                     return;
47001                 }
47002                 
47003                 if(k == e.ENTER){
47004                     r = this.doc.selection.createRange();
47005                     if(r){
47006                         var target = r.parentElement();
47007                         if(!target || target.tagName.toLowerCase() != 'li'){
47008                             e.stopEvent();
47009                             r.pasteHTML('<br/>');
47010                             r.collapse(false);
47011                             r.select();
47012                         }
47013                     }
47014                 }
47015                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47016                 //    this.cleanUpPaste.defer(100, this);
47017                 //    return;
47018                 //}
47019                 
47020                 
47021             };
47022         }else if(Roo.isOpera){
47023             return function(e){
47024                 var k = e.getKey();
47025                 if(k == e.TAB){
47026                     e.stopEvent();
47027                     this.win.focus();
47028                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
47029                     this.deferFocus();
47030                 }
47031                 //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47032                 //    this.cleanUpPaste.defer(100, this);
47033                  //   return;
47034                 //}
47035                 
47036             };
47037         }else if(Roo.isSafari){
47038             return function(e){
47039                 var k = e.getKey();
47040                 
47041                 if(k == e.TAB){
47042                     e.stopEvent();
47043                     this.execCmd('InsertText','\t');
47044                     this.deferFocus();
47045                     return;
47046                 }
47047                //if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
47048                  //   this.cleanUpPaste.defer(100, this);
47049                  //   return;
47050                // }
47051                 
47052              };
47053         }
47054     }(),
47055     
47056     getAllAncestors: function()
47057     {
47058         var p = this.getSelectedNode();
47059         var a = [];
47060         if (!p) {
47061             a.push(p); // push blank onto stack..
47062             p = this.getParentElement();
47063         }
47064         
47065         
47066         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
47067             a.push(p);
47068             p = p.parentNode;
47069         }
47070         a.push(this.doc.body);
47071         return a;
47072     },
47073     lastSel : false,
47074     lastSelNode : false,
47075     
47076     
47077     getSelection : function() 
47078     {
47079         this.assignDocWin();
47080         return Roo.isIE ? this.doc.selection : this.win.getSelection();
47081     },
47082     /**
47083      * Select a dom node
47084      * @param {DomElement} node the node to select
47085      */
47086     selectNode : function(node)
47087     {
47088         
47089             var nodeRange = node.ownerDocument.createRange();
47090             try {
47091                 nodeRange.selectNode(node);
47092             } catch (e) {
47093                 nodeRange.selectNodeContents(node);
47094             }
47095             //nodeRange.collapse(true);
47096             var s = this.win.getSelection();
47097             s.removeAllRanges();
47098             s.addRange(nodeRange);
47099     },
47100     
47101     getSelectedNode: function() 
47102     {
47103         // this may only work on Gecko!!!
47104         
47105         // should we cache this!!!!
47106         
47107         
47108         
47109          
47110         var range = this.createRange(this.getSelection()).cloneRange();
47111         
47112         if (Roo.isIE) {
47113             var parent = range.parentElement();
47114             while (true) {
47115                 var testRange = range.duplicate();
47116                 testRange.moveToElementText(parent);
47117                 if (testRange.inRange(range)) {
47118                     break;
47119                 }
47120                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
47121                     break;
47122                 }
47123                 parent = parent.parentElement;
47124             }
47125             return parent;
47126         }
47127         
47128         // is ancestor a text element.
47129         var ac =  range.commonAncestorContainer;
47130         if (ac.nodeType == 3) {
47131             ac = ac.parentNode;
47132         }
47133         
47134         var ar = ac.childNodes;
47135          
47136         var nodes = [];
47137         var other_nodes = [];
47138         var has_other_nodes = false;
47139         for (var i=0;i<ar.length;i++) {
47140             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
47141                 continue;
47142             }
47143             // fullly contained node.
47144             
47145             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
47146                 nodes.push(ar[i]);
47147                 continue;
47148             }
47149             
47150             // probably selected..
47151             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
47152                 other_nodes.push(ar[i]);
47153                 continue;
47154             }
47155             // outer..
47156             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
47157                 continue;
47158             }
47159             
47160             
47161             has_other_nodes = true;
47162         }
47163         if (!nodes.length && other_nodes.length) {
47164             nodes= other_nodes;
47165         }
47166         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
47167             return false;
47168         }
47169         
47170         return nodes[0];
47171     },
47172     createRange: function(sel)
47173     {
47174         // this has strange effects when using with 
47175         // top toolbar - not sure if it's a great idea.
47176         //this.editor.contentWindow.focus();
47177         if (typeof sel != "undefined") {
47178             try {
47179                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
47180             } catch(e) {
47181                 return this.doc.createRange();
47182             }
47183         } else {
47184             return this.doc.createRange();
47185         }
47186     },
47187     getParentElement: function()
47188     {
47189         
47190         this.assignDocWin();
47191         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
47192         
47193         var range = this.createRange(sel);
47194          
47195         try {
47196             var p = range.commonAncestorContainer;
47197             while (p.nodeType == 3) { // text node
47198                 p = p.parentNode;
47199             }
47200             return p;
47201         } catch (e) {
47202             return null;
47203         }
47204     
47205     },
47206     /***
47207      *
47208      * Range intersection.. the hard stuff...
47209      *  '-1' = before
47210      *  '0' = hits..
47211      *  '1' = after.
47212      *         [ -- selected range --- ]
47213      *   [fail]                        [fail]
47214      *
47215      *    basically..
47216      *      if end is before start or  hits it. fail.
47217      *      if start is after end or hits it fail.
47218      *
47219      *   if either hits (but other is outside. - then it's not 
47220      *   
47221      *    
47222      **/
47223     
47224     
47225     // @see http://www.thismuchiknow.co.uk/?p=64.
47226     rangeIntersectsNode : function(range, node)
47227     {
47228         var nodeRange = node.ownerDocument.createRange();
47229         try {
47230             nodeRange.selectNode(node);
47231         } catch (e) {
47232             nodeRange.selectNodeContents(node);
47233         }
47234     
47235         var rangeStartRange = range.cloneRange();
47236         rangeStartRange.collapse(true);
47237     
47238         var rangeEndRange = range.cloneRange();
47239         rangeEndRange.collapse(false);
47240     
47241         var nodeStartRange = nodeRange.cloneRange();
47242         nodeStartRange.collapse(true);
47243     
47244         var nodeEndRange = nodeRange.cloneRange();
47245         nodeEndRange.collapse(false);
47246     
47247         return rangeStartRange.compareBoundaryPoints(
47248                  Range.START_TO_START, nodeEndRange) == -1 &&
47249                rangeEndRange.compareBoundaryPoints(
47250                  Range.START_TO_START, nodeStartRange) == 1;
47251         
47252          
47253     },
47254     rangeCompareNode : function(range, node)
47255     {
47256         var nodeRange = node.ownerDocument.createRange();
47257         try {
47258             nodeRange.selectNode(node);
47259         } catch (e) {
47260             nodeRange.selectNodeContents(node);
47261         }
47262         
47263         
47264         range.collapse(true);
47265     
47266         nodeRange.collapse(true);
47267      
47268         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
47269         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
47270          
47271         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
47272         
47273         var nodeIsBefore   =  ss == 1;
47274         var nodeIsAfter    = ee == -1;
47275         
47276         if (nodeIsBefore && nodeIsAfter) {
47277             return 0; // outer
47278         }
47279         if (!nodeIsBefore && nodeIsAfter) {
47280             return 1; //right trailed.
47281         }
47282         
47283         if (nodeIsBefore && !nodeIsAfter) {
47284             return 2;  // left trailed.
47285         }
47286         // fully contined.
47287         return 3;
47288     },
47289  
47290     cleanWordChars : function(input) {// change the chars to hex code
47291         
47292        var swapCodes  = [ 
47293             [    8211, "&#8211;" ], 
47294             [    8212, "&#8212;" ], 
47295             [    8216,  "'" ],  
47296             [    8217, "'" ],  
47297             [    8220, '"' ],  
47298             [    8221, '"' ],  
47299             [    8226, "*" ],  
47300             [    8230, "..." ]
47301         ]; 
47302         var output = input;
47303         Roo.each(swapCodes, function(sw) { 
47304             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
47305             
47306             output = output.replace(swapper, sw[1]);
47307         });
47308         
47309         return output;
47310     },
47311     
47312      
47313     
47314         
47315     
47316     cleanUpChild : function (node)
47317     {
47318         
47319         new Roo.htmleditor.FilterComment({node : node});
47320         new Roo.htmleditor.FilterAttributes({
47321                 node : node,
47322                 attrib_black : this.ablack,
47323                 attrib_clean : this.aclean,
47324                 style_white : this.cwhite,
47325                 style_black : this.cblack
47326         });
47327         new Roo.htmleditor.FilterBlack({ node : node, tag : this.black});
47328         new Roo.htmleditor.FilterKeepChildren({node : node, tag : this.tag_remove} );
47329          
47330         
47331     },
47332     
47333     /**
47334      * Clean up MS wordisms...
47335      * @deprecated - use filter directly
47336      */
47337     cleanWord : function(node)
47338     {
47339         new Roo.htmleditor.FilterWord({ node : node ? node : this.doc.body });
47340         
47341     },
47342    
47343     
47344     /**
47345
47346      * @deprecated - use filters
47347      */
47348     cleanTableWidths : function(node)
47349     {
47350         new Roo.htmleditor.FilterTableWidth({ node : node ? node : this.doc.body});
47351         
47352  
47353     },
47354     
47355      
47356         
47357     applyBlacklists : function()
47358     {
47359         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
47360         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
47361         
47362         this.aclean = typeof(this.owner.aclean) != 'undefined' && this.owner.aclean ? this.owner.aclean :  Roo.HtmlEditorCore.aclean;
47363         this.ablack = typeof(this.owner.ablack) != 'undefined' && this.owner.ablack ? this.owner.ablack :  Roo.HtmlEditorCore.ablack;
47364         this.tag_remove = typeof(this.owner.tag_remove) != 'undefined' && this.owner.tag_remove ? this.owner.tag_remove :  Roo.HtmlEditorCore.tag_remove;
47365         
47366         this.white = [];
47367         this.black = [];
47368         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
47369             if (b.indexOf(tag) > -1) {
47370                 return;
47371             }
47372             this.white.push(tag);
47373             
47374         }, this);
47375         
47376         Roo.each(w, function(tag) {
47377             if (b.indexOf(tag) > -1) {
47378                 return;
47379             }
47380             if (this.white.indexOf(tag) > -1) {
47381                 return;
47382             }
47383             this.white.push(tag);
47384             
47385         }, this);
47386         
47387         
47388         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
47389             if (w.indexOf(tag) > -1) {
47390                 return;
47391             }
47392             this.black.push(tag);
47393             
47394         }, this);
47395         
47396         Roo.each(b, function(tag) {
47397             if (w.indexOf(tag) > -1) {
47398                 return;
47399             }
47400             if (this.black.indexOf(tag) > -1) {
47401                 return;
47402             }
47403             this.black.push(tag);
47404             
47405         }, this);
47406         
47407         
47408         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
47409         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
47410         
47411         this.cwhite = [];
47412         this.cblack = [];
47413         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
47414             if (b.indexOf(tag) > -1) {
47415                 return;
47416             }
47417             this.cwhite.push(tag);
47418             
47419         }, this);
47420         
47421         Roo.each(w, function(tag) {
47422             if (b.indexOf(tag) > -1) {
47423                 return;
47424             }
47425             if (this.cwhite.indexOf(tag) > -1) {
47426                 return;
47427             }
47428             this.cwhite.push(tag);
47429             
47430         }, this);
47431         
47432         
47433         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
47434             if (w.indexOf(tag) > -1) {
47435                 return;
47436             }
47437             this.cblack.push(tag);
47438             
47439         }, this);
47440         
47441         Roo.each(b, function(tag) {
47442             if (w.indexOf(tag) > -1) {
47443                 return;
47444             }
47445             if (this.cblack.indexOf(tag) > -1) {
47446                 return;
47447             }
47448             this.cblack.push(tag);
47449             
47450         }, this);
47451     },
47452     
47453     setStylesheets : function(stylesheets)
47454     {
47455         if(typeof(stylesheets) == 'string'){
47456             Roo.get(this.iframe.contentDocument.head).createChild({
47457                 tag : 'link',
47458                 rel : 'stylesheet',
47459                 type : 'text/css',
47460                 href : stylesheets
47461             });
47462             
47463             return;
47464         }
47465         var _this = this;
47466      
47467         Roo.each(stylesheets, function(s) {
47468             if(!s.length){
47469                 return;
47470             }
47471             
47472             Roo.get(_this.iframe.contentDocument.head).createChild({
47473                 tag : 'link',
47474                 rel : 'stylesheet',
47475                 type : 'text/css',
47476                 href : s
47477             });
47478         });
47479
47480         
47481     },
47482     
47483     removeStylesheets : function()
47484     {
47485         var _this = this;
47486         
47487         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
47488             s.remove();
47489         });
47490     },
47491     
47492     setStyle : function(style)
47493     {
47494         Roo.get(this.iframe.contentDocument.head).createChild({
47495             tag : 'style',
47496             type : 'text/css',
47497             html : style
47498         });
47499
47500         return;
47501     }
47502     
47503     // hide stuff that is not compatible
47504     /**
47505      * @event blur
47506      * @hide
47507      */
47508     /**
47509      * @event change
47510      * @hide
47511      */
47512     /**
47513      * @event focus
47514      * @hide
47515      */
47516     /**
47517      * @event specialkey
47518      * @hide
47519      */
47520     /**
47521      * @cfg {String} fieldClass @hide
47522      */
47523     /**
47524      * @cfg {String} focusClass @hide
47525      */
47526     /**
47527      * @cfg {String} autoCreate @hide
47528      */
47529     /**
47530      * @cfg {String} inputType @hide
47531      */
47532     /**
47533      * @cfg {String} invalidClass @hide
47534      */
47535     /**
47536      * @cfg {String} invalidText @hide
47537      */
47538     /**
47539      * @cfg {String} msgFx @hide
47540      */
47541     /**
47542      * @cfg {String} validateOnBlur @hide
47543      */
47544 });
47545
47546 Roo.HtmlEditorCore.white = [
47547         'AREA', 'BR', 'IMG', 'INPUT', 'HR', 'WBR',
47548         
47549        'ADDRESS', 'BLOCKQUOTE', 'CENTER', 'DD',      'DIR',       'DIV', 
47550        'DL',      'DT',         'H1',     'H2',      'H3',        'H4', 
47551        'H5',      'H6',         'HR',     'ISINDEX', 'LISTING',   'MARQUEE', 
47552        'MENU',    'MULTICOL',   'OL',     'P',       'PLAINTEXT', 'PRE', 
47553        'TABLE',   'UL',         'XMP', 
47554        
47555        'CAPTION', 'COL', 'COLGROUP', 'TBODY', 'TD', 'TFOOT', 'TH', 
47556       'THEAD',   'TR', 
47557      
47558       'DIR', 'MENU', 'OL', 'UL', 'DL',
47559        
47560       'EMBED',  'OBJECT'
47561 ];
47562
47563
47564 Roo.HtmlEditorCore.black = [
47565     //    'embed',  'object', // enable - backend responsiblity to clean thiese
47566         'APPLET', // 
47567         'BASE',   'BASEFONT', 'BGSOUND', 'BLINK',  'BODY', 
47568         'FRAME',  'FRAMESET', 'HEAD',    'HTML',   'ILAYER', 
47569         'IFRAME', 'LAYER',  'LINK',     'META',    'OBJECT',   
47570         'SCRIPT', 'STYLE' ,'TITLE',  'XML',
47571         //'FONT' // CLEAN LATER..
47572         'COLGROUP', 'COL'  // messy tables.
47573         
47574 ];
47575 Roo.HtmlEditorCore.clean = [ // ?? needed???
47576      'SCRIPT', 'STYLE', 'TITLE', 'XML'
47577 ];
47578 Roo.HtmlEditorCore.tag_remove = [
47579     'FONT', 'TBODY'  
47580 ];
47581 // attributes..
47582
47583 Roo.HtmlEditorCore.ablack = [
47584     'on'
47585 ];
47586     
47587 Roo.HtmlEditorCore.aclean = [ 
47588     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
47589 ];
47590
47591 // protocols..
47592 Roo.HtmlEditorCore.pwhite= [
47593         'http',  'https',  'mailto'
47594 ];
47595
47596 // white listed style attributes.
47597 Roo.HtmlEditorCore.cwhite= [
47598       //  'text-align', /// default is to allow most things..
47599       
47600          
47601 //        'font-size'//??
47602 ];
47603
47604 // black listed style attributes.
47605 Roo.HtmlEditorCore.cblack= [
47606       //  'font-size' -- this can be set by the project 
47607 ];
47608
47609
47610
47611
47612     //<script type="text/javascript">
47613
47614 /*
47615  * Ext JS Library 1.1.1
47616  * Copyright(c) 2006-2007, Ext JS, LLC.
47617  * Licence LGPL
47618  * 
47619  */
47620  
47621  
47622 Roo.form.HtmlEditor = function(config){
47623     
47624     
47625     
47626     Roo.form.HtmlEditor.superclass.constructor.call(this, config);
47627     
47628     if (!this.toolbars) {
47629         this.toolbars = [];
47630     }
47631     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
47632     
47633     
47634 };
47635
47636 /**
47637  * @class Roo.form.HtmlEditor
47638  * @extends Roo.form.Field
47639  * Provides a lightweight HTML Editor component.
47640  *
47641  * This has been tested on Fireforx / Chrome.. IE may not be so great..
47642  * 
47643  * <br><br><b>Note: The focus/blur and validation marking functionality inherited from Ext.form.Field is NOT
47644  * supported by this editor.</b><br/><br/>
47645  * An Editor is a sensitive component that can't be used in all spots standard fields can be used. Putting an Editor within
47646  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
47647  */
47648 Roo.extend(Roo.form.HtmlEditor, Roo.form.Field, {
47649     /**
47650      * @cfg {Boolean} clearUp
47651      */
47652     clearUp : true,
47653       /**
47654      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
47655      */
47656     toolbars : false,
47657    
47658      /**
47659      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
47660      *                        Roo.resizable.
47661      */
47662     resizable : false,
47663      /**
47664      * @cfg {Number} height (in pixels)
47665      */   
47666     height: 300,
47667    /**
47668      * @cfg {Number} width (in pixels)
47669      */   
47670     width: 500,
47671     
47672     /**
47673      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets - this is usally a good idea  rootURL + '/roojs1/css/undoreset.css',   .
47674      * 
47675      */
47676     stylesheets: false,
47677     
47678     
47679      /**
47680      * @cfg {Array} blacklist of css styles style attributes (blacklist overrides whitelist)
47681      * 
47682      */
47683     cblack: false,
47684     /**
47685      * @cfg {Array} whitelist of css styles style attributes (blacklist overrides whitelist)
47686      * 
47687      */
47688     cwhite: false,
47689     
47690      /**
47691      * @cfg {Array} blacklist of html tags - in addition to standard blacklist.
47692      * 
47693      */
47694     black: false,
47695     /**
47696      * @cfg {Array} whitelist of html tags - in addition to statndard whitelist
47697      * 
47698      */
47699     white: false,
47700     /**
47701      * @cfg {boolean} allowComments - default false - allow comments in HTML source - by default they are stripped - if you are editing email you may need this.
47702      */
47703     allowComments: false,
47704     /**
47705      * @cfg {string} bodyCls- default '' default classes to add to body of editable area - usually undoreset is a good start..
47706      */
47707     
47708     
47709      bodyCls : '',
47710     
47711     // id of frame..
47712     frameId: false,
47713     
47714     // private properties
47715     validationEvent : false,
47716     deferHeight: true,
47717     initialized : false,
47718     activated : false,
47719     
47720     onFocus : Roo.emptyFn,
47721     iframePad:3,
47722     hideMode:'offsets',
47723     
47724     actionMode : 'container', // defaults to hiding it...
47725     
47726     defaultAutoCreate : { // modified by initCompnoent..
47727         tag: "textarea",
47728         style:"width:500px;height:300px;",
47729         autocomplete: "new-password"
47730     },
47731
47732     // private
47733     initComponent : function(){
47734         this.addEvents({
47735             /**
47736              * @event initialize
47737              * Fires when the editor is fully initialized (including the iframe)
47738              * @param {HtmlEditor} this
47739              */
47740             initialize: true,
47741             /**
47742              * @event activate
47743              * Fires when the editor is first receives the focus. Any insertion must wait
47744              * until after this event.
47745              * @param {HtmlEditor} this
47746              */
47747             activate: true,
47748              /**
47749              * @event beforesync
47750              * Fires before the textarea is updated with content from the editor iframe. Return false
47751              * to cancel the sync.
47752              * @param {HtmlEditor} this
47753              * @param {String} html
47754              */
47755             beforesync: true,
47756              /**
47757              * @event beforepush
47758              * Fires before the iframe editor is updated with content from the textarea. Return false
47759              * to cancel the push.
47760              * @param {HtmlEditor} this
47761              * @param {String} html
47762              */
47763             beforepush: true,
47764              /**
47765              * @event sync
47766              * Fires when the textarea is updated with content from the editor iframe.
47767              * @param {HtmlEditor} this
47768              * @param {String} html
47769              */
47770             sync: true,
47771              /**
47772              * @event push
47773              * Fires when the iframe editor is updated with content from the textarea.
47774              * @param {HtmlEditor} this
47775              * @param {String} html
47776              */
47777             push: true,
47778              /**
47779              * @event editmodechange
47780              * Fires when the editor switches edit modes
47781              * @param {HtmlEditor} this
47782              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
47783              */
47784             editmodechange: true,
47785             /**
47786              * @event editorevent
47787              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
47788              * @param {HtmlEditor} this
47789              */
47790             editorevent: true,
47791             /**
47792              * @event firstfocus
47793              * Fires when on first focus - needed by toolbars..
47794              * @param {HtmlEditor} this
47795              */
47796             firstfocus: true,
47797             /**
47798              * @event autosave
47799              * Auto save the htmlEditor value as a file into Events
47800              * @param {HtmlEditor} this
47801              */
47802             autosave: true,
47803             /**
47804              * @event savedpreview
47805              * preview the saved version of htmlEditor
47806              * @param {HtmlEditor} this
47807              */
47808             savedpreview: true,
47809             
47810             /**
47811             * @event stylesheetsclick
47812             * Fires when press the Sytlesheets button
47813             * @param {Roo.HtmlEditorCore} this
47814             */
47815             stylesheetsclick: true,
47816             /**
47817             * @event paste
47818             * Fires when press user pastes into the editor
47819             * @param {Roo.HtmlEditorCore} this
47820             */
47821             paste: true 
47822         });
47823         this.defaultAutoCreate =  {
47824             tag: "textarea",
47825             style:'width: ' + this.width + 'px;height: ' + this.height + 'px;',
47826             autocomplete: "new-password"
47827         };
47828     },
47829
47830     /**
47831      * Protected method that will not generally be called directly. It
47832      * is called when the editor creates its toolbar. Override this method if you need to
47833      * add custom toolbar buttons.
47834      * @param {HtmlEditor} editor
47835      */
47836     createToolbar : function(editor){
47837         Roo.log("create toolbars");
47838         if (!editor.toolbars || !editor.toolbars.length) {
47839             editor.toolbars = [ new Roo.form.HtmlEditor.ToolbarStandard() ]; // can be empty?
47840         }
47841         
47842         for (var i =0 ; i < editor.toolbars.length;i++) {
47843             editor.toolbars[i] = Roo.factory(
47844                     typeof(editor.toolbars[i]) == 'string' ?
47845                         { xtype: editor.toolbars[i]} : editor.toolbars[i],
47846                 Roo.form.HtmlEditor);
47847             editor.toolbars[i].init(editor);
47848         }
47849          
47850         
47851     },
47852
47853      
47854     // private
47855     onRender : function(ct, position)
47856     {
47857         var _t = this;
47858         Roo.form.HtmlEditor.superclass.onRender.call(this, ct, position);
47859         
47860         this.wrap = this.el.wrap({
47861             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
47862         });
47863         
47864         this.editorcore.onRender(ct, position);
47865          
47866         if (this.resizable) {
47867             this.resizeEl = new Roo.Resizable(this.wrap, {
47868                 pinned : true,
47869                 wrap: true,
47870                 dynamic : true,
47871                 minHeight : this.height,
47872                 height: this.height,
47873                 handles : this.resizable,
47874                 width: this.width,
47875                 listeners : {
47876                     resize : function(r, w, h) {
47877                         _t.onResize(w,h); // -something
47878                     }
47879                 }
47880             });
47881             
47882         }
47883         this.createToolbar(this);
47884        
47885         
47886         if(!this.width){
47887             this.setSize(this.wrap.getSize());
47888         }
47889         if (this.resizeEl) {
47890             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
47891             // should trigger onReize..
47892         }
47893         
47894         this.keyNav = new Roo.KeyNav(this.el, {
47895             
47896             "tab" : function(e){
47897                 e.preventDefault();
47898                 
47899                 var value = this.getValue();
47900                 
47901                 var start = this.el.dom.selectionStart;
47902                 var end = this.el.dom.selectionEnd;
47903                 
47904                 if(!e.shiftKey){
47905                     
47906                     this.setValue(value.substring(0, start) + "\t" + value.substring(end));
47907                     this.el.dom.setSelectionRange(end + 1, end + 1);
47908                     return;
47909                 }
47910                 
47911                 var f = value.substring(0, start).split("\t");
47912                 
47913                 if(f.pop().length != 0){
47914                     return;
47915                 }
47916                 
47917                 this.setValue(f.join("\t") + value.substring(end));
47918                 this.el.dom.setSelectionRange(start - 1, start - 1);
47919                 
47920             },
47921             
47922             "home" : function(e){
47923                 e.preventDefault();
47924                 
47925                 var curr = this.el.dom.selectionStart;
47926                 var lines = this.getValue().split("\n");
47927                 
47928                 if(!lines.length){
47929                     return;
47930                 }
47931                 
47932                 if(e.ctrlKey){
47933                     this.el.dom.setSelectionRange(0, 0);
47934                     return;
47935                 }
47936                 
47937                 var pos = 0;
47938                 
47939                 for (var i = 0; i < lines.length;i++) {
47940                     pos += lines[i].length;
47941                     
47942                     if(i != 0){
47943                         pos += 1;
47944                     }
47945                     
47946                     if(pos < curr){
47947                         continue;
47948                     }
47949                     
47950                     pos -= lines[i].length;
47951                     
47952                     break;
47953                 }
47954                 
47955                 if(!e.shiftKey){
47956                     this.el.dom.setSelectionRange(pos, pos);
47957                     return;
47958                 }
47959                 
47960                 this.el.dom.selectionStart = pos;
47961                 this.el.dom.selectionEnd = curr;
47962             },
47963             
47964             "end" : function(e){
47965                 e.preventDefault();
47966                 
47967                 var curr = this.el.dom.selectionStart;
47968                 var lines = this.getValue().split("\n");
47969                 
47970                 if(!lines.length){
47971                     return;
47972                 }
47973                 
47974                 if(e.ctrlKey){
47975                     this.el.dom.setSelectionRange(this.getValue().length, this.getValue().length);
47976                     return;
47977                 }
47978                 
47979                 var pos = 0;
47980                 
47981                 for (var i = 0; i < lines.length;i++) {
47982                     
47983                     pos += lines[i].length;
47984                     
47985                     if(i != 0){
47986                         pos += 1;
47987                     }
47988                     
47989                     if(pos < curr){
47990                         continue;
47991                     }
47992                     
47993                     break;
47994                 }
47995                 
47996                 if(!e.shiftKey){
47997                     this.el.dom.setSelectionRange(pos, pos);
47998                     return;
47999                 }
48000                 
48001                 this.el.dom.selectionStart = curr;
48002                 this.el.dom.selectionEnd = pos;
48003             },
48004
48005             scope : this,
48006
48007             doRelay : function(foo, bar, hname){
48008                 return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
48009             },
48010
48011             forceKeyDown: true
48012         });
48013         
48014 //        if(this.autosave && this.w){
48015 //            this.autoSaveFn = setInterval(this.autosave, 1000);
48016 //        }
48017     },
48018
48019     // private
48020     onResize : function(w, h)
48021     {
48022         Roo.form.HtmlEditor.superclass.onResize.apply(this, arguments);
48023         var ew = false;
48024         var eh = false;
48025         
48026         if(this.el ){
48027             if(typeof w == 'number'){
48028                 var aw = w - this.wrap.getFrameWidth('lr');
48029                 this.el.setWidth(this.adjustWidth('textarea', aw));
48030                 ew = aw;
48031             }
48032             if(typeof h == 'number'){
48033                 var tbh = 0;
48034                 for (var i =0; i < this.toolbars.length;i++) {
48035                     // fixme - ask toolbars for heights?
48036                     tbh += this.toolbars[i].tb.el.getHeight();
48037                     if (this.toolbars[i].footer) {
48038                         tbh += this.toolbars[i].footer.el.getHeight();
48039                     }
48040                 }
48041                 
48042                 
48043                 
48044                 
48045                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
48046                 ah -= 5; // knock a few pixes off for look..
48047 //                Roo.log(ah);
48048                 this.el.setHeight(this.adjustWidth('textarea', ah));
48049                 var eh = ah;
48050             }
48051         }
48052         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
48053         this.editorcore.onResize(ew,eh);
48054         
48055     },
48056
48057     /**
48058      * Toggles the editor between standard and source edit mode.
48059      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
48060      */
48061     toggleSourceEdit : function(sourceEditMode)
48062     {
48063         this.editorcore.toggleSourceEdit(sourceEditMode);
48064         
48065         if(this.editorcore.sourceEditMode){
48066             Roo.log('editor - showing textarea');
48067             
48068 //            Roo.log('in');
48069 //            Roo.log(this.syncValue());
48070             this.editorcore.syncValue();
48071             this.el.removeClass('x-hidden');
48072             this.el.dom.removeAttribute('tabIndex');
48073             this.el.focus();
48074             this.el.dom.scrollTop = 0;
48075             
48076             
48077             for (var i = 0; i < this.toolbars.length; i++) {
48078                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48079                     this.toolbars[i].tb.hide();
48080                     this.toolbars[i].footer.hide();
48081                 }
48082             }
48083             
48084         }else{
48085             Roo.log('editor - hiding textarea');
48086 //            Roo.log('out')
48087 //            Roo.log(this.pushValue()); 
48088             this.editorcore.pushValue();
48089             
48090             this.el.addClass('x-hidden');
48091             this.el.dom.setAttribute('tabIndex', -1);
48092             
48093             for (var i = 0; i < this.toolbars.length; i++) {
48094                 if(this.toolbars[i] instanceof Roo.form.HtmlEditor.ToolbarContext){
48095                     this.toolbars[i].tb.show();
48096                     this.toolbars[i].footer.show();
48097                 }
48098             }
48099             
48100             //this.deferFocus();
48101         }
48102         
48103         this.setSize(this.wrap.getSize());
48104         this.onResize(this.wrap.getSize().width, this.wrap.getSize().height);
48105         
48106         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
48107     },
48108  
48109     // private (for BoxComponent)
48110     adjustSize : Roo.BoxComponent.prototype.adjustSize,
48111
48112     // private (for BoxComponent)
48113     getResizeEl : function(){
48114         return this.wrap;
48115     },
48116
48117     // private (for BoxComponent)
48118     getPositionEl : function(){
48119         return this.wrap;
48120     },
48121
48122     // private
48123     initEvents : function(){
48124         this.originalValue = this.getValue();
48125     },
48126
48127     /**
48128      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48129      * @method
48130      */
48131     markInvalid : Roo.emptyFn,
48132     /**
48133      * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
48134      * @method
48135      */
48136     clearInvalid : Roo.emptyFn,
48137
48138     setValue : function(v){
48139         Roo.form.HtmlEditor.superclass.setValue.call(this, v);
48140         this.editorcore.pushValue();
48141     },
48142
48143      
48144     // private
48145     deferFocus : function(){
48146         this.focus.defer(10, this);
48147     },
48148
48149     // doc'ed in Field
48150     focus : function(){
48151         this.editorcore.focus();
48152         
48153     },
48154       
48155
48156     // private
48157     onDestroy : function(){
48158         
48159         
48160         
48161         if(this.rendered){
48162             
48163             for (var i =0; i < this.toolbars.length;i++) {
48164                 // fixme - ask toolbars for heights?
48165                 this.toolbars[i].onDestroy();
48166             }
48167             
48168             this.wrap.dom.innerHTML = '';
48169             this.wrap.remove();
48170         }
48171     },
48172
48173     // private
48174     onFirstFocus : function(){
48175         //Roo.log("onFirstFocus");
48176         this.editorcore.onFirstFocus();
48177          for (var i =0; i < this.toolbars.length;i++) {
48178             this.toolbars[i].onFirstFocus();
48179         }
48180         
48181     },
48182     
48183     // private
48184     syncValue : function()
48185     {
48186         this.editorcore.syncValue();
48187     },
48188     
48189     pushValue : function()
48190     {
48191         this.editorcore.pushValue();
48192     },
48193     
48194     setStylesheets : function(stylesheets)
48195     {
48196         this.editorcore.setStylesheets(stylesheets);
48197     },
48198     
48199     removeStylesheets : function()
48200     {
48201         this.editorcore.removeStylesheets();
48202     }
48203      
48204     
48205     // hide stuff that is not compatible
48206     /**
48207      * @event blur
48208      * @hide
48209      */
48210     /**
48211      * @event change
48212      * @hide
48213      */
48214     /**
48215      * @event focus
48216      * @hide
48217      */
48218     /**
48219      * @event specialkey
48220      * @hide
48221      */
48222     /**
48223      * @cfg {String} fieldClass @hide
48224      */
48225     /**
48226      * @cfg {String} focusClass @hide
48227      */
48228     /**
48229      * @cfg {String} autoCreate @hide
48230      */
48231     /**
48232      * @cfg {String} inputType @hide
48233      */
48234     /**
48235      * @cfg {String} invalidClass @hide
48236      */
48237     /**
48238      * @cfg {String} invalidText @hide
48239      */
48240     /**
48241      * @cfg {String} msgFx @hide
48242      */
48243     /**
48244      * @cfg {String} validateOnBlur @hide
48245      */
48246 });
48247  
48248     // <script type="text/javascript">
48249 /*
48250  * Based on
48251  * Ext JS Library 1.1.1
48252  * Copyright(c) 2006-2007, Ext JS, LLC.
48253  *  
48254  
48255  */
48256
48257 /**
48258  * @class Roo.form.HtmlEditorToolbar1
48259  * Basic Toolbar
48260  * 
48261  * Usage:
48262  *
48263  new Roo.form.HtmlEditor({
48264     ....
48265     toolbars : [
48266         new Roo.form.HtmlEditorToolbar1({
48267             disable : { fonts: 1 , format: 1, ..., ... , ...],
48268             btns : [ .... ]
48269         })
48270     }
48271      
48272  * 
48273  * @cfg {Object} disable List of elements to disable..
48274  * @cfg {Array} btns List of additional buttons.
48275  * 
48276  * 
48277  * NEEDS Extra CSS? 
48278  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
48279  */
48280  
48281 Roo.form.HtmlEditor.ToolbarStandard = function(config)
48282 {
48283     
48284     Roo.apply(this, config);
48285     
48286     // default disabled, based on 'good practice'..
48287     this.disable = this.disable || {};
48288     Roo.applyIf(this.disable, {
48289         fontSize : true,
48290         colors : true,
48291         specialElements : true
48292     });
48293     
48294     
48295     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
48296     // dont call parent... till later.
48297 }
48298
48299 Roo.apply(Roo.form.HtmlEditor.ToolbarStandard.prototype,  {
48300     
48301     tb: false,
48302     
48303     rendered: false,
48304     
48305     editor : false,
48306     editorcore : false,
48307     /**
48308      * @cfg {Object} disable  List of toolbar elements to disable
48309          
48310      */
48311     disable : false,
48312     
48313     
48314      /**
48315      * @cfg {String} createLinkText The default text for the create link prompt
48316      */
48317     createLinkText : 'Please enter the URL for the link:',
48318     /**
48319      * @cfg {String} defaultLinkValue The default value for the create link prompt (defaults to http:/ /)
48320      */
48321     defaultLinkValue : 'http:/'+'/',
48322    
48323     
48324       /**
48325      * @cfg {Array} fontFamilies An array of available font families
48326      */
48327     fontFamilies : [
48328         'Arial',
48329         'Courier New',
48330         'Tahoma',
48331         'Times New Roman',
48332         'Verdana'
48333     ],
48334     
48335     specialChars : [
48336            "&#169;",
48337           "&#174;",     
48338           "&#8482;",    
48339           "&#163;" ,    
48340          // "&#8212;",    
48341           "&#8230;",    
48342           "&#247;" ,    
48343         //  "&#225;" ,     ?? a acute?
48344            "&#8364;"    , //Euro
48345        //   "&#8220;"    ,
48346         //  "&#8221;"    ,
48347         //  "&#8226;"    ,
48348           "&#176;"  //   , // degrees
48349
48350          // "&#233;"     , // e ecute
48351          // "&#250;"     , // u ecute?
48352     ],
48353     
48354     specialElements : [
48355         {
48356             text: "Insert Table",
48357             xtype: 'MenuItem',
48358             xns : Roo.Menu,
48359             ihtml :  '<table><tr><td>Cell</td></tr></table>' 
48360                 
48361         },
48362         {    
48363             text: "Insert Image",
48364             xtype: 'MenuItem',
48365             xns : Roo.Menu,
48366             ihtml : '<img src="about:blank"/>'
48367             
48368         }
48369         
48370          
48371     ],
48372     
48373     
48374     inputElements : [ 
48375             "form", "input:text", "input:hidden", "input:checkbox", "input:radio", "input:password", 
48376             "input:submit", "input:button", "select", "textarea", "label" ],
48377     formats : [
48378         ["p"] ,  
48379         ["h1"],["h2"],["h3"],["h4"],["h5"],["h6"], 
48380         ["pre"],[ "code"], 
48381         ["abbr"],[ "acronym"],[ "address"],[ "cite"],[ "samp"],[ "var"],
48382         ['div'],['span'],
48383         ['sup'],['sub']
48384     ],
48385     
48386     cleanStyles : [
48387         "font-size"
48388     ],
48389      /**
48390      * @cfg {String} defaultFont default font to use.
48391      */
48392     defaultFont: 'tahoma',
48393    
48394     fontSelect : false,
48395     
48396     
48397     formatCombo : false,
48398     
48399     init : function(editor)
48400     {
48401         this.editor = editor;
48402         this.editorcore = editor.editorcore ? editor.editorcore : editor;
48403         var editorcore = this.editorcore;
48404         
48405         var _t = this;
48406         
48407         var fid = editorcore.frameId;
48408         var etb = this;
48409         function btn(id, toggle, handler){
48410             var xid = fid + '-'+ id ;
48411             return {
48412                 id : xid,
48413                 cmd : id,
48414                 cls : 'x-btn-icon x-edit-'+id,
48415                 enableToggle:toggle !== false,
48416                 scope: _t, // was editor...
48417                 handler:handler||_t.relayBtnCmd,
48418                 clickEvent:'mousedown',
48419                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
48420                 tabIndex:-1
48421             };
48422         }
48423         
48424         
48425         
48426         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
48427         this.tb = tb;
48428          // stop form submits
48429         tb.el.on('click', function(e){
48430             e.preventDefault(); // what does this do?
48431         });
48432
48433         if(!this.disable.font) { // && !Roo.isSafari){
48434             /* why no safari for fonts 
48435             editor.fontSelect = tb.el.createChild({
48436                 tag:'select',
48437                 tabIndex: -1,
48438                 cls:'x-font-select',
48439                 html: this.createFontOptions()
48440             });
48441             
48442             editor.fontSelect.on('change', function(){
48443                 var font = editor.fontSelect.dom.value;
48444                 editor.relayCmd('fontname', font);
48445                 editor.deferFocus();
48446             }, editor);
48447             
48448             tb.add(
48449                 editor.fontSelect.dom,
48450                 '-'
48451             );
48452             */
48453             
48454         };
48455         if(!this.disable.formats){
48456             this.formatCombo = new Roo.form.ComboBox({
48457                 store: new Roo.data.SimpleStore({
48458                     id : 'tag',
48459                     fields: ['tag'],
48460                     data : this.formats // from states.js
48461                 }),
48462                 blockFocus : true,
48463                 name : '',
48464                 //autoCreate : {tag: "div",  size: "20"},
48465                 displayField:'tag',
48466                 typeAhead: false,
48467                 mode: 'local',
48468                 editable : false,
48469                 triggerAction: 'all',
48470                 emptyText:'Add tag',
48471                 selectOnFocus:true,
48472                 width:135,
48473                 listeners : {
48474                     'select': function(c, r, i) {
48475                         editorcore.insertTag(r.get('tag'));
48476                         editor.focus();
48477                     }
48478                 }
48479
48480             });
48481             tb.addField(this.formatCombo);
48482             
48483         }
48484         
48485         if(!this.disable.format){
48486             tb.add(
48487                 btn('bold'),
48488                 btn('italic'),
48489                 btn('underline'),
48490                 btn('strikethrough')
48491             );
48492         };
48493         if(!this.disable.fontSize){
48494             tb.add(
48495                 '-',
48496                 
48497                 
48498                 btn('increasefontsize', false, editorcore.adjustFont),
48499                 btn('decreasefontsize', false, editorcore.adjustFont)
48500             );
48501         };
48502         
48503         
48504         if(!this.disable.colors){
48505             tb.add(
48506                 '-', {
48507                     id:editorcore.frameId +'-forecolor',
48508                     cls:'x-btn-icon x-edit-forecolor',
48509                     clickEvent:'mousedown',
48510                     tooltip: this.buttonTips['forecolor'] || undefined,
48511                     tabIndex:-1,
48512                     menu : new Roo.menu.ColorMenu({
48513                         allowReselect: true,
48514                         focus: Roo.emptyFn,
48515                         value:'000000',
48516                         plain:true,
48517                         selectHandler: function(cp, color){
48518                             editorcore.execCmd('forecolor', Roo.isSafari || Roo.isIE ? '#'+color : color);
48519                             editor.deferFocus();
48520                         },
48521                         scope: editorcore,
48522                         clickEvent:'mousedown'
48523                     })
48524                 }, {
48525                     id:editorcore.frameId +'backcolor',
48526                     cls:'x-btn-icon x-edit-backcolor',
48527                     clickEvent:'mousedown',
48528                     tooltip: this.buttonTips['backcolor'] || undefined,
48529                     tabIndex:-1,
48530                     menu : new Roo.menu.ColorMenu({
48531                         focus: Roo.emptyFn,
48532                         value:'FFFFFF',
48533                         plain:true,
48534                         allowReselect: true,
48535                         selectHandler: function(cp, color){
48536                             if(Roo.isGecko){
48537                                 editorcore.execCmd('useCSS', false);
48538                                 editorcore.execCmd('hilitecolor', color);
48539                                 editorcore.execCmd('useCSS', true);
48540                                 editor.deferFocus();
48541                             }else{
48542                                 editorcore.execCmd(Roo.isOpera ? 'hilitecolor' : 'backcolor', 
48543                                     Roo.isSafari || Roo.isIE ? '#'+color : color);
48544                                 editor.deferFocus();
48545                             }
48546                         },
48547                         scope:editorcore,
48548                         clickEvent:'mousedown'
48549                     })
48550                 }
48551             );
48552         };
48553         // now add all the items...
48554         
48555
48556         if(!this.disable.alignments){
48557             tb.add(
48558                 '-',
48559                 btn('justifyleft'),
48560                 btn('justifycenter'),
48561                 btn('justifyright')
48562             );
48563         };
48564
48565         //if(!Roo.isSafari){
48566             if(!this.disable.links){
48567                 tb.add(
48568                     '-',
48569                     btn('createlink', false, this.createLink)    /// MOVE TO HERE?!!?!?!?!
48570                 );
48571             };
48572
48573             if(!this.disable.lists){
48574                 tb.add(
48575                     '-',
48576                     btn('insertorderedlist'),
48577                     btn('insertunorderedlist')
48578                 );
48579             }
48580             if(!this.disable.sourceEdit){
48581                 tb.add(
48582                     '-',
48583                     btn('sourceedit', true, function(btn){
48584                         this.toggleSourceEdit(btn.pressed);
48585                     })
48586                 );
48587             }
48588         //}
48589         
48590         var smenu = { };
48591         // special menu.. - needs to be tidied up..
48592         if (!this.disable.special) {
48593             smenu = {
48594                 text: "&#169;",
48595                 cls: 'x-edit-none',
48596                 
48597                 menu : {
48598                     items : []
48599                 }
48600             };
48601             for (var i =0; i < this.specialChars.length; i++) {
48602                 smenu.menu.items.push({
48603                     
48604                     html: this.specialChars[i],
48605                     handler: function(a,b) {
48606                         editorcore.insertAtCursor(String.fromCharCode(a.html.replace('&#','').replace(';', '')));
48607                         //editor.insertAtCursor(a.html);
48608                         
48609                     },
48610                     tabIndex:-1
48611                 });
48612             }
48613             
48614             
48615             tb.add(smenu);
48616             
48617             
48618         }
48619         
48620         var cmenu = { };
48621         if (!this.disable.cleanStyles) {
48622             cmenu = {
48623                 cls: 'x-btn-icon x-btn-clear',
48624                 
48625                 menu : {
48626                     items : []
48627                 }
48628             };
48629             for (var i =0; i < this.cleanStyles.length; i++) {
48630                 cmenu.menu.items.push({
48631                     actiontype : this.cleanStyles[i],
48632                     html: 'Remove ' + this.cleanStyles[i],
48633                     handler: function(a,b) {
48634 //                        Roo.log(a);
48635 //                        Roo.log(b);
48636                         var c = Roo.get(editorcore.doc.body);
48637                         c.select('[style]').each(function(s) {
48638                             s.dom.style.removeProperty(a.actiontype);
48639                         });
48640                         editorcore.syncValue();
48641                     },
48642                     tabIndex:-1
48643                 });
48644             }
48645             cmenu.menu.items.push({
48646                 actiontype : 'tablewidths',
48647                 html: 'Remove Table Widths',
48648                 handler: function(a,b) {
48649                     editorcore.cleanTableWidths();
48650                     editorcore.syncValue();
48651                 },
48652                 tabIndex:-1
48653             });
48654             cmenu.menu.items.push({
48655                 actiontype : 'word',
48656                 html: 'Remove MS Word Formating',
48657                 handler: function(a,b) {
48658                     editorcore.cleanWord();
48659                     editorcore.syncValue();
48660                 },
48661                 tabIndex:-1
48662             });
48663             
48664             cmenu.menu.items.push({
48665                 actiontype : 'all',
48666                 html: 'Remove All Styles',
48667                 handler: function(a,b) {
48668                     
48669                     var c = Roo.get(editorcore.doc.body);
48670                     c.select('[style]').each(function(s) {
48671                         s.dom.removeAttribute('style');
48672                     });
48673                     editorcore.syncValue();
48674                 },
48675                 tabIndex:-1
48676             });
48677             
48678             cmenu.menu.items.push({
48679                 actiontype : 'all',
48680                 html: 'Remove All CSS Classes',
48681                 handler: function(a,b) {
48682                     
48683                     var c = Roo.get(editorcore.doc.body);
48684                     c.select('[class]').each(function(s) {
48685                         s.dom.removeAttribute('class');
48686                     });
48687                     editorcore.cleanWord();
48688                     editorcore.syncValue();
48689                 },
48690                 tabIndex:-1
48691             });
48692             
48693              cmenu.menu.items.push({
48694                 actiontype : 'tidy',
48695                 html: 'Tidy HTML Source',
48696                 handler: function(a,b) {
48697                     new Roo.htmleditor.Tidy(editorcore.doc.body);
48698                     editorcore.syncValue();
48699                 },
48700                 tabIndex:-1
48701             });
48702             
48703             
48704             tb.add(cmenu);
48705         }
48706          
48707         if (!this.disable.specialElements) {
48708             var semenu = {
48709                 text: "Other;",
48710                 cls: 'x-edit-none',
48711                 menu : {
48712                     items : []
48713                 }
48714             };
48715             for (var i =0; i < this.specialElements.length; i++) {
48716                 semenu.menu.items.push(
48717                     Roo.apply({ 
48718                         handler: function(a,b) {
48719                             editor.insertAtCursor(this.ihtml);
48720                         }
48721                     }, this.specialElements[i])
48722                 );
48723                     
48724             }
48725             
48726             tb.add(semenu);
48727             
48728             
48729         }
48730          
48731         
48732         if (this.btns) {
48733             for(var i =0; i< this.btns.length;i++) {
48734                 var b = Roo.factory(this.btns[i],Roo.form);
48735                 b.cls =  'x-edit-none';
48736                 
48737                 if(typeof(this.btns[i].cls) != 'undefined' && this.btns[i].cls.indexOf('x-init-enable') !== -1){
48738                     b.cls += ' x-init-enable';
48739                 }
48740                 
48741                 b.scope = editorcore;
48742                 tb.add(b);
48743             }
48744         
48745         }
48746         
48747         
48748         
48749         // disable everything...
48750         
48751         this.tb.items.each(function(item){
48752             
48753            if(
48754                 item.id != editorcore.frameId+ '-sourceedit' && 
48755                 (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)
48756             ){
48757                 
48758                 item.disable();
48759             }
48760         });
48761         this.rendered = true;
48762         
48763         // the all the btns;
48764         editor.on('editorevent', this.updateToolbar, this);
48765         // other toolbars need to implement this..
48766         //editor.on('editmodechange', this.updateToolbar, this);
48767     },
48768     
48769     
48770     relayBtnCmd : function(btn) {
48771         this.editorcore.relayCmd(btn.cmd);
48772     },
48773     // private used internally
48774     createLink : function(){
48775         Roo.log("create link?");
48776         var url = prompt(this.createLinkText, this.defaultLinkValue);
48777         if(url && url != 'http:/'+'/'){
48778             this.editorcore.relayCmd('createlink', url);
48779         }
48780     },
48781
48782     
48783     /**
48784      * Protected method that will not generally be called directly. It triggers
48785      * a toolbar update by reading the markup state of the current selection in the editor.
48786      */
48787     updateToolbar: function(){
48788
48789         if(!this.editorcore.activated){
48790             this.editor.onFirstFocus();
48791             return;
48792         }
48793
48794         var btns = this.tb.items.map, 
48795             doc = this.editorcore.doc,
48796             frameId = this.editorcore.frameId;
48797
48798         if(!this.disable.font && !Roo.isSafari){
48799             /*
48800             var name = (doc.queryCommandValue('FontName')||this.editor.defaultFont).toLowerCase();
48801             if(name != this.fontSelect.dom.value){
48802                 this.fontSelect.dom.value = name;
48803             }
48804             */
48805         }
48806         if(!this.disable.format){
48807             btns[frameId + '-bold'].toggle(doc.queryCommandState('bold'));
48808             btns[frameId + '-italic'].toggle(doc.queryCommandState('italic'));
48809             btns[frameId + '-underline'].toggle(doc.queryCommandState('underline'));
48810             btns[frameId + '-strikethrough'].toggle(doc.queryCommandState('strikethrough'));
48811         }
48812         if(!this.disable.alignments){
48813             btns[frameId + '-justifyleft'].toggle(doc.queryCommandState('justifyleft'));
48814             btns[frameId + '-justifycenter'].toggle(doc.queryCommandState('justifycenter'));
48815             btns[frameId + '-justifyright'].toggle(doc.queryCommandState('justifyright'));
48816         }
48817         if(!Roo.isSafari && !this.disable.lists){
48818             btns[frameId + '-insertorderedlist'].toggle(doc.queryCommandState('insertorderedlist'));
48819             btns[frameId + '-insertunorderedlist'].toggle(doc.queryCommandState('insertunorderedlist'));
48820         }
48821         
48822         var ans = this.editorcore.getAllAncestors();
48823         if (this.formatCombo) {
48824             
48825             
48826             var store = this.formatCombo.store;
48827             this.formatCombo.setValue("");
48828             for (var i =0; i < ans.length;i++) {
48829                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
48830                     // select it..
48831                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
48832                     break;
48833                 }
48834             }
48835         }
48836         
48837         
48838         
48839         // hides menus... - so this cant be on a menu...
48840         Roo.menu.MenuMgr.hideAll();
48841
48842         //this.editorsyncValue();
48843     },
48844    
48845     
48846     createFontOptions : function(){
48847         var buf = [], fs = this.fontFamilies, ff, lc;
48848         
48849         
48850         
48851         for(var i = 0, len = fs.length; i< len; i++){
48852             ff = fs[i];
48853             lc = ff.toLowerCase();
48854             buf.push(
48855                 '<option value="',lc,'" style="font-family:',ff,';"',
48856                     (this.defaultFont == lc ? ' selected="true">' : '>'),
48857                     ff,
48858                 '</option>'
48859             );
48860         }
48861         return buf.join('');
48862     },
48863     
48864     toggleSourceEdit : function(sourceEditMode){
48865         
48866         Roo.log("toolbar toogle");
48867         if(sourceEditMode === undefined){
48868             sourceEditMode = !this.sourceEditMode;
48869         }
48870         this.sourceEditMode = sourceEditMode === true;
48871         var btn = this.tb.items.get(this.editorcore.frameId +'-sourceedit');
48872         // just toggle the button?
48873         if(btn.pressed !== this.sourceEditMode){
48874             btn.toggle(this.sourceEditMode);
48875             return;
48876         }
48877         
48878         if(sourceEditMode){
48879             Roo.log("disabling buttons");
48880             this.tb.items.each(function(item){
48881                 if(item.cmd != 'sourceedit' && (typeof(item.cls) != 'undefined' && item.cls.indexOf('x-init-enable') === -1)){
48882                     item.disable();
48883                 }
48884             });
48885           
48886         }else{
48887             Roo.log("enabling buttons");
48888             if(this.editorcore.initialized){
48889                 this.tb.items.each(function(item){
48890                     item.enable();
48891                 });
48892             }
48893             
48894         }
48895         Roo.log("calling toggole on editor");
48896         // tell the editor that it's been pressed..
48897         this.editor.toggleSourceEdit(sourceEditMode);
48898        
48899     },
48900      /**
48901      * Object collection of toolbar tooltips for the buttons in the editor. The key
48902      * is the command id associated with that button and the value is a valid QuickTips object.
48903      * For example:
48904 <pre><code>
48905 {
48906     bold : {
48907         title: 'Bold (Ctrl+B)',
48908         text: 'Make the selected text bold.',
48909         cls: 'x-html-editor-tip'
48910     },
48911     italic : {
48912         title: 'Italic (Ctrl+I)',
48913         text: 'Make the selected text italic.',
48914         cls: 'x-html-editor-tip'
48915     },
48916     ...
48917 </code></pre>
48918     * @type Object
48919      */
48920     buttonTips : {
48921         bold : {
48922             title: 'Bold (Ctrl+B)',
48923             text: 'Make the selected text bold.',
48924             cls: 'x-html-editor-tip'
48925         },
48926         italic : {
48927             title: 'Italic (Ctrl+I)',
48928             text: 'Make the selected text italic.',
48929             cls: 'x-html-editor-tip'
48930         },
48931         underline : {
48932             title: 'Underline (Ctrl+U)',
48933             text: 'Underline the selected text.',
48934             cls: 'x-html-editor-tip'
48935         },
48936         strikethrough : {
48937             title: 'Strikethrough',
48938             text: 'Strikethrough the selected text.',
48939             cls: 'x-html-editor-tip'
48940         },
48941         increasefontsize : {
48942             title: 'Grow Text',
48943             text: 'Increase the font size.',
48944             cls: 'x-html-editor-tip'
48945         },
48946         decreasefontsize : {
48947             title: 'Shrink Text',
48948             text: 'Decrease the font size.',
48949             cls: 'x-html-editor-tip'
48950         },
48951         backcolor : {
48952             title: 'Text Highlight Color',
48953             text: 'Change the background color of the selected text.',
48954             cls: 'x-html-editor-tip'
48955         },
48956         forecolor : {
48957             title: 'Font Color',
48958             text: 'Change the color of the selected text.',
48959             cls: 'x-html-editor-tip'
48960         },
48961         justifyleft : {
48962             title: 'Align Text Left',
48963             text: 'Align text to the left.',
48964             cls: 'x-html-editor-tip'
48965         },
48966         justifycenter : {
48967             title: 'Center Text',
48968             text: 'Center text in the editor.',
48969             cls: 'x-html-editor-tip'
48970         },
48971         justifyright : {
48972             title: 'Align Text Right',
48973             text: 'Align text to the right.',
48974             cls: 'x-html-editor-tip'
48975         },
48976         insertunorderedlist : {
48977             title: 'Bullet List',
48978             text: 'Start a bulleted list.',
48979             cls: 'x-html-editor-tip'
48980         },
48981         insertorderedlist : {
48982             title: 'Numbered List',
48983             text: 'Start a numbered list.',
48984             cls: 'x-html-editor-tip'
48985         },
48986         createlink : {
48987             title: 'Hyperlink',
48988             text: 'Make the selected text a hyperlink.',
48989             cls: 'x-html-editor-tip'
48990         },
48991         sourceedit : {
48992             title: 'Source Edit',
48993             text: 'Switch to source editing mode.',
48994             cls: 'x-html-editor-tip'
48995         }
48996     },
48997     // private
48998     onDestroy : function(){
48999         if(this.rendered){
49000             
49001             this.tb.items.each(function(item){
49002                 if(item.menu){
49003                     item.menu.removeAll();
49004                     if(item.menu.el){
49005                         item.menu.el.destroy();
49006                     }
49007                 }
49008                 item.destroy();
49009             });
49010              
49011         }
49012     },
49013     onFirstFocus: function() {
49014         this.tb.items.each(function(item){
49015            item.enable();
49016         });
49017     }
49018 });
49019
49020
49021
49022
49023 // <script type="text/javascript">
49024 /*
49025  * Based on
49026  * Ext JS Library 1.1.1
49027  * Copyright(c) 2006-2007, Ext JS, LLC.
49028  *  
49029  
49030  */
49031
49032  
49033 /**
49034  * @class Roo.form.HtmlEditor.ToolbarContext
49035  * Context Toolbar
49036  * 
49037  * Usage:
49038  *
49039  new Roo.form.HtmlEditor({
49040     ....
49041     toolbars : [
49042         { xtype: 'ToolbarStandard', styles : {} }
49043         { xtype: 'ToolbarContext', disable : {} }
49044     ]
49045 })
49046
49047      
49048  * 
49049  * @config : {Object} disable List of elements to disable.. (not done yet.)
49050  * @config : {Object} styles  Map of styles available.
49051  * 
49052  */
49053
49054 Roo.form.HtmlEditor.ToolbarContext = function(config)
49055 {
49056     
49057     Roo.apply(this, config);
49058     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
49059     // dont call parent... till later.
49060     this.styles = this.styles || {};
49061 }
49062
49063  
49064
49065 Roo.form.HtmlEditor.ToolbarContext.types = {
49066     'IMG' : {
49067         width : {
49068             title: "Width",
49069             width: 40
49070         },
49071         height:  {
49072             title: "Height",
49073             width: 40
49074         },
49075         align: {
49076             title: "Align",
49077             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49078             width : 80
49079             
49080         },
49081         border: {
49082             title: "Border",
49083             width: 40
49084         },
49085         alt: {
49086             title: "Alt",
49087             width: 120
49088         },
49089         src : {
49090             title: "Src",
49091             width: 220
49092         }
49093         
49094     },
49095     
49096     'FIGURE' : {
49097          align: {
49098             title: "Align",
49099             opts : [ [""],[ "left"],[ "right"],[ "center"],[ "top"]],
49100             width : 80  
49101         }
49102     },
49103     'A' : {
49104         name : {
49105             title: "Name",
49106             width: 50
49107         },
49108         target:  {
49109             title: "Target",
49110             width: 120
49111         },
49112         href:  {
49113             title: "Href",
49114             width: 220
49115         } // border?
49116         
49117     },
49118     'TABLE' : {
49119         rows : {
49120             title: "Rows",
49121             width: 20
49122         },
49123         cols : {
49124             title: "Cols",
49125             width: 20
49126         },
49127         width : {
49128             title: "Width",
49129             width: 40
49130         },
49131         height : {
49132             title: "Height",
49133             width: 40
49134         },
49135         border : {
49136             title: "Border",
49137             width: 20
49138         }
49139     },
49140     'TD' : {
49141         width : {
49142             title: "Width",
49143             width: 40
49144         },
49145         height : {
49146             title: "Height",
49147             width: 40
49148         },   
49149         align: {
49150             title: "Align",
49151             opts : [[""],[ "left"],[ "center"],[ "right"],[ "justify"],[ "char"]],
49152             width: 80
49153         },
49154         valign: {
49155             title: "Valign",
49156             opts : [[""],[ "top"],[ "middle"],[ "bottom"],[ "baseline"]],
49157             width: 80
49158         },
49159         colspan: {
49160             title: "Colspan",
49161             width: 20
49162             
49163         },
49164          'font-family'  : {
49165             title : "Font",
49166             style : 'fontFamily',
49167             displayField: 'display',
49168             optname : 'font-family',
49169             width: 140
49170         }
49171     },
49172     'INPUT' : {
49173         name : {
49174             title: "name",
49175             width: 120
49176         },
49177         value : {
49178             title: "Value",
49179             width: 120
49180         },
49181         width : {
49182             title: "Width",
49183             width: 40
49184         }
49185     },
49186     'LABEL' : {
49187         'for' : {
49188             title: "For",
49189             width: 120
49190         }
49191     },
49192     'TEXTAREA' : {
49193         name : {
49194             title: "name",
49195             width: 120
49196         },
49197         rows : {
49198             title: "Rows",
49199             width: 20
49200         },
49201         cols : {
49202             title: "Cols",
49203             width: 20
49204         }
49205     },
49206     'SELECT' : {
49207         name : {
49208             title: "name",
49209             width: 120
49210         },
49211         selectoptions : {
49212             title: "Options",
49213             width: 200
49214         }
49215     },
49216     
49217     // should we really allow this??
49218     // should this just be 
49219     'BODY' : {
49220         title : {
49221             title: "Title",
49222             width: 200,
49223             disabled : true
49224         }
49225     },
49226     /*
49227     'SPAN' : {
49228         'font-family'  : {
49229             title : "Font",
49230             style : 'fontFamily',
49231             displayField: 'display',
49232             optname : 'font-family',
49233             width: 140
49234         }
49235     },
49236     'DIV' : {
49237         'font-family'  : {
49238             title : "Font",
49239             style : 'fontFamily',
49240             displayField: 'display',
49241             optname : 'font-family',
49242             width: 140
49243         }
49244     },
49245      'P' : {
49246         'font-family'  : {
49247             title : "Font",
49248             style : 'fontFamily',
49249             displayField: 'display',
49250             optname : 'font-family',
49251             width: 140
49252         }
49253     },
49254     */
49255     '*' : {
49256         // empty..
49257     }
49258
49259 };
49260
49261 // this should be configurable.. - you can either set it up using stores, or modify options somehwere..
49262 Roo.form.HtmlEditor.ToolbarContext.stores = false;
49263
49264 Roo.form.HtmlEditor.ToolbarContext.options = {
49265         'font-family'  : [ 
49266                 [ 'Helvetica,Arial,sans-serif', 'Helvetica'],
49267                 [ 'Courier New', 'Courier New'],
49268                 [ 'Tahoma', 'Tahoma'],
49269                 [ 'Times New Roman,serif', 'Times'],
49270                 [ 'Verdana','Verdana' ]
49271         ]
49272 };
49273
49274 // fixme - these need to be configurable..
49275  
49276
49277 //Roo.form.HtmlEditor.ToolbarContext.types
49278
49279
49280 Roo.apply(Roo.form.HtmlEditor.ToolbarContext.prototype,  {
49281     
49282     tb: false,
49283     
49284     rendered: false,
49285     
49286     editor : false,
49287     editorcore : false,
49288     /**
49289      * @cfg {Object} disable  List of toolbar elements to disable
49290          
49291      */
49292     disable : false,
49293     /**
49294      * @cfg {Object} styles List of styles 
49295      *    eg. { '*' : [ 'headline' ] , 'TD' : [ 'underline', 'double-underline' ] } 
49296      *
49297      * These must be defined in the page, so they get rendered correctly..
49298      * .headline { }
49299      * TD.underline { }
49300      * 
49301      */
49302     styles : false,
49303     
49304     options: false,
49305     
49306     toolbars : false,
49307     
49308     init : function(editor)
49309     {
49310         this.editor = editor;
49311         this.editorcore = editor.editorcore ? editor.editorcore : editor;
49312         var editorcore = this.editorcore;
49313         
49314         var fid = editorcore.frameId;
49315         var etb = this;
49316         function btn(id, toggle, handler){
49317             var xid = fid + '-'+ id ;
49318             return {
49319                 id : xid,
49320                 cmd : id,
49321                 cls : 'x-btn-icon x-edit-'+id,
49322                 enableToggle:toggle !== false,
49323                 scope: editorcore, // was editor...
49324                 handler:handler||editorcore.relayBtnCmd,
49325                 clickEvent:'mousedown',
49326                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
49327                 tabIndex:-1
49328             };
49329         }
49330         // create a new element.
49331         var wdiv = editor.wrap.createChild({
49332                 tag: 'div'
49333             }, editor.wrap.dom.firstChild.nextSibling, true);
49334         
49335         // can we do this more than once??
49336         
49337          // stop form submits
49338       
49339  
49340         // disable everything...
49341         var ty= Roo.form.HtmlEditor.ToolbarContext.types;
49342         this.toolbars = {};
49343            
49344         for (var i in  ty) {
49345           
49346             this.toolbars[i] = this.buildToolbar(ty[i],i);
49347         }
49348         this.tb = this.toolbars.BODY;
49349         this.tb.el.show();
49350         this.buildFooter();
49351         this.footer.show();
49352         editor.on('hide', function( ) { this.footer.hide() }, this);
49353         editor.on('show', function( ) { this.footer.show() }, this);
49354         
49355          
49356         this.rendered = true;
49357         
49358         // the all the btns;
49359         editor.on('editorevent', this.updateToolbar, this);
49360         // other toolbars need to implement this..
49361         //editor.on('editmodechange', this.updateToolbar, this);
49362     },
49363     
49364     
49365     
49366     /**
49367      * Protected method that will not generally be called directly. It triggers
49368      * a toolbar update by reading the markup state of the current selection in the editor.
49369      *
49370      * Note you can force an update by calling on('editorevent', scope, false)
49371      */
49372     updateToolbar: function(editor ,ev, sel){
49373
49374         //Roo.log(ev);
49375         // capture mouse up - this is handy for selecting images..
49376         // perhaps should go somewhere else...
49377         if(!this.editorcore.activated){
49378              this.editor.onFirstFocus();
49379             return;
49380         }
49381         
49382         
49383         
49384         // http://developer.yahoo.com/yui/docs/simple-editor.js.html
49385         // selectNode - might want to handle IE?
49386         
49387         
49388         
49389         if (ev &&
49390             (ev.type == 'mouseup' || ev.type == 'click' ) &&
49391             ev.target && ev.target.tagName == 'IMG') {
49392             // they have click on an image...
49393             // let's see if we can change the selection...
49394             sel = ev.target;
49395             
49396             
49397             this.editorcore.selectNode(sel);
49398              
49399         }  
49400         
49401       
49402         //var updateFooter = sel ? false : true; 
49403         
49404         
49405         var ans = this.editorcore.getAllAncestors();
49406         
49407         // pick
49408         var ty = Roo.form.HtmlEditor.ToolbarContext.types;
49409         
49410         if (!sel) { 
49411             sel = ans.length ? (ans[0] ?  ans[0]  : ans[1]) : this.editorcore.doc.body;
49412             sel = sel ? sel : this.editorcore.doc.body;
49413             sel = sel.tagName.length ? sel : this.editorcore.doc.body;
49414             
49415         }
49416         
49417         var tn = sel.tagName.toUpperCase();
49418         var lastSel = this.tb.selectedNode;
49419         this.tb.selectedNode = sel;
49420         var left_label = tn;
49421         
49422         // ok see if we are editing a block?
49423         
49424         var db = Roo.get(sel).findParent('[data-block]');
49425         var cepar = Roo.get(sel).findParent('[contenteditable=true]');
49426         if (db && cepar && cepar.tagName != 'BODY') {
49427             db = false; // we are inside an editable block.. = not sure how we are going to handle nested blocks!?
49428         }
49429         var block = false;
49430         if (db && !sel.hasAttribute('contenteditable') && sel.getAttribute('contenteditable') != 'true' ) {
49431             block = Roo.htmleditor.Block.factory(db);
49432             if (block) {
49433                 tn = 'BLOCK.' + db.getAttribute('data-block');
49434                 this.tb.selectedNode = db;
49435                 this.editorcore.selectNode(db);
49436                 if (typeof(this.toolbars[tn]) == 'undefined') {
49437                    this.toolbars[tn] = this.buildToolbar( block.context,tn ,block.friendly_name);
49438                 }
49439                 left_label = block.friendly_name;
49440                 ans = this.editorcore.getAllAncestors();
49441             }
49442             
49443                 
49444             
49445         }
49446         
49447         
49448         if (this.tb.name == tn && lastSel == this.tb.selectedNode && ev !== false) {
49449             return; // no change?
49450         }
49451         
49452         
49453           
49454         this.tb.el.hide();
49455         ///console.log("show: " + tn);
49456         this.tb =  typeof(this.toolbars[tn]) != 'undefined' ? this.toolbars[tn] : this.toolbars['*'];
49457         
49458         this.tb.el.show();
49459         // update name
49460         this.tb.items.first().el.innerHTML = left_label + ':&nbsp;';
49461         
49462         
49463         // update attributes
49464         if (block) {
49465              
49466             this.tb.fields.each(function(e) {
49467                 e.setValue(block[e.attrname]);
49468             });
49469             
49470             
49471         } else  if (this.tb.fields && this.tb.selectedNode) {
49472             this.tb.fields.each( function(e) {
49473                 if (e.stylename) {
49474                     e.setValue(this.tb.selectedNode.style[e.stylename]);
49475                     return;
49476                 } 
49477                 e.setValue(this.tb.selectedNode.getAttribute(e.attrname));
49478             }, this);
49479             this.updateToolbarStyles(this.tb.selectedNode);  
49480         }
49481         
49482         
49483        
49484         Roo.menu.MenuMgr.hideAll();
49485
49486         
49487         
49488     
49489         // update the footer
49490         //
49491         this.updateFooter(ans);
49492              
49493     },
49494     
49495     updateToolbarStyles : function(sel)
49496     {
49497          var hasStyles = false;
49498         for(var i in this.styles) {
49499             hasStyles = true;
49500             break;
49501         }
49502         
49503         // update styles
49504         if (hasStyles) { 
49505             var st = this.tb.fields.item(0);
49506             
49507             st.store.removeAll();
49508             var cn = sel.className.split(/\s+/);
49509             
49510             var avs = [];
49511             if (this.styles['*']) {
49512                 
49513                 Roo.each(this.styles['*'], function(v) {
49514                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49515                 });
49516             }
49517             if (this.styles[tn]) { 
49518                 Roo.each(this.styles[tn], function(v) {
49519                     avs.push( [ v , cn.indexOf(v) > -1 ? 1 : 0 ] );         
49520                 });
49521             }
49522             
49523             st.store.loadData(avs);
49524             st.collapse();
49525             st.setValue(cn);
49526         }
49527     },
49528     
49529      
49530     updateFooter : function(ans)
49531     {
49532         var html = '';
49533         if (ans === false) {
49534             this.footDisp.dom.innerHTML = '';
49535             return;
49536         }
49537         
49538         this.footerEls = ans.reverse();
49539         Roo.each(this.footerEls, function(a,i) {
49540             if (!a) { return; }
49541             html += html.length ? ' &gt; '  :  '';
49542             
49543             html += '<span class="x-ed-loc-' + i + '">' + a.tagName + '</span>';
49544             
49545         });
49546        
49547         // 
49548         var sz = this.footDisp.up('td').getSize();
49549         this.footDisp.dom.style.width = (sz.width -10) + 'px';
49550         this.footDisp.dom.style.marginLeft = '5px';
49551         
49552         this.footDisp.dom.style.overflow = 'hidden';
49553         
49554         this.footDisp.dom.innerHTML = html;
49555             
49556         
49557     },
49558    
49559        
49560     // private
49561     onDestroy : function(){
49562         if(this.rendered){
49563             
49564             this.tb.items.each(function(item){
49565                 if(item.menu){
49566                     item.menu.removeAll();
49567                     if(item.menu.el){
49568                         item.menu.el.destroy();
49569                     }
49570                 }
49571                 item.destroy();
49572             });
49573              
49574         }
49575     },
49576     onFirstFocus: function() {
49577         // need to do this for all the toolbars..
49578         this.tb.items.each(function(item){
49579            item.enable();
49580         });
49581     },
49582     buildToolbar: function(tlist, nm, friendly_name)
49583     {
49584         var editor = this.editor;
49585         var editorcore = this.editorcore;
49586          // create a new element.
49587         var wdiv = editor.wrap.createChild({
49588                 tag: 'div'
49589             }, editor.wrap.dom.firstChild.nextSibling, true);
49590         
49591        
49592         var tb = new Roo.Toolbar(wdiv);
49593         tb.name = nm;
49594         
49595         tb.add((typeof(friendly_name) == 'undefined' ? nm : friendly_name) + ":&nbsp;");
49596         
49597         var styles = Array.from(this.styles);
49598         
49599         
49600         // styles...
49601         if (styles && styles.length) {
49602             
49603             // this needs a multi-select checkbox...
49604             tb.addField( new Roo.form.ComboBox({
49605                 store: new Roo.data.SimpleStore({
49606                     id : 'val',
49607                     fields: ['val', 'selected'],
49608                     data : [] 
49609                 }),
49610                 name : '-roo-edit-className',
49611                 attrname : 'className',
49612                 displayField: 'val',
49613                 typeAhead: false,
49614                 mode: 'local',
49615                 editable : false,
49616                 triggerAction: 'all',
49617                 emptyText:'Select Style',
49618                 selectOnFocus:true,
49619                 width: 130,
49620                 listeners : {
49621                     'select': function(c, r, i) {
49622                         // initial support only for on class per el..
49623                         tb.selectedNode.className =  r ? r.get('val') : '';
49624                         editorcore.syncValue();
49625                     }
49626                 }
49627     
49628             }));
49629         }
49630         
49631         var tbc = Roo.form.HtmlEditor.ToolbarContext;
49632         
49633         
49634         for (var i in tlist) {
49635             
49636             var item = tlist[i];
49637             tb.add(item.title + ":&nbsp;");
49638             
49639             
49640             //optname == used so you can configure the options available..
49641             var opts = item.opts ? item.opts : false;
49642             if (item.optname) { // use the b
49643                 opts = Roo.form.HtmlEditor.ToolbarContext.options[item.optname];
49644            
49645             }
49646             
49647             if (opts) {
49648                 // opts == pulldown..
49649                 tb.addField( new Roo.form.ComboBox({
49650                     store:   typeof(tbc.stores[i]) != 'undefined' ?  Roo.factory(tbc.stores[i],Roo.data) : new Roo.data.SimpleStore({
49651                         id : 'val',
49652                         fields: ['val', 'display'],
49653                         data : opts  
49654                     }),
49655                     name : '-roo-edit-' + i,
49656                     
49657                     attrname : i,
49658                     stylename : item.style ? item.style : false,
49659                     
49660                     displayField: item.displayField ? item.displayField : 'val',
49661                     valueField :  'val',
49662                     typeAhead: false,
49663                     mode: typeof(tbc.stores[i]) != 'undefined'  ? 'remote' : 'local',
49664                     editable : false,
49665                     triggerAction: 'all',
49666                     emptyText:'Select',
49667                     selectOnFocus:true,
49668                     width: item.width ? item.width  : 130,
49669                     listeners : {
49670                         'select': function(c, r, i) {
49671                             if (tb.selectedNode.hasAttribute('data-block')) {
49672                                 var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49673                                 b[c.attrname] = r.get('val');
49674                                 b.updateElement(tb.selectedNode);
49675                                 editorcore.syncValue();
49676                                 return;
49677                             }
49678                             
49679                             if (c.stylename) {
49680                                 tb.selectedNode.style[c.stylename] =  r.get('val');
49681                                 editorcore.syncValue();
49682                                 return;
49683                             }
49684                             if (r === false) {
49685                                 tb.selectedNode.removeAttribute(c.attrname);
49686                                 editorcore.syncValue();
49687                                 return;
49688                             }
49689                             tb.selectedNode.setAttribute(c.attrname, r.get('val'));
49690                             editorcore.syncValue();
49691                         }
49692                     }
49693
49694                 }));
49695                 continue;
49696                     
49697                  
49698                 /*
49699                 tb.addField( new Roo.form.TextField({
49700                     name: i,
49701                     width: 100,
49702                     //allowBlank:false,
49703                     value: ''
49704                 }));
49705                 continue;
49706                 */
49707             }
49708             tb.addField( new Roo.form.TextField({
49709                 name: '-roo-edit-' + i,
49710                 attrname : i,
49711                 
49712                 width: item.width,
49713                 //allowBlank:true,
49714                 value: '',
49715                 listeners: {
49716                     'change' : function(f, nv, ov) {
49717                         
49718                         if (tb.selectedNode.hasAttribute('data-block')) {
49719                             var b = Roo.htmleditor.Block.factory(tb.selectedNode);
49720                             b[f.attrname] = nv;
49721                             b.updateElement(tb.selectedNode);
49722                             editorcore.syncValue();
49723                             return;
49724                         }
49725                         
49726                         tb.selectedNode.setAttribute(f.attrname, nv);
49727                         editorcore.syncValue();
49728                     }
49729                 }
49730             }));
49731              
49732         }
49733         
49734         var _this = this;
49735         
49736         if(nm == 'BODY'){
49737             tb.addSeparator();
49738         
49739             tb.addButton( {
49740                 text: 'Stylesheets',
49741
49742                 listeners : {
49743                     click : function ()
49744                     {
49745                         _this.editor.fireEvent('stylesheetsclick', _this.editor);
49746                     }
49747                 }
49748             });
49749         }
49750         
49751         tb.addFill();
49752         tb.addButton({
49753             text: 'Remove Block or Formating', // remove the tag, and puts the children outside...
49754     
49755             listeners : {
49756                 click : function ()
49757                 {
49758                     // remove
49759                     // undo does not work.
49760                     var sn = tb.selectedNode;
49761                     var stn =  sn.childNodes[0] || sn.nextSibling || sn.previousSibling || sn.parentNode;
49762                     if (sn.hasAttribute('data-block')) {
49763                         stn =  sn.nextSibling || sn.previousSibling || sn.parentNode;
49764                         sn.parentNode.removeChild(sn);
49765                         
49766                     } else {
49767                         // remove and keep parents.
49768                         a = new Roo.htmleditor.FilterKeepChildren({tag : false});
49769                         a.removeTag(sn);
49770                     }
49771                     
49772                     
49773                     var range = editorcore.createRange();
49774         
49775                     range.setStart(stn,0);
49776                     range.setEnd(stn,0); 
49777                     var selection = editorcore.getSelection();
49778                     selection.removeAllRanges();
49779                     selection.addRange(range);
49780                     
49781                     
49782                     //_this.updateToolbar(null, null, pn);
49783                     _this.updateToolbar(null, null, null);
49784                     _this.updateFooter(false);
49785                     
49786                 }
49787             }
49788             
49789                     
49790                 
49791             
49792         });
49793         
49794         
49795         tb.el.on('click', function(e){
49796             e.preventDefault(); // what does this do?
49797         });
49798         tb.el.setVisibilityMode( Roo.Element.DISPLAY);
49799         tb.el.hide();
49800         
49801         // dont need to disable them... as they will get hidden
49802         return tb;
49803          
49804         
49805     },
49806     buildFooter : function()
49807     {
49808         
49809         var fel = this.editor.wrap.createChild();
49810         this.footer = new Roo.Toolbar(fel);
49811         // toolbar has scrolly on left / right?
49812         var footDisp= new Roo.Toolbar.Fill();
49813         var _t = this;
49814         this.footer.add(
49815             {
49816                 text : '&lt;',
49817                 xtype: 'Button',
49818                 handler : function() {
49819                     _t.footDisp.scrollTo('left',0,true)
49820                 }
49821             }
49822         );
49823         this.footer.add( footDisp );
49824         this.footer.add( 
49825             {
49826                 text : '&gt;',
49827                 xtype: 'Button',
49828                 handler : function() {
49829                     // no animation..
49830                     _t.footDisp.select('span').last().scrollIntoView(_t.footDisp,true);
49831                 }
49832             }
49833         );
49834         var fel = Roo.get(footDisp.el);
49835         fel.addClass('x-editor-context');
49836         this.footDispWrap = fel; 
49837         this.footDispWrap.overflow  = 'hidden';
49838         
49839         this.footDisp = fel.createChild();
49840         this.footDispWrap.on('click', this.onContextClick, this)
49841         
49842         
49843     },
49844     onContextClick : function (ev,dom)
49845     {
49846         ev.preventDefault();
49847         var  cn = dom.className;
49848         //Roo.log(cn);
49849         if (!cn.match(/x-ed-loc-/)) {
49850             return;
49851         }
49852         var n = cn.split('-').pop();
49853         var ans = this.footerEls;
49854         var sel = ans[n];
49855         
49856          // pick
49857         var range = this.editorcore.createRange();
49858         
49859         range.selectNodeContents(sel);
49860         //range.selectNode(sel);
49861         
49862         
49863         var selection = this.editorcore.getSelection();
49864         selection.removeAllRanges();
49865         selection.addRange(range);
49866         
49867         
49868         
49869         this.updateToolbar(null, null, sel);
49870         
49871         
49872     }
49873     
49874     
49875     
49876     
49877     
49878 });
49879
49880
49881
49882
49883
49884 /*
49885  * Based on:
49886  * Ext JS Library 1.1.1
49887  * Copyright(c) 2006-2007, Ext JS, LLC.
49888  *
49889  * Originally Released Under LGPL - original licence link has changed is not relivant.
49890  *
49891  * Fork - LGPL
49892  * <script type="text/javascript">
49893  */
49894  
49895 /**
49896  * @class Roo.form.BasicForm
49897  * @extends Roo.util.Observable
49898  * Supplies the functionality to do "actions" on forms and initialize Roo.form.Field types on existing markup.
49899  * @constructor
49900  * @param {String/HTMLElement/Roo.Element} el The form element or its id
49901  * @param {Object} config Configuration options
49902  */
49903 Roo.form.BasicForm = function(el, config){
49904     this.allItems = [];
49905     this.childForms = [];
49906     Roo.apply(this, config);
49907     /*
49908      * The Roo.form.Field items in this form.
49909      * @type MixedCollection
49910      */
49911      
49912      
49913     this.items = new Roo.util.MixedCollection(false, function(o){
49914         return o.id || (o.id = Roo.id());
49915     });
49916     this.addEvents({
49917         /**
49918          * @event beforeaction
49919          * Fires before any action is performed. Return false to cancel the action.
49920          * @param {Form} this
49921          * @param {Action} action The action to be performed
49922          */
49923         beforeaction: true,
49924         /**
49925          * @event actionfailed
49926          * Fires when an action fails.
49927          * @param {Form} this
49928          * @param {Action} action The action that failed
49929          */
49930         actionfailed : true,
49931         /**
49932          * @event actioncomplete
49933          * Fires when an action is completed.
49934          * @param {Form} this
49935          * @param {Action} action The action that completed
49936          */
49937         actioncomplete : true
49938     });
49939     if(el){
49940         this.initEl(el);
49941     }
49942     Roo.form.BasicForm.superclass.constructor.call(this);
49943     
49944     Roo.form.BasicForm.popover.apply();
49945 };
49946
49947 Roo.extend(Roo.form.BasicForm, Roo.util.Observable, {
49948     /**
49949      * @cfg {String} method
49950      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
49951      */
49952     /**
49953      * @cfg {DataReader} reader
49954      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when executing "load" actions.
49955      * This is optional as there is built-in support for processing JSON.
49956      */
49957     /**
49958      * @cfg {DataReader} errorReader
49959      * An Roo.data.DataReader (e.g. {@link Roo.data.XmlReader}) to be used to read data when reading validation errors on "submit" actions.
49960      * This is completely optional as there is built-in support for processing JSON.
49961      */
49962     /**
49963      * @cfg {String} url
49964      * The URL to use for form actions if one isn't supplied in the action options.
49965      */
49966     /**
49967      * @cfg {Boolean} fileUpload
49968      * Set to true if this form is a file upload.
49969      */
49970      
49971     /**
49972      * @cfg {Object} baseParams
49973      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
49974      */
49975      /**
49976      
49977     /**
49978      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
49979      */
49980     timeout: 30,
49981
49982     // private
49983     activeAction : null,
49984
49985     /**
49986      * @cfg {Boolean} trackResetOnLoad If set to true, form.reset() resets to the last loaded
49987      * or setValues() data instead of when the form was first created.
49988      */
49989     trackResetOnLoad : false,
49990     
49991     
49992     /**
49993      * childForms - used for multi-tab forms
49994      * @type {Array}
49995      */
49996     childForms : false,
49997     
49998     /**
49999      * allItems - full list of fields.
50000      * @type {Array}
50001      */
50002     allItems : false,
50003     
50004     /**
50005      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
50006      * element by passing it or its id or mask the form itself by passing in true.
50007      * @type Mixed
50008      */
50009     waitMsgTarget : false,
50010     
50011     /**
50012      * @type Boolean
50013      */
50014     disableMask : false,
50015     
50016     /**
50017      * @cfg {Boolean} errorMask (true|false) default false
50018      */
50019     errorMask : false,
50020     
50021     /**
50022      * @cfg {Number} maskOffset Default 100
50023      */
50024     maskOffset : 100,
50025
50026     // private
50027     initEl : function(el){
50028         this.el = Roo.get(el);
50029         this.id = this.el.id || Roo.id();
50030         this.el.on('submit', this.onSubmit, this);
50031         this.el.addClass('x-form');
50032     },
50033
50034     // private
50035     onSubmit : function(e){
50036         e.stopEvent();
50037     },
50038
50039     /**
50040      * Returns true if client-side validation on the form is successful.
50041      * @return Boolean
50042      */
50043     isValid : function(){
50044         var valid = true;
50045         var target = false;
50046         this.items.each(function(f){
50047             if(f.validate()){
50048                 return;
50049             }
50050             
50051             valid = false;
50052                 
50053             if(!target && f.el.isVisible(true)){
50054                 target = f;
50055             }
50056         });
50057         
50058         if(this.errorMask && !valid){
50059             Roo.form.BasicForm.popover.mask(this, target);
50060         }
50061         
50062         return valid;
50063     },
50064     /**
50065      * Returns array of invalid form fields.
50066      * @return Array
50067      */
50068     
50069     invalidFields : function()
50070     {
50071         var ret = [];
50072         this.items.each(function(f){
50073             if(f.validate()){
50074                 return;
50075             }
50076             ret.push(f);
50077             
50078         });
50079         
50080         return ret;
50081     },
50082     
50083     
50084     /**
50085      * DEPRICATED Returns true if any fields in this form have changed since their original load. 
50086      * @return Boolean
50087      */
50088     isDirty : function(){
50089         var dirty = false;
50090         this.items.each(function(f){
50091            if(f.isDirty()){
50092                dirty = true;
50093                return false;
50094            }
50095         });
50096         return dirty;
50097     },
50098     
50099     /**
50100      * Returns true if any fields in this form have changed since their original load. (New version)
50101      * @return Boolean
50102      */
50103     
50104     hasChanged : function()
50105     {
50106         var dirty = false;
50107         this.items.each(function(f){
50108            if(f.hasChanged()){
50109                dirty = true;
50110                return false;
50111            }
50112         });
50113         return dirty;
50114         
50115     },
50116     /**
50117      * Resets all hasChanged to 'false' -
50118      * The old 'isDirty' used 'original value..' however this breaks reset() and a few other things.
50119      * So hasChanged storage is only to be used for this purpose
50120      * @return Boolean
50121      */
50122     resetHasChanged : function()
50123     {
50124         this.items.each(function(f){
50125            f.resetHasChanged();
50126         });
50127         
50128     },
50129     
50130     
50131     /**
50132      * Performs a predefined action (submit or load) or custom actions you define on this form.
50133      * @param {String} actionName The name of the action type
50134      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
50135      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
50136      * accept other config options):
50137      * <pre>
50138 Property          Type             Description
50139 ----------------  ---------------  ----------------------------------------------------------------------------------
50140 url               String           The url for the action (defaults to the form's url)
50141 method            String           The form method to use (defaults to the form's method, or POST if not defined)
50142 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
50143 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
50144                                    validate the form on the client (defaults to false)
50145      * </pre>
50146      * @return {BasicForm} this
50147      */
50148     doAction : function(action, options){
50149         if(typeof action == 'string'){
50150             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
50151         }
50152         if(this.fireEvent('beforeaction', this, action) !== false){
50153             this.beforeAction(action);
50154             action.run.defer(100, action);
50155         }
50156         return this;
50157     },
50158
50159     /**
50160      * Shortcut to do a submit action.
50161      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50162      * @return {BasicForm} this
50163      */
50164     submit : function(options){
50165         this.doAction('submit', options);
50166         return this;
50167     },
50168
50169     /**
50170      * Shortcut to do a load action.
50171      * @param {Object} options The options to pass to the action (see {@link #doAction} for details)
50172      * @return {BasicForm} this
50173      */
50174     load : function(options){
50175         this.doAction('load', options);
50176         return this;
50177     },
50178
50179     /**
50180      * Persists the values in this form into the passed Roo.data.Record object in a beginEdit/endEdit block.
50181      * @param {Record} record The record to edit
50182      * @return {BasicForm} this
50183      */
50184     updateRecord : function(record){
50185         record.beginEdit();
50186         var fs = record.fields;
50187         fs.each(function(f){
50188             var field = this.findField(f.name);
50189             if(field){
50190                 record.set(f.name, field.getValue());
50191             }
50192         }, this);
50193         record.endEdit();
50194         return this;
50195     },
50196
50197     /**
50198      * Loads an Roo.data.Record into this form.
50199      * @param {Record} record The record to load
50200      * @return {BasicForm} this
50201      */
50202     loadRecord : function(record){
50203         this.setValues(record.data);
50204         return this;
50205     },
50206
50207     // private
50208     beforeAction : function(action){
50209         var o = action.options;
50210         
50211         if(!this.disableMask) {
50212             if(this.waitMsgTarget === true){
50213                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
50214             }else if(this.waitMsgTarget){
50215                 this.waitMsgTarget = Roo.get(this.waitMsgTarget);
50216                 this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
50217             }else {
50218                 Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
50219             }
50220         }
50221         
50222          
50223     },
50224
50225     // private
50226     afterAction : function(action, success){
50227         this.activeAction = null;
50228         var o = action.options;
50229         
50230         if(!this.disableMask) {
50231             if(this.waitMsgTarget === true){
50232                 this.el.unmask();
50233             }else if(this.waitMsgTarget){
50234                 this.waitMsgTarget.unmask();
50235             }else{
50236                 Roo.MessageBox.updateProgress(1);
50237                 Roo.MessageBox.hide();
50238             }
50239         }
50240         
50241         if(success){
50242             if(o.reset){
50243                 this.reset();
50244             }
50245             Roo.callback(o.success, o.scope, [this, action]);
50246             this.fireEvent('actioncomplete', this, action);
50247             
50248         }else{
50249             
50250             // failure condition..
50251             // we have a scenario where updates need confirming.
50252             // eg. if a locking scenario exists..
50253             // we look for { errors : { needs_confirm : true }} in the response.
50254             if (
50255                 (typeof(action.result) != 'undefined')  &&
50256                 (typeof(action.result.errors) != 'undefined')  &&
50257                 (typeof(action.result.errors.needs_confirm) != 'undefined')
50258            ){
50259                 var _t = this;
50260                 Roo.MessageBox.confirm(
50261                     "Change requires confirmation",
50262                     action.result.errorMsg,
50263                     function(r) {
50264                         if (r != 'yes') {
50265                             return;
50266                         }
50267                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
50268                     }
50269                     
50270                 );
50271                 
50272                 
50273                 
50274                 return;
50275             }
50276             
50277             Roo.callback(o.failure, o.scope, [this, action]);
50278             // show an error message if no failed handler is set..
50279             if (!this.hasListener('actionfailed')) {
50280                 Roo.MessageBox.alert("Error",
50281                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
50282                         action.result.errorMsg :
50283                         "Saving Failed, please check your entries or try again"
50284                 );
50285             }
50286             
50287             this.fireEvent('actionfailed', this, action);
50288         }
50289         
50290     },
50291
50292     /**
50293      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
50294      * @param {String} id The value to search for
50295      * @return Field
50296      */
50297     findField : function(id){
50298         var field = this.items.get(id);
50299         if(!field){
50300             this.items.each(function(f){
50301                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
50302                     field = f;
50303                     return false;
50304                 }
50305             });
50306         }
50307         return field || null;
50308     },
50309
50310     /**
50311      * Add a secondary form to this one, 
50312      * Used to provide tabbed forms. One form is primary, with hidden values 
50313      * which mirror the elements from the other forms.
50314      * 
50315      * @param {Roo.form.Form} form to add.
50316      * 
50317      */
50318     addForm : function(form)
50319     {
50320        
50321         if (this.childForms.indexOf(form) > -1) {
50322             // already added..
50323             return;
50324         }
50325         this.childForms.push(form);
50326         var n = '';
50327         Roo.each(form.allItems, function (fe) {
50328             
50329             n = typeof(fe.getName) == 'undefined' ? fe.name : fe.getName();
50330             if (this.findField(n)) { // already added..
50331                 return;
50332             }
50333             var add = new Roo.form.Hidden({
50334                 name : n
50335             });
50336             add.render(this.el);
50337             
50338             this.add( add );
50339         }, this);
50340         
50341     },
50342     /**
50343      * Mark fields in this form invalid in bulk.
50344      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
50345      * @return {BasicForm} this
50346      */
50347     markInvalid : function(errors){
50348         if(errors instanceof Array){
50349             for(var i = 0, len = errors.length; i < len; i++){
50350                 var fieldError = errors[i];
50351                 var f = this.findField(fieldError.id);
50352                 if(f){
50353                     f.markInvalid(fieldError.msg);
50354                 }
50355             }
50356         }else{
50357             var field, id;
50358             for(id in errors){
50359                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
50360                     field.markInvalid(errors[id]);
50361                 }
50362             }
50363         }
50364         Roo.each(this.childForms || [], function (f) {
50365             f.markInvalid(errors);
50366         });
50367         
50368         return this;
50369     },
50370
50371     /**
50372      * Set values for fields in this form in bulk.
50373      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
50374      * @return {BasicForm} this
50375      */
50376     setValues : function(values){
50377         if(values instanceof Array){ // array of objects
50378             for(var i = 0, len = values.length; i < len; i++){
50379                 var v = values[i];
50380                 var f = this.findField(v.id);
50381                 if(f){
50382                     f.setValue(v.value);
50383                     if(this.trackResetOnLoad){
50384                         f.originalValue = f.getValue();
50385                     }
50386                 }
50387             }
50388         }else{ // object hash
50389             var field, id;
50390             for(id in values){
50391                 if(typeof values[id] != 'function' && (field = this.findField(id))){
50392                     
50393                     if (field.setFromData && 
50394                         field.valueField && 
50395                         field.displayField &&
50396                         // combos' with local stores can 
50397                         // be queried via setValue()
50398                         // to set their value..
50399                         (field.store && !field.store.isLocal)
50400                         ) {
50401                         // it's a combo
50402                         var sd = { };
50403                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
50404                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
50405                         field.setFromData(sd);
50406                         
50407                     } else {
50408                         field.setValue(values[id]);
50409                     }
50410                     
50411                     
50412                     if(this.trackResetOnLoad){
50413                         field.originalValue = field.getValue();
50414                     }
50415                 }
50416             }
50417         }
50418         this.resetHasChanged();
50419         
50420         
50421         Roo.each(this.childForms || [], function (f) {
50422             f.setValues(values);
50423             f.resetHasChanged();
50424         });
50425                 
50426         return this;
50427     },
50428  
50429     /**
50430      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
50431      * they are returned as an array.
50432      * @param {Boolean} asString
50433      * @return {Object}
50434      */
50435     getValues : function(asString){
50436         if (this.childForms) {
50437             // copy values from the child forms
50438             Roo.each(this.childForms, function (f) {
50439                 this.setValues(f.getValues());
50440             }, this);
50441         }
50442         
50443         // use formdata
50444         if (typeof(FormData) != 'undefined' && asString !== true) {
50445             // this relies on a 'recent' version of chrome apparently...
50446             try {
50447                 var fd = (new FormData(this.el.dom)).entries();
50448                 var ret = {};
50449                 var ent = fd.next();
50450                 while (!ent.done) {
50451                     ret[ent.value[0]] = ent.value[1]; // not sure how this will handle duplicates..
50452                     ent = fd.next();
50453                 };
50454                 return ret;
50455             } catch(e) {
50456                 
50457             }
50458             
50459         }
50460         
50461         
50462         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
50463         if(asString === true){
50464             return fs;
50465         }
50466         return Roo.urlDecode(fs);
50467     },
50468     
50469     /**
50470      * Returns the fields in this form as an object with key/value pairs. 
50471      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
50472      * @return {Object}
50473      */
50474     getFieldValues : function(with_hidden)
50475     {
50476         if (this.childForms) {
50477             // copy values from the child forms
50478             // should this call getFieldValues - probably not as we do not currently copy
50479             // hidden fields when we generate..
50480             Roo.each(this.childForms, function (f) {
50481                 this.setValues(f.getValues());
50482             }, this);
50483         }
50484         
50485         var ret = {};
50486         this.items.each(function(f){
50487             if (!f.getName()) {
50488                 return;
50489             }
50490             var v = f.getValue();
50491             if (f.inputType =='radio') {
50492                 if (typeof(ret[f.getName()]) == 'undefined') {
50493                     ret[f.getName()] = ''; // empty..
50494                 }
50495                 
50496                 if (!f.el.dom.checked) {
50497                     return;
50498                     
50499                 }
50500                 v = f.el.dom.value;
50501                 
50502             }
50503             
50504             // not sure if this supported any more..
50505             if ((typeof(v) == 'object') && f.getRawValue) {
50506                 v = f.getRawValue() ; // dates..
50507             }
50508             // combo boxes where name != hiddenName...
50509             if (f.name != f.getName()) {
50510                 ret[f.name] = f.getRawValue();
50511             }
50512             ret[f.getName()] = v;
50513         });
50514         
50515         return ret;
50516     },
50517
50518     /**
50519      * Clears all invalid messages in this form.
50520      * @return {BasicForm} this
50521      */
50522     clearInvalid : function(){
50523         this.items.each(function(f){
50524            f.clearInvalid();
50525         });
50526         
50527         Roo.each(this.childForms || [], function (f) {
50528             f.clearInvalid();
50529         });
50530         
50531         
50532         return this;
50533     },
50534
50535     /**
50536      * Resets this form.
50537      * @return {BasicForm} this
50538      */
50539     reset : function(){
50540         this.items.each(function(f){
50541             f.reset();
50542         });
50543         
50544         Roo.each(this.childForms || [], function (f) {
50545             f.reset();
50546         });
50547         this.resetHasChanged();
50548         
50549         return this;
50550     },
50551
50552     /**
50553      * Add Roo.form components to this form.
50554      * @param {Field} field1
50555      * @param {Field} field2 (optional)
50556      * @param {Field} etc (optional)
50557      * @return {BasicForm} this
50558      */
50559     add : function(){
50560         this.items.addAll(Array.prototype.slice.call(arguments, 0));
50561         return this;
50562     },
50563
50564
50565     /**
50566      * Removes a field from the items collection (does NOT remove its markup).
50567      * @param {Field} field
50568      * @return {BasicForm} this
50569      */
50570     remove : function(field){
50571         this.items.remove(field);
50572         return this;
50573     },
50574
50575     /**
50576      * Looks at the fields in this form, checks them for an id attribute,
50577      * and calls applyTo on the existing dom element with that id.
50578      * @return {BasicForm} this
50579      */
50580     render : function(){
50581         this.items.each(function(f){
50582             if(f.isFormField && !f.rendered && document.getElementById(f.id)){ // if the element exists
50583                 f.applyTo(f.id);
50584             }
50585         });
50586         return this;
50587     },
50588
50589     /**
50590      * Calls {@link Ext#apply} for all fields in this form with the passed object.
50591      * @param {Object} values
50592      * @return {BasicForm} this
50593      */
50594     applyToFields : function(o){
50595         this.items.each(function(f){
50596            Roo.apply(f, o);
50597         });
50598         return this;
50599     },
50600
50601     /**
50602      * Calls {@link Ext#applyIf} for all field in this form with the passed object.
50603      * @param {Object} values
50604      * @return {BasicForm} this
50605      */
50606     applyIfToFields : function(o){
50607         this.items.each(function(f){
50608            Roo.applyIf(f, o);
50609         });
50610         return this;
50611     }
50612 });
50613
50614 // back compat
50615 Roo.BasicForm = Roo.form.BasicForm;
50616
50617 Roo.apply(Roo.form.BasicForm, {
50618     
50619     popover : {
50620         
50621         padding : 5,
50622         
50623         isApplied : false,
50624         
50625         isMasked : false,
50626         
50627         form : false,
50628         
50629         target : false,
50630         
50631         intervalID : false,
50632         
50633         maskEl : false,
50634         
50635         apply : function()
50636         {
50637             if(this.isApplied){
50638                 return;
50639             }
50640             
50641             this.maskEl = {
50642                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
50643                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
50644                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
50645                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
50646             };
50647             
50648             this.maskEl.top.enableDisplayMode("block");
50649             this.maskEl.left.enableDisplayMode("block");
50650             this.maskEl.bottom.enableDisplayMode("block");
50651             this.maskEl.right.enableDisplayMode("block");
50652             
50653             Roo.get(document.body).on('click', function(){
50654                 this.unmask();
50655             }, this);
50656             
50657             Roo.get(document.body).on('touchstart', function(){
50658                 this.unmask();
50659             }, this);
50660             
50661             this.isApplied = true
50662         },
50663         
50664         mask : function(form, target)
50665         {
50666             this.form = form;
50667             
50668             this.target = target;
50669             
50670             if(!this.form.errorMask || !target.el){
50671                 return;
50672             }
50673             
50674             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.x-layout-active-content', 100, true) || Roo.get(document.body);
50675             
50676             var ot = this.target.el.calcOffsetsTo(scrollable);
50677             
50678             var scrollTo = ot[1] - this.form.maskOffset;
50679             
50680             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
50681             
50682             scrollable.scrollTo('top', scrollTo);
50683             
50684             var el = this.target.wrap || this.target.el;
50685             
50686             var box = el.getBox();
50687             
50688             this.maskEl.top.setStyle('position', 'absolute');
50689             this.maskEl.top.setStyle('z-index', 10000);
50690             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
50691             this.maskEl.top.setLeft(0);
50692             this.maskEl.top.setTop(0);
50693             this.maskEl.top.show();
50694             
50695             this.maskEl.left.setStyle('position', 'absolute');
50696             this.maskEl.left.setStyle('z-index', 10000);
50697             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
50698             this.maskEl.left.setLeft(0);
50699             this.maskEl.left.setTop(box.y - this.padding);
50700             this.maskEl.left.show();
50701
50702             this.maskEl.bottom.setStyle('position', 'absolute');
50703             this.maskEl.bottom.setStyle('z-index', 10000);
50704             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
50705             this.maskEl.bottom.setLeft(0);
50706             this.maskEl.bottom.setTop(box.bottom + this.padding);
50707             this.maskEl.bottom.show();
50708
50709             this.maskEl.right.setStyle('position', 'absolute');
50710             this.maskEl.right.setStyle('z-index', 10000);
50711             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
50712             this.maskEl.right.setLeft(box.right + this.padding);
50713             this.maskEl.right.setTop(box.y - this.padding);
50714             this.maskEl.right.show();
50715
50716             this.intervalID = window.setInterval(function() {
50717                 Roo.form.BasicForm.popover.unmask();
50718             }, 10000);
50719
50720             window.onwheel = function(){ return false;};
50721             
50722             (function(){ this.isMasked = true; }).defer(500, this);
50723             
50724         },
50725         
50726         unmask : function()
50727         {
50728             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
50729                 return;
50730             }
50731             
50732             this.maskEl.top.setStyle('position', 'absolute');
50733             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
50734             this.maskEl.top.hide();
50735
50736             this.maskEl.left.setStyle('position', 'absolute');
50737             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
50738             this.maskEl.left.hide();
50739
50740             this.maskEl.bottom.setStyle('position', 'absolute');
50741             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
50742             this.maskEl.bottom.hide();
50743
50744             this.maskEl.right.setStyle('position', 'absolute');
50745             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
50746             this.maskEl.right.hide();
50747             
50748             window.onwheel = function(){ return true;};
50749             
50750             if(this.intervalID){
50751                 window.clearInterval(this.intervalID);
50752                 this.intervalID = false;
50753             }
50754             
50755             this.isMasked = false;
50756             
50757         }
50758         
50759     }
50760     
50761 });/*
50762  * Based on:
50763  * Ext JS Library 1.1.1
50764  * Copyright(c) 2006-2007, Ext JS, LLC.
50765  *
50766  * Originally Released Under LGPL - original licence link has changed is not relivant.
50767  *
50768  * Fork - LGPL
50769  * <script type="text/javascript">
50770  */
50771
50772 /**
50773  * @class Roo.form.Form
50774  * @extends Roo.form.BasicForm
50775  * @children Roo.form.Column Roo.form.FieldSet Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
50776  * Adds the ability to dynamically render forms with JavaScript to {@link Roo.form.BasicForm}.
50777  * @constructor
50778  * @param {Object} config Configuration options
50779  */
50780 Roo.form.Form = function(config){
50781     var xitems =  [];
50782     if (config.items) {
50783         xitems = config.items;
50784         delete config.items;
50785     }
50786    
50787     
50788     Roo.form.Form.superclass.constructor.call(this, null, config);
50789     this.url = this.url || this.action;
50790     if(!this.root){
50791         this.root = new Roo.form.Layout(Roo.applyIf({
50792             id: Roo.id()
50793         }, config));
50794     }
50795     this.active = this.root;
50796     /**
50797      * Array of all the buttons that have been added to this form via {@link addButton}
50798      * @type Array
50799      */
50800     this.buttons = [];
50801     this.allItems = [];
50802     this.addEvents({
50803         /**
50804          * @event clientvalidation
50805          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
50806          * @param {Form} this
50807          * @param {Boolean} valid true if the form has passed client-side validation
50808          */
50809         clientvalidation: true,
50810         /**
50811          * @event rendered
50812          * Fires when the form is rendered
50813          * @param {Roo.form.Form} form
50814          */
50815         rendered : true
50816     });
50817     
50818     if (this.progressUrl) {
50819             // push a hidden field onto the list of fields..
50820             this.addxtype( {
50821                     xns: Roo.form, 
50822                     xtype : 'Hidden', 
50823                     name : 'UPLOAD_IDENTIFIER' 
50824             });
50825         }
50826         
50827     
50828     Roo.each(xitems, this.addxtype, this);
50829     
50830 };
50831
50832 Roo.extend(Roo.form.Form, Roo.form.BasicForm, {
50833      /**
50834      * @cfg {Roo.Button} buttons[] buttons at bottom of form
50835      */
50836     
50837     /**
50838      * @cfg {Number} labelWidth The width of labels. This property cascades to child containers.
50839      */
50840     /**
50841      * @cfg {String} itemCls A css class to apply to the x-form-item of fields. This property cascades to child containers.
50842      */
50843     /**
50844      * @cfg {String} buttonAlign Valid values are "left," "center" and "right" (defaults to "center")
50845      */
50846     buttonAlign:'center',
50847
50848     /**
50849      * @cfg {Number} minButtonWidth Minimum width of all buttons in pixels (defaults to 75)
50850      */
50851     minButtonWidth:75,
50852
50853     /**
50854      * @cfg {String} labelAlign Valid values are "left," "top" and "right" (defaults to "left").
50855      * This property cascades to child containers if not set.
50856      */
50857     labelAlign:'left',
50858
50859     /**
50860      * @cfg {Boolean} monitorValid If true the form monitors its valid state <b>client-side</b> and
50861      * fires a looping event with that state. This is required to bind buttons to the valid
50862      * state using the config value formBind:true on the button.
50863      */
50864     monitorValid : false,
50865
50866     /**
50867      * @cfg {Number} monitorPoll The milliseconds to poll valid state, ignored if monitorValid is not true (defaults to 200)
50868      */
50869     monitorPoll : 200,
50870     
50871     /**
50872      * @cfg {String} progressUrl - Url to return progress data 
50873      */
50874     
50875     progressUrl : false,
50876     /**
50877      * @cfg {boolean|FormData} formData - true to use new 'FormData' post, or set to a new FormData({dom form}) Object, if
50878      * sending a formdata with extra parameters - eg uploaded elements.
50879      */
50880     
50881     formData : false,
50882     
50883     /**
50884      * Opens a new {@link Roo.form.Column} container in the layout stack. If fields are passed after the config, the
50885      * fields are added and the column is closed. If no fields are passed the column remains open
50886      * until end() is called.
50887      * @param {Object} config The config to pass to the column
50888      * @param {Field} field1 (optional)
50889      * @param {Field} field2 (optional)
50890      * @param {Field} etc (optional)
50891      * @return Column The column container object
50892      */
50893     column : function(c){
50894         var col = new Roo.form.Column(c);
50895         this.start(col);
50896         if(arguments.length > 1){ // duplicate code required because of Opera
50897             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50898             this.end();
50899         }
50900         return col;
50901     },
50902
50903     /**
50904      * Opens a new {@link Roo.form.FieldSet} container in the layout stack. If fields are passed after the config, the
50905      * fields are added and the fieldset is closed. If no fields are passed the fieldset remains open
50906      * until end() is called.
50907      * @param {Object} config The config to pass to the fieldset
50908      * @param {Field} field1 (optional)
50909      * @param {Field} field2 (optional)
50910      * @param {Field} etc (optional)
50911      * @return FieldSet The fieldset container object
50912      */
50913     fieldset : function(c){
50914         var fs = new Roo.form.FieldSet(c);
50915         this.start(fs);
50916         if(arguments.length > 1){ // duplicate code required because of Opera
50917             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50918             this.end();
50919         }
50920         return fs;
50921     },
50922
50923     /**
50924      * Opens a new {@link Roo.form.Layout} container in the layout stack. If fields are passed after the config, the
50925      * fields are added and the container is closed. If no fields are passed the container remains open
50926      * until end() is called.
50927      * @param {Object} config The config to pass to the Layout
50928      * @param {Field} field1 (optional)
50929      * @param {Field} field2 (optional)
50930      * @param {Field} etc (optional)
50931      * @return Layout The container object
50932      */
50933     container : function(c){
50934         var l = new Roo.form.Layout(c);
50935         this.start(l);
50936         if(arguments.length > 1){ // duplicate code required because of Opera
50937             this.add.apply(this, Array.prototype.slice.call(arguments, 1));
50938             this.end();
50939         }
50940         return l;
50941     },
50942
50943     /**
50944      * Opens the passed container in the layout stack. The container can be any {@link Roo.form.Layout} or subclass.
50945      * @param {Object} container A Roo.form.Layout or subclass of Layout
50946      * @return {Form} this
50947      */
50948     start : function(c){
50949         // cascade label info
50950         Roo.applyIf(c, {'labelAlign': this.active.labelAlign, 'labelWidth': this.active.labelWidth, 'itemCls': this.active.itemCls});
50951         this.active.stack.push(c);
50952         c.ownerCt = this.active;
50953         this.active = c;
50954         return this;
50955     },
50956
50957     /**
50958      * Closes the current open container
50959      * @return {Form} this
50960      */
50961     end : function(){
50962         if(this.active == this.root){
50963             return this;
50964         }
50965         this.active = this.active.ownerCt;
50966         return this;
50967     },
50968
50969     /**
50970      * Add Roo.form components to the current open container (e.g. column, fieldset, etc.).  Fields added via this method
50971      * can also be passed with an additional property of fieldLabel, which if supplied, will provide the text to display
50972      * as the label of the field.
50973      * @param {Field} field1
50974      * @param {Field} field2 (optional)
50975      * @param {Field} etc. (optional)
50976      * @return {Form} this
50977      */
50978     add : function(){
50979         this.active.stack.push.apply(this.active.stack, arguments);
50980         this.allItems.push.apply(this.allItems,arguments);
50981         var r = [];
50982         for(var i = 0, a = arguments, len = a.length; i < len; i++) {
50983             if(a[i].isFormField){
50984                 r.push(a[i]);
50985             }
50986         }
50987         if(r.length > 0){
50988             Roo.form.Form.superclass.add.apply(this, r);
50989         }
50990         return this;
50991     },
50992     
50993
50994     
50995     
50996     
50997      /**
50998      * Find any element that has been added to a form, using it's ID or name
50999      * This can include framesets, columns etc. along with regular fields..
51000      * @param {String} id - id or name to find.
51001      
51002      * @return {Element} e - or false if nothing found.
51003      */
51004     findbyId : function(id)
51005     {
51006         var ret = false;
51007         if (!id) {
51008             return ret;
51009         }
51010         Roo.each(this.allItems, function(f){
51011             if (f.id == id || f.name == id ){
51012                 ret = f;
51013                 return false;
51014             }
51015         });
51016         return ret;
51017     },
51018
51019     
51020     
51021     /**
51022      * Render this form into the passed container. This should only be called once!
51023      * @param {String/HTMLElement/Element} container The element this component should be rendered into
51024      * @return {Form} this
51025      */
51026     render : function(ct)
51027     {
51028         
51029         
51030         
51031         ct = Roo.get(ct);
51032         var o = this.autoCreate || {
51033             tag: 'form',
51034             method : this.method || 'POST',
51035             id : this.id || Roo.id()
51036         };
51037         this.initEl(ct.createChild(o));
51038
51039         this.root.render(this.el);
51040         
51041        
51042              
51043         this.items.each(function(f){
51044             f.render('x-form-el-'+f.id);
51045         });
51046
51047         if(this.buttons.length > 0){
51048             // tables are required to maintain order and for correct IE layout
51049             var tb = this.el.createChild({cls:'x-form-btns-ct', cn: {
51050                 cls:"x-form-btns x-form-btns-"+this.buttonAlign,
51051                 html:'<table cellspacing="0"><tbody><tr></tr></tbody></table><div class="x-clear"></div>'
51052             }}, null, true);
51053             var tr = tb.getElementsByTagName('tr')[0];
51054             for(var i = 0, len = this.buttons.length; i < len; i++) {
51055                 var b = this.buttons[i];
51056                 var td = document.createElement('td');
51057                 td.className = 'x-form-btn-td';
51058                 b.render(tr.appendChild(td));
51059             }
51060         }
51061         if(this.monitorValid){ // initialize after render
51062             this.startMonitoring();
51063         }
51064         this.fireEvent('rendered', this);
51065         return this;
51066     },
51067
51068     /**
51069      * Adds a button to the footer of the form - this <b>must</b> be called before the form is rendered.
51070      * @param {String/Object} config A string becomes the button text, an object can either be a Button config
51071      * object or a valid Roo.DomHelper element config
51072      * @param {Function} handler The function called when the button is clicked
51073      * @param {Object} scope (optional) The scope of the handler function
51074      * @return {Roo.Button}
51075      */
51076     addButton : function(config, handler, scope){
51077         var bc = {
51078             handler: handler,
51079             scope: scope,
51080             minWidth: this.minButtonWidth,
51081             hideParent:true
51082         };
51083         if(typeof config == "string"){
51084             bc.text = config;
51085         }else{
51086             Roo.apply(bc, config);
51087         }
51088         var btn = new Roo.Button(null, bc);
51089         this.buttons.push(btn);
51090         return btn;
51091     },
51092
51093      /**
51094      * Adds a series of form elements (using the xtype property as the factory method.
51095      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column, (and 'end' to close a block)
51096      * @param {Object} config 
51097      */
51098     
51099     addxtype : function()
51100     {
51101         var ar = Array.prototype.slice.call(arguments, 0);
51102         var ret = false;
51103         for(var i = 0; i < ar.length; i++) {
51104             if (!ar[i]) {
51105                 continue; // skip -- if this happends something invalid got sent, we 
51106                 // should ignore it, as basically that interface element will not show up
51107                 // and that should be pretty obvious!!
51108             }
51109             
51110             if (Roo.form[ar[i].xtype]) {
51111                 ar[i].form = this;
51112                 var fe = Roo.factory(ar[i], Roo.form);
51113                 if (!ret) {
51114                     ret = fe;
51115                 }
51116                 fe.form = this;
51117                 if (fe.store) {
51118                     fe.store.form = this;
51119                 }
51120                 if (fe.isLayout) {  
51121                          
51122                     this.start(fe);
51123                     this.allItems.push(fe);
51124                     if (fe.items && fe.addxtype) {
51125                         fe.addxtype.apply(fe, fe.items);
51126                         delete fe.items;
51127                     }
51128                      this.end();
51129                     continue;
51130                 }
51131                 
51132                 
51133                  
51134                 this.add(fe);
51135               //  console.log('adding ' + ar[i].xtype);
51136             }
51137             if (ar[i].xtype == 'Button') {  
51138                 //console.log('adding button');
51139                 //console.log(ar[i]);
51140                 this.addButton(ar[i]);
51141                 this.allItems.push(fe);
51142                 continue;
51143             }
51144             
51145             if (ar[i].xtype == 'end') { // so we can add fieldsets... / layout etc.
51146                 alert('end is not supported on xtype any more, use items');
51147             //    this.end();
51148             //    //console.log('adding end');
51149             }
51150             
51151         }
51152         return ret;
51153     },
51154     
51155     /**
51156      * Starts monitoring of the valid state of this form. Usually this is done by passing the config
51157      * option "monitorValid"
51158      */
51159     startMonitoring : function(){
51160         if(!this.bound){
51161             this.bound = true;
51162             Roo.TaskMgr.start({
51163                 run : this.bindHandler,
51164                 interval : this.monitorPoll || 200,
51165                 scope: this
51166             });
51167         }
51168     },
51169
51170     /**
51171      * Stops monitoring of the valid state of this form
51172      */
51173     stopMonitoring : function(){
51174         this.bound = false;
51175     },
51176
51177     // private
51178     bindHandler : function(){
51179         if(!this.bound){
51180             return false; // stops binding
51181         }
51182         var valid = true;
51183         this.items.each(function(f){
51184             if(!f.isValid(true)){
51185                 valid = false;
51186                 return false;
51187             }
51188         });
51189         for(var i = 0, len = this.buttons.length; i < len; i++){
51190             var btn = this.buttons[i];
51191             if(btn.formBind === true && btn.disabled === valid){
51192                 btn.setDisabled(!valid);
51193             }
51194         }
51195         this.fireEvent('clientvalidation', this, valid);
51196     }
51197     
51198     
51199     
51200     
51201     
51202     
51203     
51204     
51205 });
51206
51207
51208 // back compat
51209 Roo.Form = Roo.form.Form;
51210 /*
51211  * Based on:
51212  * Ext JS Library 1.1.1
51213  * Copyright(c) 2006-2007, Ext JS, LLC.
51214  *
51215  * Originally Released Under LGPL - original licence link has changed is not relivant.
51216  *
51217  * Fork - LGPL
51218  * <script type="text/javascript">
51219  */
51220
51221 // as we use this in bootstrap.
51222 Roo.namespace('Roo.form');
51223  /**
51224  * @class Roo.form.Action
51225  * Internal Class used to handle form actions
51226  * @constructor
51227  * @param {Roo.form.BasicForm} el The form element or its id
51228  * @param {Object} config Configuration options
51229  */
51230
51231  
51232  
51233 // define the action interface
51234 Roo.form.Action = function(form, options){
51235     this.form = form;
51236     this.options = options || {};
51237 };
51238 /**
51239  * Client Validation Failed
51240  * @const 
51241  */
51242 Roo.form.Action.CLIENT_INVALID = 'client';
51243 /**
51244  * Server Validation Failed
51245  * @const 
51246  */
51247 Roo.form.Action.SERVER_INVALID = 'server';
51248  /**
51249  * Connect to Server Failed
51250  * @const 
51251  */
51252 Roo.form.Action.CONNECT_FAILURE = 'connect';
51253 /**
51254  * Reading Data from Server Failed
51255  * @const 
51256  */
51257 Roo.form.Action.LOAD_FAILURE = 'load';
51258
51259 Roo.form.Action.prototype = {
51260     type : 'default',
51261     failureType : undefined,
51262     response : undefined,
51263     result : undefined,
51264
51265     // interface method
51266     run : function(options){
51267
51268     },
51269
51270     // interface method
51271     success : function(response){
51272
51273     },
51274
51275     // interface method
51276     handleResponse : function(response){
51277
51278     },
51279
51280     // default connection failure
51281     failure : function(response){
51282         
51283         this.response = response;
51284         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51285         this.form.afterAction(this, false);
51286     },
51287
51288     processResponse : function(response){
51289         this.response = response;
51290         if(!response.responseText){
51291             return true;
51292         }
51293         this.result = this.handleResponse(response);
51294         return this.result;
51295     },
51296
51297     // utility functions used internally
51298     getUrl : function(appendParams){
51299         var url = this.options.url || this.form.url || this.form.el.dom.action;
51300         if(appendParams){
51301             var p = this.getParams();
51302             if(p){
51303                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
51304             }
51305         }
51306         return url;
51307     },
51308
51309     getMethod : function(){
51310         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
51311     },
51312
51313     getParams : function(){
51314         var bp = this.form.baseParams;
51315         var p = this.options.params;
51316         if(p){
51317             if(typeof p == "object"){
51318                 p = Roo.urlEncode(Roo.applyIf(p, bp));
51319             }else if(typeof p == 'string' && bp){
51320                 p += '&' + Roo.urlEncode(bp);
51321             }
51322         }else if(bp){
51323             p = Roo.urlEncode(bp);
51324         }
51325         return p;
51326     },
51327
51328     createCallback : function(){
51329         return {
51330             success: this.success,
51331             failure: this.failure,
51332             scope: this,
51333             timeout: (this.form.timeout*1000),
51334             upload: this.form.fileUpload ? this.success : undefined
51335         };
51336     }
51337 };
51338
51339 Roo.form.Action.Submit = function(form, options){
51340     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
51341 };
51342
51343 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
51344     type : 'submit',
51345
51346     haveProgress : false,
51347     uploadComplete : false,
51348     
51349     // uploadProgress indicator.
51350     uploadProgress : function()
51351     {
51352         if (!this.form.progressUrl) {
51353             return;
51354         }
51355         
51356         if (!this.haveProgress) {
51357             Roo.MessageBox.progress("Uploading", "Uploading");
51358         }
51359         if (this.uploadComplete) {
51360            Roo.MessageBox.hide();
51361            return;
51362         }
51363         
51364         this.haveProgress = true;
51365    
51366         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
51367         
51368         var c = new Roo.data.Connection();
51369         c.request({
51370             url : this.form.progressUrl,
51371             params: {
51372                 id : uid
51373             },
51374             method: 'GET',
51375             success : function(req){
51376                //console.log(data);
51377                 var rdata = false;
51378                 var edata;
51379                 try  {
51380                    rdata = Roo.decode(req.responseText)
51381                 } catch (e) {
51382                     Roo.log("Invalid data from server..");
51383                     Roo.log(edata);
51384                     return;
51385                 }
51386                 if (!rdata || !rdata.success) {
51387                     Roo.log(rdata);
51388                     Roo.MessageBox.alert(Roo.encode(rdata));
51389                     return;
51390                 }
51391                 var data = rdata.data;
51392                 
51393                 if (this.uploadComplete) {
51394                    Roo.MessageBox.hide();
51395                    return;
51396                 }
51397                    
51398                 if (data){
51399                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
51400                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
51401                     );
51402                 }
51403                 this.uploadProgress.defer(2000,this);
51404             },
51405        
51406             failure: function(data) {
51407                 Roo.log('progress url failed ');
51408                 Roo.log(data);
51409             },
51410             scope : this
51411         });
51412            
51413     },
51414     
51415     
51416     run : function()
51417     {
51418         // run get Values on the form, so it syncs any secondary forms.
51419         this.form.getValues();
51420         
51421         var o = this.options;
51422         var method = this.getMethod();
51423         var isPost = method == 'POST';
51424         if(o.clientValidation === false || this.form.isValid()){
51425             
51426             if (this.form.progressUrl) {
51427                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
51428                     (new Date() * 1) + '' + Math.random());
51429                     
51430             } 
51431             
51432             
51433             Roo.Ajax.request(Roo.apply(this.createCallback(), {
51434                 form:this.form.el.dom,
51435                 url:this.getUrl(!isPost),
51436                 method: method,
51437                 params:isPost ? this.getParams() : null,
51438                 isUpload: this.form.fileUpload,
51439                 formData : this.form.formData
51440             }));
51441             
51442             this.uploadProgress();
51443
51444         }else if (o.clientValidation !== false){ // client validation failed
51445             this.failureType = Roo.form.Action.CLIENT_INVALID;
51446             this.form.afterAction(this, false);
51447         }
51448     },
51449
51450     success : function(response)
51451     {
51452         this.uploadComplete= true;
51453         if (this.haveProgress) {
51454             Roo.MessageBox.hide();
51455         }
51456         
51457         
51458         var result = this.processResponse(response);
51459         if(result === true || result.success){
51460             this.form.afterAction(this, true);
51461             return;
51462         }
51463         if(result.errors){
51464             this.form.markInvalid(result.errors);
51465             this.failureType = Roo.form.Action.SERVER_INVALID;
51466         }
51467         this.form.afterAction(this, false);
51468     },
51469     failure : function(response)
51470     {
51471         this.uploadComplete= true;
51472         if (this.haveProgress) {
51473             Roo.MessageBox.hide();
51474         }
51475         
51476         this.response = response;
51477         this.failureType = Roo.form.Action.CONNECT_FAILURE;
51478         this.form.afterAction(this, false);
51479     },
51480     
51481     handleResponse : function(response){
51482         if(this.form.errorReader){
51483             var rs = this.form.errorReader.read(response);
51484             var errors = [];
51485             if(rs.records){
51486                 for(var i = 0, len = rs.records.length; i < len; i++) {
51487                     var r = rs.records[i];
51488                     errors[i] = r.data;
51489                 }
51490             }
51491             if(errors.length < 1){
51492                 errors = null;
51493             }
51494             return {
51495                 success : rs.success,
51496                 errors : errors
51497             };
51498         }
51499         var ret = false;
51500         try {
51501             ret = Roo.decode(response.responseText);
51502         } catch (e) {
51503             ret = {
51504                 success: false,
51505                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
51506                 errors : []
51507             };
51508         }
51509         return ret;
51510         
51511     }
51512 });
51513
51514
51515 Roo.form.Action.Load = function(form, options){
51516     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
51517     this.reader = this.form.reader;
51518 };
51519
51520 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
51521     type : 'load',
51522
51523     run : function(){
51524         
51525         Roo.Ajax.request(Roo.apply(
51526                 this.createCallback(), {
51527                     method:this.getMethod(),
51528                     url:this.getUrl(false),
51529                     params:this.getParams()
51530         }));
51531     },
51532
51533     success : function(response){
51534         
51535         var result = this.processResponse(response);
51536         if(result === true || !result.success || !result.data){
51537             this.failureType = Roo.form.Action.LOAD_FAILURE;
51538             this.form.afterAction(this, false);
51539             return;
51540         }
51541         this.form.clearInvalid();
51542         this.form.setValues(result.data);
51543         this.form.afterAction(this, true);
51544     },
51545
51546     handleResponse : function(response){
51547         if(this.form.reader){
51548             var rs = this.form.reader.read(response);
51549             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
51550             return {
51551                 success : rs.success,
51552                 data : data
51553             };
51554         }
51555         return Roo.decode(response.responseText);
51556     }
51557 });
51558
51559 Roo.form.Action.ACTION_TYPES = {
51560     'load' : Roo.form.Action.Load,
51561     'submit' : Roo.form.Action.Submit
51562 };/*
51563  * Based on:
51564  * Ext JS Library 1.1.1
51565  * Copyright(c) 2006-2007, Ext JS, LLC.
51566  *
51567  * Originally Released Under LGPL - original licence link has changed is not relivant.
51568  *
51569  * Fork - LGPL
51570  * <script type="text/javascript">
51571  */
51572  
51573 /**
51574  * @class Roo.form.Layout
51575  * @extends Roo.Component
51576  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51577  * Creates a container for layout and rendering of fields in an {@link Roo.form.Form}.
51578  * @constructor
51579  * @param {Object} config Configuration options
51580  */
51581 Roo.form.Layout = function(config){
51582     var xitems = [];
51583     if (config.items) {
51584         xitems = config.items;
51585         delete config.items;
51586     }
51587     Roo.form.Layout.superclass.constructor.call(this, config);
51588     this.stack = [];
51589     Roo.each(xitems, this.addxtype, this);
51590      
51591 };
51592
51593 Roo.extend(Roo.form.Layout, Roo.Component, {
51594     /**
51595      * @cfg {String/Object} autoCreate
51596      * A DomHelper element spec used to autocreate the layout (defaults to {tag: 'div', cls: 'x-form-ct'})
51597      */
51598     /**
51599      * @cfg {String/Object/Function} style
51600      * A style specification string, e.g. "width:100px", or object in the form {width:"100px"}, or
51601      * a function which returns such a specification.
51602      */
51603     /**
51604      * @cfg {String} labelAlign
51605      * Valid values are "left," "top" and "right" (defaults to "left")
51606      */
51607     /**
51608      * @cfg {Number} labelWidth
51609      * Fixed width in pixels of all field labels (defaults to undefined)
51610      */
51611     /**
51612      * @cfg {Boolean} clear
51613      * True to add a clearing element at the end of this layout, equivalent to CSS clear: both (defaults to true)
51614      */
51615     clear : true,
51616     /**
51617      * @cfg {String} labelSeparator
51618      * The separator to use after field labels (defaults to ':')
51619      */
51620     labelSeparator : ':',
51621     /**
51622      * @cfg {Boolean} hideLabels
51623      * True to suppress the display of field labels in this layout (defaults to false)
51624      */
51625     hideLabels : false,
51626
51627     // private
51628     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct'},
51629     
51630     isLayout : true,
51631     
51632     // private
51633     onRender : function(ct, position){
51634         if(this.el){ // from markup
51635             this.el = Roo.get(this.el);
51636         }else {  // generate
51637             var cfg = this.getAutoCreate();
51638             this.el = ct.createChild(cfg, position);
51639         }
51640         if(this.style){
51641             this.el.applyStyles(this.style);
51642         }
51643         if(this.labelAlign){
51644             this.el.addClass('x-form-label-'+this.labelAlign);
51645         }
51646         if(this.hideLabels){
51647             this.labelStyle = "display:none";
51648             this.elementStyle = "padding-left:0;";
51649         }else{
51650             if(typeof this.labelWidth == 'number'){
51651                 this.labelStyle = "width:"+this.labelWidth+"px;";
51652                 this.elementStyle = "padding-left:"+((this.labelWidth+(typeof this.labelPad == 'number' ? this.labelPad : 5))+'px')+";";
51653             }
51654             if(this.labelAlign == 'top'){
51655                 this.labelStyle = "width:auto;";
51656                 this.elementStyle = "padding-left:0;";
51657             }
51658         }
51659         var stack = this.stack;
51660         var slen = stack.length;
51661         if(slen > 0){
51662             if(!this.fieldTpl){
51663                 var t = new Roo.Template(
51664                     '<div class="x-form-item {5}">',
51665                         '<label for="{0}" style="{2}">{1}{4}</label>',
51666                         '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51667                         '</div>',
51668                     '</div><div class="x-form-clear-left"></div>'
51669                 );
51670                 t.disableFormats = true;
51671                 t.compile();
51672                 Roo.form.Layout.prototype.fieldTpl = t;
51673             }
51674             for(var i = 0; i < slen; i++) {
51675                 if(stack[i].isFormField){
51676                     this.renderField(stack[i]);
51677                 }else{
51678                     this.renderComponent(stack[i]);
51679                 }
51680             }
51681         }
51682         if(this.clear){
51683             this.el.createChild({cls:'x-form-clear'});
51684         }
51685     },
51686
51687     // private
51688     renderField : function(f){
51689         f.fieldEl = Roo.get(this.fieldTpl.append(this.el, [
51690                f.id, //0
51691                f.fieldLabel, //1
51692                f.labelStyle||this.labelStyle||'', //2
51693                this.elementStyle||'', //3
51694                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator, //4
51695                f.itemCls||this.itemCls||''  //5
51696        ], true).getPrevSibling());
51697     },
51698
51699     // private
51700     renderComponent : function(c){
51701         c.render(c.isLayout ? this.el : this.el.createChild());    
51702     },
51703     /**
51704      * Adds a object form elements (using the xtype property as the factory method.)
51705      * Valid xtypes are:  TextField, TextArea .... Button, Layout, FieldSet, Column
51706      * @param {Object} config 
51707      */
51708     addxtype : function(o)
51709     {
51710         // create the lement.
51711         o.form = this.form;
51712         var fe = Roo.factory(o, Roo.form);
51713         this.form.allItems.push(fe);
51714         this.stack.push(fe);
51715         
51716         if (fe.isFormField) {
51717             this.form.items.add(fe);
51718         }
51719          
51720         return fe;
51721     }
51722 });
51723
51724 /**
51725  * @class Roo.form.Column
51726  * @extends Roo.form.Layout
51727  * Creates a column container for layout and rendering of fields in an {@link Roo.form.Form}.
51728  * @constructor
51729  * @param {Object} config Configuration options
51730  */
51731 Roo.form.Column = function(config){
51732     Roo.form.Column.superclass.constructor.call(this, config);
51733 };
51734
51735 Roo.extend(Roo.form.Column, Roo.form.Layout, {
51736     /**
51737      * @cfg {Number/String} width
51738      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51739      */
51740     /**
51741      * @cfg {String/Object} autoCreate
51742      * A DomHelper element spec used to autocreate the column (defaults to {tag: 'div', cls: 'x-form-ct x-form-column'})
51743      */
51744
51745     // private
51746     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-column'},
51747
51748     // private
51749     onRender : function(ct, position){
51750         Roo.form.Column.superclass.onRender.call(this, ct, position);
51751         if(this.width){
51752             this.el.setWidth(this.width);
51753         }
51754     }
51755 });
51756
51757
51758 /**
51759  * @class Roo.form.Row
51760  * @extends Roo.form.Layout
51761  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51762  * Creates a row container for layout and rendering of fields in an {@link Roo.form.Form}.
51763  * @constructor
51764  * @param {Object} config Configuration options
51765  */
51766
51767  
51768 Roo.form.Row = function(config){
51769     Roo.form.Row.superclass.constructor.call(this, config);
51770 };
51771  
51772 Roo.extend(Roo.form.Row, Roo.form.Layout, {
51773       /**
51774      * @cfg {Number/String} width
51775      * The fixed width of the column in pixels or CSS value (defaults to "auto")
51776      */
51777     /**
51778      * @cfg {Number/String} height
51779      * The fixed height of the column in pixels or CSS value (defaults to "auto")
51780      */
51781     defaultAutoCreate : {tag: 'div', cls: 'x-form-ct x-form-row'},
51782     
51783     padWidth : 20,
51784     // private
51785     onRender : function(ct, position){
51786         //console.log('row render');
51787         if(!this.rowTpl){
51788             var t = new Roo.Template(
51789                 '<div class="x-form-item {5}" style="float:left;width:{6}px">',
51790                     '<label for="{0}" style="{2}">{1}{4}</label>',
51791                     '<div class="x-form-element" id="x-form-el-{0}" style="{3}">',
51792                     '</div>',
51793                 '</div>'
51794             );
51795             t.disableFormats = true;
51796             t.compile();
51797             Roo.form.Layout.prototype.rowTpl = t;
51798         }
51799         this.fieldTpl = this.rowTpl;
51800         
51801         //console.log('lw' + this.labelWidth +', la:' + this.labelAlign);
51802         var labelWidth = 100;
51803         
51804         if ((this.labelAlign != 'top')) {
51805             if (typeof this.labelWidth == 'number') {
51806                 labelWidth = this.labelWidth
51807             }
51808             this.padWidth =  20 + labelWidth;
51809             
51810         }
51811         
51812         Roo.form.Column.superclass.onRender.call(this, ct, position);
51813         if(this.width){
51814             this.el.setWidth(this.width);
51815         }
51816         if(this.height){
51817             this.el.setHeight(this.height);
51818         }
51819     },
51820     
51821     // private
51822     renderField : function(f){
51823         f.fieldEl = this.fieldTpl.append(this.el, [
51824                f.id, f.fieldLabel,
51825                f.labelStyle||this.labelStyle||'',
51826                this.elementStyle||'',
51827                typeof f.labelSeparator == 'undefined' ? this.labelSeparator : f.labelSeparator,
51828                f.itemCls||this.itemCls||'',
51829                f.width ? f.width + this.padWidth : 160 + this.padWidth
51830        ],true);
51831     }
51832 });
51833  
51834
51835 /**
51836  * @class Roo.form.FieldSet
51837  * @extends Roo.form.Layout
51838  * @children Roo.form.Column Roo.form.Row Roo.form.Field Roo.Button Roo.form.TextItem
51839  * Creates a fieldset container for layout and rendering of fields in an {@link Roo.form.Form}.
51840  * @constructor
51841  * @param {Object} config Configuration options
51842  */
51843 Roo.form.FieldSet = function(config){
51844     Roo.form.FieldSet.superclass.constructor.call(this, config);
51845 };
51846
51847 Roo.extend(Roo.form.FieldSet, Roo.form.Layout, {
51848     /**
51849      * @cfg {String} legend
51850      * The text to display as the legend for the FieldSet (defaults to '')
51851      */
51852     /**
51853      * @cfg {String/Object} autoCreate
51854      * A DomHelper element spec used to autocreate the fieldset (defaults to {tag: 'fieldset', cn: {tag:'legend'}})
51855      */
51856
51857     // private
51858     defaultAutoCreate : {tag: 'fieldset', cn: {tag:'legend'}},
51859
51860     // private
51861     onRender : function(ct, position){
51862         Roo.form.FieldSet.superclass.onRender.call(this, ct, position);
51863         if(this.legend){
51864             this.setLegend(this.legend);
51865         }
51866     },
51867
51868     // private
51869     setLegend : function(text){
51870         if(this.rendered){
51871             this.el.child('legend').update(text);
51872         }
51873     }
51874 });/*
51875  * Based on:
51876  * Ext JS Library 1.1.1
51877  * Copyright(c) 2006-2007, Ext JS, LLC.
51878  *
51879  * Originally Released Under LGPL - original licence link has changed is not relivant.
51880  *
51881  * Fork - LGPL
51882  * <script type="text/javascript">
51883  */
51884 /**
51885  * @class Roo.form.VTypes
51886  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
51887  * @static
51888  */
51889 Roo.form.VTypes = function(){
51890     // closure these in so they are only created once.
51891     var alpha = /^[a-zA-Z_]+$/;
51892     var alphanum = /^[a-zA-Z0-9_]+$/;
51893     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
51894     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
51895
51896     // All these messages and functions are configurable
51897     return {
51898         /**
51899          * The function used to validate email addresses
51900          * @param {String} value The email address
51901          */
51902         'email' : function(v){
51903             return email.test(v);
51904         },
51905         /**
51906          * The error text to display when the email validation function returns false
51907          * @type String
51908          */
51909         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
51910         /**
51911          * The keystroke filter mask to be applied on email input
51912          * @type RegExp
51913          */
51914         'emailMask' : /[a-z0-9_\.\-@]/i,
51915
51916         /**
51917          * The function used to validate URLs
51918          * @param {String} value The URL
51919          */
51920         'url' : function(v){
51921             return url.test(v);
51922         },
51923         /**
51924          * The error text to display when the url validation function returns false
51925          * @type String
51926          */
51927         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
51928         
51929         /**
51930          * The function used to validate alpha values
51931          * @param {String} value The value
51932          */
51933         'alpha' : function(v){
51934             return alpha.test(v);
51935         },
51936         /**
51937          * The error text to display when the alpha validation function returns false
51938          * @type String
51939          */
51940         'alphaText' : 'This field should only contain letters and _',
51941         /**
51942          * The keystroke filter mask to be applied on alpha input
51943          * @type RegExp
51944          */
51945         'alphaMask' : /[a-z_]/i,
51946
51947         /**
51948          * The function used to validate alphanumeric values
51949          * @param {String} value The value
51950          */
51951         'alphanum' : function(v){
51952             return alphanum.test(v);
51953         },
51954         /**
51955          * The error text to display when the alphanumeric validation function returns false
51956          * @type String
51957          */
51958         'alphanumText' : 'This field should only contain letters, numbers and _',
51959         /**
51960          * The keystroke filter mask to be applied on alphanumeric input
51961          * @type RegExp
51962          */
51963         'alphanumMask' : /[a-z0-9_]/i
51964     };
51965 }();//<script type="text/javascript">
51966
51967 /**
51968  * @class Roo.form.FCKeditor
51969  * @extends Roo.form.TextArea
51970  * Wrapper around the FCKEditor http://www.fckeditor.net
51971  * @constructor
51972  * Creates a new FCKeditor
51973  * @param {Object} config Configuration options
51974  */
51975 Roo.form.FCKeditor = function(config){
51976     Roo.form.FCKeditor.superclass.constructor.call(this, config);
51977     this.addEvents({
51978          /**
51979          * @event editorinit
51980          * Fired when the editor is initialized - you can add extra handlers here..
51981          * @param {FCKeditor} this
51982          * @param {Object} the FCK object.
51983          */
51984         editorinit : true
51985     });
51986     
51987     
51988 };
51989 Roo.form.FCKeditor.editors = { };
51990 Roo.extend(Roo.form.FCKeditor, Roo.form.TextArea,
51991 {
51992     //defaultAutoCreate : {
51993     //    tag : "textarea",style   : "width:100px;height:60px;" ,autocomplete    : "off"
51994     //},
51995     // private
51996     /**
51997      * @cfg {Object} fck options - see fck manual for details.
51998      */
51999     fckconfig : false,
52000     
52001     /**
52002      * @cfg {Object} fck toolbar set (Basic or Default)
52003      */
52004     toolbarSet : 'Basic',
52005     /**
52006      * @cfg {Object} fck BasePath
52007      */ 
52008     basePath : '/fckeditor/',
52009     
52010     
52011     frame : false,
52012     
52013     value : '',
52014     
52015    
52016     onRender : function(ct, position)
52017     {
52018         if(!this.el){
52019             this.defaultAutoCreate = {
52020                 tag: "textarea",
52021                 style:"width:300px;height:60px;",
52022                 autocomplete: "new-password"
52023             };
52024         }
52025         Roo.form.FCKeditor.superclass.onRender.call(this, ct, position);
52026         /*
52027         if(this.grow){
52028             this.textSizeEl = Roo.DomHelper.append(document.body, {tag: "pre", cls: "x-form-grow-sizer"});
52029             if(this.preventScrollbars){
52030                 this.el.setStyle("overflow", "hidden");
52031             }
52032             this.el.setHeight(this.growMin);
52033         }
52034         */
52035         //console.log('onrender' + this.getId() );
52036         Roo.form.FCKeditor.editors[this.getId()] = this;
52037          
52038
52039         this.replaceTextarea() ;
52040         
52041     },
52042     
52043     getEditor : function() {
52044         return this.fckEditor;
52045     },
52046     /**
52047      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
52048      * @param {Mixed} value The value to set
52049      */
52050     
52051     
52052     setValue : function(value)
52053     {
52054         //console.log('setValue: ' + value);
52055         
52056         if(typeof(value) == 'undefined') { // not sure why this is happending...
52057             return;
52058         }
52059         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52060         
52061         //if(!this.el || !this.getEditor()) {
52062         //    this.value = value;
52063             //this.setValue.defer(100,this,[value]);    
52064         //    return;
52065         //} 
52066         
52067         if(!this.getEditor()) {
52068             return;
52069         }
52070         
52071         this.getEditor().SetData(value);
52072         
52073         //
52074
52075     },
52076
52077     /**
52078      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
52079      * @return {Mixed} value The field value
52080      */
52081     getValue : function()
52082     {
52083         
52084         if (this.frame && this.frame.dom.style.display == 'none') {
52085             return Roo.form.FCKeditor.superclass.getValue.call(this);
52086         }
52087         
52088         if(!this.el || !this.getEditor()) {
52089            
52090            // this.getValue.defer(100,this); 
52091             return this.value;
52092         }
52093        
52094         
52095         var value=this.getEditor().GetData();
52096         Roo.form.FCKeditor.superclass.setValue.apply(this,[value]);
52097         return Roo.form.FCKeditor.superclass.getValue.call(this);
52098         
52099
52100     },
52101
52102     /**
52103      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
52104      * @return {Mixed} value The field value
52105      */
52106     getRawValue : function()
52107     {
52108         if (this.frame && this.frame.dom.style.display == 'none') {
52109             return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52110         }
52111         
52112         if(!this.el || !this.getEditor()) {
52113             //this.getRawValue.defer(100,this); 
52114             return this.value;
52115             return;
52116         }
52117         
52118         
52119         
52120         var value=this.getEditor().GetData();
52121         Roo.form.FCKeditor.superclass.setRawValue.apply(this,[value]);
52122         return Roo.form.FCKeditor.superclass.getRawValue.call(this);
52123          
52124     },
52125     
52126     setSize : function(w,h) {
52127         
52128         
52129         
52130         //if (this.frame && this.frame.dom.style.display == 'none') {
52131         //    Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52132         //    return;
52133         //}
52134         //if(!this.el || !this.getEditor()) {
52135         //    this.setSize.defer(100,this, [w,h]); 
52136         //    return;
52137         //}
52138         
52139         
52140         
52141         Roo.form.FCKeditor.superclass.setSize.apply(this, [w, h]);
52142         
52143         this.frame.dom.setAttribute('width', w);
52144         this.frame.dom.setAttribute('height', h);
52145         this.frame.setSize(w,h);
52146         
52147     },
52148     
52149     toggleSourceEdit : function(value) {
52150         
52151       
52152          
52153         this.el.dom.style.display = value ? '' : 'none';
52154         this.frame.dom.style.display = value ?  'none' : '';
52155         
52156     },
52157     
52158     
52159     focus: function(tag)
52160     {
52161         if (this.frame.dom.style.display == 'none') {
52162             return Roo.form.FCKeditor.superclass.focus.call(this);
52163         }
52164         if(!this.el || !this.getEditor()) {
52165             this.focus.defer(100,this, [tag]); 
52166             return;
52167         }
52168         
52169         
52170         
52171         
52172         var tgs = this.getEditor().EditorDocument.getElementsByTagName(tag);
52173         this.getEditor().Focus();
52174         if (tgs.length) {
52175             if (!this.getEditor().Selection.GetSelection()) {
52176                 this.focus.defer(100,this, [tag]); 
52177                 return;
52178             }
52179             
52180             
52181             var r = this.getEditor().EditorDocument.createRange();
52182             r.setStart(tgs[0],0);
52183             r.setEnd(tgs[0],0);
52184             this.getEditor().Selection.GetSelection().removeAllRanges();
52185             this.getEditor().Selection.GetSelection().addRange(r);
52186             this.getEditor().Focus();
52187         }
52188         
52189     },
52190     
52191     
52192     
52193     replaceTextarea : function()
52194     {
52195         if ( document.getElementById( this.getId() + '___Frame' ) ) {
52196             return ;
52197         }
52198         //if ( !this.checkBrowser || this._isCompatibleBrowser() )
52199         //{
52200             // We must check the elements firstly using the Id and then the name.
52201         var oTextarea = document.getElementById( this.getId() );
52202         
52203         var colElementsByName = document.getElementsByName( this.getId() ) ;
52204          
52205         oTextarea.style.display = 'none' ;
52206
52207         if ( oTextarea.tabIndex ) {            
52208             this.TabIndex = oTextarea.tabIndex ;
52209         }
52210         
52211         this._insertHtmlBefore( this._getConfigHtml(), oTextarea ) ;
52212         this._insertHtmlBefore( this._getIFrameHtml(), oTextarea ) ;
52213         this.frame = Roo.get(this.getId() + '___Frame')
52214     },
52215     
52216     _getConfigHtml : function()
52217     {
52218         var sConfig = '' ;
52219
52220         for ( var o in this.fckconfig ) {
52221             sConfig += sConfig.length > 0  ? '&amp;' : '';
52222             sConfig += encodeURIComponent( o ) + '=' + encodeURIComponent( this.fckconfig[o] ) ;
52223         }
52224
52225         return '<input type="hidden" id="' + this.getId() + '___Config" value="' + sConfig + '" style="display:none" />' ;
52226     },
52227     
52228     
52229     _getIFrameHtml : function()
52230     {
52231         var sFile = 'fckeditor.html' ;
52232         /* no idea what this is about..
52233         try
52234         {
52235             if ( (/fcksource=true/i).test( window.top.location.search ) )
52236                 sFile = 'fckeditor.original.html' ;
52237         }
52238         catch (e) { 
52239         */
52240
52241         var sLink = this.basePath + 'editor/' + sFile + '?InstanceName=' + encodeURIComponent( this.getId() ) ;
52242         sLink += this.toolbarSet ? ( '&amp;Toolbar=' + this.toolbarSet)  : '';
52243         
52244         
52245         var html = '<iframe id="' + this.getId() +
52246             '___Frame" src="' + sLink +
52247             '" width="' + this.width +
52248             '" height="' + this.height + '"' +
52249             (this.tabIndex ?  ' tabindex="' + this.tabIndex + '"' :'' ) +
52250             ' frameborder="0" scrolling="no"></iframe>' ;
52251
52252         return html ;
52253     },
52254     
52255     _insertHtmlBefore : function( html, element )
52256     {
52257         if ( element.insertAdjacentHTML )       {
52258             // IE
52259             element.insertAdjacentHTML( 'beforeBegin', html ) ;
52260         } else { // Gecko
52261             var oRange = document.createRange() ;
52262             oRange.setStartBefore( element ) ;
52263             var oFragment = oRange.createContextualFragment( html );
52264             element.parentNode.insertBefore( oFragment, element ) ;
52265         }
52266     }
52267     
52268     
52269   
52270     
52271     
52272     
52273     
52274
52275 });
52276
52277 //Roo.reg('fckeditor', Roo.form.FCKeditor);
52278
52279 function FCKeditor_OnComplete(editorInstance){
52280     var f = Roo.form.FCKeditor.editors[editorInstance.Name];
52281     f.fckEditor = editorInstance;
52282     //console.log("loaded");
52283     f.fireEvent('editorinit', f, editorInstance);
52284
52285   
52286
52287  
52288
52289
52290
52291
52292
52293
52294
52295
52296
52297
52298
52299
52300
52301
52302
52303 //<script type="text/javascript">
52304 /**
52305  * @class Roo.form.GridField
52306  * @extends Roo.form.Field
52307  * Embed a grid (or editable grid into a form)
52308  * STATUS ALPHA
52309  * 
52310  * This embeds a grid in a form, the value of the field should be the json encoded array of rows
52311  * it needs 
52312  * xgrid.store = Roo.data.Store
52313  * xgrid.store.proxy = Roo.data.MemoryProxy (data = [] )
52314  * xgrid.store.reader = Roo.data.JsonReader 
52315  * 
52316  * 
52317  * @constructor
52318  * Creates a new GridField
52319  * @param {Object} config Configuration options
52320  */
52321 Roo.form.GridField = function(config){
52322     Roo.form.GridField.superclass.constructor.call(this, config);
52323      
52324 };
52325
52326 Roo.extend(Roo.form.GridField, Roo.form.Field,  {
52327     /**
52328      * @cfg {Number} width  - used to restrict width of grid..
52329      */
52330     width : 100,
52331     /**
52332      * @cfg {Number} height - used to restrict height of grid..
52333      */
52334     height : 50,
52335      /**
52336      * @cfg {Object} xgrid (xtype'd description of grid) { xtype : 'Grid', dataSource: .... }
52337          * 
52338          *}
52339      */
52340     xgrid : false, 
52341     /**
52342      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52343      * {tag: "input", type: "checkbox", autocomplete: "off"})
52344      */
52345    // defaultAutoCreate : { tag: 'div' },
52346     defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'new-password'},
52347     /**
52348      * @cfg {String} addTitle Text to include for adding a title.
52349      */
52350     addTitle : false,
52351     //
52352     onResize : function(){
52353         Roo.form.Field.superclass.onResize.apply(this, arguments);
52354     },
52355
52356     initEvents : function(){
52357         // Roo.form.Checkbox.superclass.initEvents.call(this);
52358         // has no events...
52359        
52360     },
52361
52362
52363     getResizeEl : function(){
52364         return this.wrap;
52365     },
52366
52367     getPositionEl : function(){
52368         return this.wrap;
52369     },
52370
52371     // private
52372     onRender : function(ct, position){
52373         
52374         this.style = this.style || 'overflow: hidden; border:1px solid #c3daf9;';
52375         var style = this.style;
52376         delete this.style;
52377         
52378         Roo.form.GridField.superclass.onRender.call(this, ct, position);
52379         this.wrap = this.el.wrap({cls: ''}); // not sure why ive done thsi...
52380         this.viewEl = this.wrap.createChild({ tag: 'div' });
52381         if (style) {
52382             this.viewEl.applyStyles(style);
52383         }
52384         if (this.width) {
52385             this.viewEl.setWidth(this.width);
52386         }
52387         if (this.height) {
52388             this.viewEl.setHeight(this.height);
52389         }
52390         //if(this.inputValue !== undefined){
52391         //this.setValue(this.value);
52392         
52393         
52394         this.grid = new Roo.grid[this.xgrid.xtype](this.viewEl, this.xgrid);
52395         
52396         
52397         this.grid.render();
52398         this.grid.getDataSource().on('remove', this.refreshValue, this);
52399         this.grid.getDataSource().on('update', this.refreshValue, this);
52400         this.grid.on('afteredit', this.refreshValue, this);
52401  
52402     },
52403      
52404     
52405     /**
52406      * Sets the value of the item. 
52407      * @param {String} either an object  or a string..
52408      */
52409     setValue : function(v){
52410         //this.value = v;
52411         v = v || []; // empty set..
52412         // this does not seem smart - it really only affects memoryproxy grids..
52413         if (this.grid && this.grid.getDataSource() && typeof(v) != 'undefined') {
52414             var ds = this.grid.getDataSource();
52415             // assumes a json reader..
52416             var data = {}
52417             data[ds.reader.meta.root ] =  typeof(v) == 'string' ? Roo.decode(v) : v;
52418             ds.loadData( data);
52419         }
52420         // clear selection so it does not get stale.
52421         if (this.grid.sm) { 
52422             this.grid.sm.clearSelections();
52423         }
52424         
52425         Roo.form.GridField.superclass.setValue.call(this, v);
52426         this.refreshValue();
52427         // should load data in the grid really....
52428     },
52429     
52430     // private
52431     refreshValue: function() {
52432          var val = [];
52433         this.grid.getDataSource().each(function(r) {
52434             val.push(r.data);
52435         });
52436         this.el.dom.value = Roo.encode(val);
52437     }
52438     
52439      
52440     
52441     
52442 });/*
52443  * Based on:
52444  * Ext JS Library 1.1.1
52445  * Copyright(c) 2006-2007, Ext JS, LLC.
52446  *
52447  * Originally Released Under LGPL - original licence link has changed is not relivant.
52448  *
52449  * Fork - LGPL
52450  * <script type="text/javascript">
52451  */
52452 /**
52453  * @class Roo.form.DisplayField
52454  * @extends Roo.form.Field
52455  * A generic Field to display non-editable data.
52456  * @cfg {Boolean} closable (true|false) default false
52457  * @constructor
52458  * Creates a new Display Field item.
52459  * @param {Object} config Configuration options
52460  */
52461 Roo.form.DisplayField = function(config){
52462     Roo.form.DisplayField.superclass.constructor.call(this, config);
52463     
52464     this.addEvents({
52465         /**
52466          * @event close
52467          * Fires after the click the close btn
52468              * @param {Roo.form.DisplayField} this
52469              */
52470         close : true
52471     });
52472 };
52473
52474 Roo.extend(Roo.form.DisplayField, Roo.form.TextField,  {
52475     inputType:      'hidden',
52476     allowBlank:     true,
52477     readOnly:         true,
52478     
52479  
52480     /**
52481      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52482      */
52483     focusClass : undefined,
52484     /**
52485      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52486      */
52487     fieldClass: 'x-form-field',
52488     
52489      /**
52490      * @cfg {Function} valueRenderer The renderer for the field (so you can reformat output). should return raw HTML
52491      */
52492     valueRenderer: undefined,
52493     
52494     width: 100,
52495     /**
52496      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52497      * {tag: "input", type: "checkbox", autocomplete: "off"})
52498      */
52499      
52500  //   defaultAutoCreate : { tag: 'input', type: 'hidden', autocomplete: 'off'},
52501  
52502     closable : false,
52503     
52504     onResize : function(){
52505         Roo.form.DisplayField.superclass.onResize.apply(this, arguments);
52506         
52507     },
52508
52509     initEvents : function(){
52510         // Roo.form.Checkbox.superclass.initEvents.call(this);
52511         // has no events...
52512         
52513         if(this.closable){
52514             this.closeEl.on('click', this.onClose, this);
52515         }
52516        
52517     },
52518
52519
52520     getResizeEl : function(){
52521         return this.wrap;
52522     },
52523
52524     getPositionEl : function(){
52525         return this.wrap;
52526     },
52527
52528     // private
52529     onRender : function(ct, position){
52530         
52531         Roo.form.DisplayField.superclass.onRender.call(this, ct, position);
52532         //if(this.inputValue !== undefined){
52533         this.wrap = this.el.wrap();
52534         
52535         this.viewEl = this.wrap.createChild({ tag: 'div', cls: 'x-form-displayfield'});
52536         
52537         if(this.closable){
52538             this.closeEl = this.wrap.createChild({ tag: 'div', cls: 'x-dlg-close'});
52539         }
52540         
52541         if (this.bodyStyle) {
52542             this.viewEl.applyStyles(this.bodyStyle);
52543         }
52544         //this.viewEl.setStyle('padding', '2px');
52545         
52546         this.setValue(this.value);
52547         
52548     },
52549 /*
52550     // private
52551     initValue : Roo.emptyFn,
52552
52553   */
52554
52555         // private
52556     onClick : function(){
52557         
52558     },
52559
52560     /**
52561      * Sets the checked state of the checkbox.
52562      * @param {Boolean/String} checked True, 'true', '1', or 'on' to check the checkbox, any other value will uncheck it.
52563      */
52564     setValue : function(v){
52565         this.value = v;
52566         var html = this.valueRenderer ?  this.valueRenderer(v) : String.format('{0}', v);
52567         // this might be called before we have a dom element..
52568         if (!this.viewEl) {
52569             return;
52570         }
52571         this.viewEl.dom.innerHTML = html;
52572         Roo.form.DisplayField.superclass.setValue.call(this, v);
52573
52574     },
52575     
52576     onClose : function(e)
52577     {
52578         e.preventDefault();
52579         
52580         this.fireEvent('close', this);
52581     }
52582 });/*
52583  * 
52584  * Licence- LGPL
52585  * 
52586  */
52587
52588 /**
52589  * @class Roo.form.DayPicker
52590  * @extends Roo.form.Field
52591  * A Day picker show [M] [T] [W] ....
52592  * @constructor
52593  * Creates a new Day Picker
52594  * @param {Object} config Configuration options
52595  */
52596 Roo.form.DayPicker= function(config){
52597     Roo.form.DayPicker.superclass.constructor.call(this, config);
52598      
52599 };
52600
52601 Roo.extend(Roo.form.DayPicker, Roo.form.Field,  {
52602     /**
52603      * @cfg {String} focusClass The CSS class to use when the checkbox receives focus (defaults to undefined)
52604      */
52605     focusClass : undefined,
52606     /**
52607      * @cfg {String} fieldClass The default CSS class for the checkbox (defaults to "x-form-field")
52608      */
52609     fieldClass: "x-form-field",
52610    
52611     /**
52612      * @cfg {String/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to
52613      * {tag: "input", type: "checkbox", autocomplete: "off"})
52614      */
52615     defaultAutoCreate : { tag: "input", type: 'hidden', autocomplete: "new-password"},
52616     
52617    
52618     actionMode : 'viewEl', 
52619     //
52620     // private
52621  
52622     inputType : 'hidden',
52623     
52624      
52625     inputElement: false, // real input element?
52626     basedOn: false, // ????
52627     
52628     isFormField: true, // not sure where this is needed!!!!
52629
52630     onResize : function(){
52631         Roo.form.Checkbox.superclass.onResize.apply(this, arguments);
52632         if(!this.boxLabel){
52633             this.el.alignTo(this.wrap, 'c-c');
52634         }
52635     },
52636
52637     initEvents : function(){
52638         Roo.form.Checkbox.superclass.initEvents.call(this);
52639         this.el.on("click", this.onClick,  this);
52640         this.el.on("change", this.onClick,  this);
52641     },
52642
52643
52644     getResizeEl : function(){
52645         return this.wrap;
52646     },
52647
52648     getPositionEl : function(){
52649         return this.wrap;
52650     },
52651
52652     
52653     // private
52654     onRender : function(ct, position){
52655         Roo.form.Checkbox.superclass.onRender.call(this, ct, position);
52656        
52657         this.wrap = this.el.wrap({cls: 'x-form-daypick-item '});
52658         
52659         var r1 = '<table><tr>';
52660         var r2 = '<tr class="x-form-daypick-icons">';
52661         for (var i=0; i < 7; i++) {
52662             r1+= '<td><div>' + Date.dayNames[i].substring(0,3) + '</div></td>';
52663             r2+= '<td><img class="x-menu-item-icon" src="' + Roo.BLANK_IMAGE_URL  +'"></td>';
52664         }
52665         
52666         var viewEl = this.wrap.createChild( r1 + '</tr>' + r2 + '</tr></table>');
52667         viewEl.select('img').on('click', this.onClick, this);
52668         this.viewEl = viewEl;   
52669         
52670         
52671         // this will not work on Chrome!!!
52672         this.el.on('DOMAttrModified', this.setFromHidden,  this); //ff
52673         this.el.on('propertychange', this.setFromHidden,  this);  //ie
52674         
52675         
52676           
52677
52678     },
52679
52680     // private
52681     initValue : Roo.emptyFn,
52682
52683     /**
52684      * Returns the checked state of the checkbox.
52685      * @return {Boolean} True if checked, else false
52686      */
52687     getValue : function(){
52688         return this.el.dom.value;
52689         
52690     },
52691
52692         // private
52693     onClick : function(e){ 
52694         //this.setChecked(!this.checked);
52695         Roo.get(e.target).toggleClass('x-menu-item-checked');
52696         this.refreshValue();
52697         //if(this.el.dom.checked != this.checked){
52698         //    this.setValue(this.el.dom.checked);
52699        // }
52700     },
52701     
52702     // private
52703     refreshValue : function()
52704     {
52705         var val = '';
52706         this.viewEl.select('img',true).each(function(e,i,n)  {
52707             val += e.is(".x-menu-item-checked") ? String(n) : '';
52708         });
52709         this.setValue(val, true);
52710     },
52711
52712     /**
52713      * Sets the checked state of the checkbox.
52714      * On is always based on a string comparison between inputValue and the param.
52715      * @param {Boolean/String} value - the value to set 
52716      * @param {Boolean/String} suppressEvent - whether to suppress the checkchange event.
52717      */
52718     setValue : function(v,suppressEvent){
52719         if (!this.el.dom) {
52720             return;
52721         }
52722         var old = this.el.dom.value ;
52723         this.el.dom.value = v;
52724         if (suppressEvent) {
52725             return ;
52726         }
52727          
52728         // update display..
52729         this.viewEl.select('img',true).each(function(e,i,n)  {
52730             
52731             var on = e.is(".x-menu-item-checked");
52732             var newv = v.indexOf(String(n)) > -1;
52733             if (on != newv) {
52734                 e.toggleClass('x-menu-item-checked');
52735             }
52736             
52737         });
52738         
52739         
52740         this.fireEvent('change', this, v, old);
52741         
52742         
52743     },
52744    
52745     // handle setting of hidden value by some other method!!?!?
52746     setFromHidden: function()
52747     {
52748         if(!this.el){
52749             return;
52750         }
52751         //console.log("SET FROM HIDDEN");
52752         //alert('setFrom hidden');
52753         this.setValue(this.el.dom.value);
52754     },
52755     
52756     onDestroy : function()
52757     {
52758         if(this.viewEl){
52759             Roo.get(this.viewEl).remove();
52760         }
52761          
52762         Roo.form.DayPicker.superclass.onDestroy.call(this);
52763     }
52764
52765 });/*
52766  * RooJS Library 1.1.1
52767  * Copyright(c) 2008-2011  Alan Knowles
52768  *
52769  * License - LGPL
52770  */
52771  
52772
52773 /**
52774  * @class Roo.form.ComboCheck
52775  * @extends Roo.form.ComboBox
52776  * A combobox for multiple select items.
52777  *
52778  * FIXME - could do with a reset button..
52779  * 
52780  * @constructor
52781  * Create a new ComboCheck
52782  * @param {Object} config Configuration options
52783  */
52784 Roo.form.ComboCheck = function(config){
52785     Roo.form.ComboCheck.superclass.constructor.call(this, config);
52786     // should verify some data...
52787     // like
52788     // hiddenName = required..
52789     // displayField = required
52790     // valudField == required
52791     var req= [ 'hiddenName', 'displayField', 'valueField' ];
52792     var _t = this;
52793     Roo.each(req, function(e) {
52794         if ((typeof(_t[e]) == 'undefined' ) || !_t[e].length) {
52795             throw "Roo.form.ComboCheck : missing value for: " + e;
52796         }
52797     });
52798     
52799     
52800 };
52801
52802 Roo.extend(Roo.form.ComboCheck, Roo.form.ComboBox, {
52803      
52804      
52805     editable : false,
52806      
52807     selectedClass: 'x-menu-item-checked', 
52808     
52809     // private
52810     onRender : function(ct, position){
52811         var _t = this;
52812         
52813         
52814         
52815         if(!this.tpl){
52816             var cls = 'x-combo-list';
52817
52818             
52819             this.tpl =  new Roo.Template({
52820                 html :  '<div class="'+cls+'-item x-menu-check-item">' +
52821                    '<img class="x-menu-item-icon" style="margin: 0px;" src="' + Roo.BLANK_IMAGE_URL + '">' + 
52822                    '<span>{' + this.displayField + '}</span>' +
52823                     '</div>' 
52824                 
52825             });
52826         }
52827  
52828         
52829         Roo.form.ComboCheck.superclass.onRender.call(this, ct, position);
52830         this.view.singleSelect = false;
52831         this.view.multiSelect = true;
52832         this.view.toggleSelect = true;
52833         this.pageTb.add(new Roo.Toolbar.Fill(), {
52834             
52835             text: 'Done',
52836             handler: function()
52837             {
52838                 _t.collapse();
52839             }
52840         });
52841     },
52842     
52843     onViewOver : function(e, t){
52844         // do nothing...
52845         return;
52846         
52847     },
52848     
52849     onViewClick : function(doFocus,index){
52850         return;
52851         
52852     },
52853     select: function () {
52854         //Roo.log("SELECT CALLED");
52855     },
52856      
52857     selectByValue : function(xv, scrollIntoView){
52858         var ar = this.getValueArray();
52859         var sels = [];
52860         
52861         Roo.each(ar, function(v) {
52862             if(v === undefined || v === null){
52863                 return;
52864             }
52865             var r = this.findRecord(this.valueField, v);
52866             if(r){
52867                 sels.push(this.store.indexOf(r))
52868                 
52869             }
52870         },this);
52871         this.view.select(sels);
52872         return false;
52873     },
52874     
52875     
52876     
52877     onSelect : function(record, index){
52878        // Roo.log("onselect Called");
52879        // this is only called by the clear button now..
52880         this.view.clearSelections();
52881         this.setValue('[]');
52882         if (this.value != this.valueBefore) {
52883             this.fireEvent('change', this, this.value, this.valueBefore);
52884             this.valueBefore = this.value;
52885         }
52886     },
52887     getValueArray : function()
52888     {
52889         var ar = [] ;
52890         
52891         try {
52892             //Roo.log(this.value);
52893             if (typeof(this.value) == 'undefined') {
52894                 return [];
52895             }
52896             var ar = Roo.decode(this.value);
52897             return  ar instanceof Array ? ar : []; //?? valid?
52898             
52899         } catch(e) {
52900             Roo.log(e + "\nRoo.form.ComboCheck:getValueArray  invalid data:" + this.getValue());
52901             return [];
52902         }
52903          
52904     },
52905     expand : function ()
52906     {
52907         
52908         Roo.form.ComboCheck.superclass.expand.call(this);
52909         this.valueBefore = typeof(this.value) == 'undefined' ? '' : this.value;
52910         //this.valueBefore = typeof(this.valueBefore) == 'undefined' ? '' : this.valueBefore;
52911         
52912
52913     },
52914     
52915     collapse : function(){
52916         Roo.form.ComboCheck.superclass.collapse.call(this);
52917         var sl = this.view.getSelectedIndexes();
52918         var st = this.store;
52919         var nv = [];
52920         var tv = [];
52921         var r;
52922         Roo.each(sl, function(i) {
52923             r = st.getAt(i);
52924             nv.push(r.get(this.valueField));
52925         },this);
52926         this.setValue(Roo.encode(nv));
52927         if (this.value != this.valueBefore) {
52928
52929             this.fireEvent('change', this, this.value, this.valueBefore);
52930             this.valueBefore = this.value;
52931         }
52932         
52933     },
52934     
52935     setValue : function(v){
52936         // Roo.log(v);
52937         this.value = v;
52938         
52939         var vals = this.getValueArray();
52940         var tv = [];
52941         Roo.each(vals, function(k) {
52942             var r = this.findRecord(this.valueField, k);
52943             if(r){
52944                 tv.push(r.data[this.displayField]);
52945             }else if(this.valueNotFoundText !== undefined){
52946                 tv.push( this.valueNotFoundText );
52947             }
52948         },this);
52949        // Roo.log(tv);
52950         
52951         Roo.form.ComboBox.superclass.setValue.call(this, tv.join(', '));
52952         this.hiddenField.value = v;
52953         this.value = v;
52954     }
52955     
52956 });/*
52957  * Based on:
52958  * Ext JS Library 1.1.1
52959  * Copyright(c) 2006-2007, Ext JS, LLC.
52960  *
52961  * Originally Released Under LGPL - original licence link has changed is not relivant.
52962  *
52963  * Fork - LGPL
52964  * <script type="text/javascript">
52965  */
52966  
52967 /**
52968  * @class Roo.form.Signature
52969  * @extends Roo.form.Field
52970  * Signature field.  
52971  * @constructor
52972  * 
52973  * @param {Object} config Configuration options
52974  */
52975
52976 Roo.form.Signature = function(config){
52977     Roo.form.Signature.superclass.constructor.call(this, config);
52978     
52979     this.addEvents({// not in used??
52980          /**
52981          * @event confirm
52982          * Fires when the 'confirm' icon is pressed (add a listener to enable add button)
52983              * @param {Roo.form.Signature} combo This combo box
52984              */
52985         'confirm' : true,
52986         /**
52987          * @event reset
52988          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
52989              * @param {Roo.form.ComboBox} combo This combo box
52990              * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
52991              */
52992         'reset' : true
52993     });
52994 };
52995
52996 Roo.extend(Roo.form.Signature, Roo.form.Field,  {
52997     /**
52998      * @cfg {Object} labels Label to use when rendering a form.
52999      * defaults to 
53000      * labels : { 
53001      *      clear : "Clear",
53002      *      confirm : "Confirm"
53003      *  }
53004      */
53005     labels : { 
53006         clear : "Clear",
53007         confirm : "Confirm"
53008     },
53009     /**
53010      * @cfg {Number} width The signature panel width (defaults to 300)
53011      */
53012     width: 300,
53013     /**
53014      * @cfg {Number} height The signature panel height (defaults to 100)
53015      */
53016     height : 100,
53017     /**
53018      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to false)
53019      */
53020     allowBlank : false,
53021     
53022     //private
53023     // {Object} signPanel The signature SVG panel element (defaults to {})
53024     signPanel : {},
53025     // {Boolean} isMouseDown False to validate that the mouse down event (defaults to false)
53026     isMouseDown : false,
53027     // {Boolean} isConfirmed validate the signature is confirmed or not for submitting form (defaults to false)
53028     isConfirmed : false,
53029     // {String} signatureTmp SVG mapping string (defaults to empty string)
53030     signatureTmp : '',
53031     
53032     
53033     defaultAutoCreate : { // modified by initCompnoent..
53034         tag: "input",
53035         type:"hidden"
53036     },
53037
53038     // private
53039     onRender : function(ct, position){
53040         
53041         Roo.form.Signature.superclass.onRender.call(this, ct, position);
53042         
53043         this.wrap = this.el.wrap({
53044             cls:'x-form-signature-wrap', style : 'width: ' + this.width + 'px', cn:{cls:'x-form-signature'}
53045         });
53046         
53047         this.createToolbar(this);
53048         this.signPanel = this.wrap.createChild({
53049                 tag: 'div',
53050                 style: 'width: ' + this.width + 'px; height: ' + this.height + 'px; border: 0;'
53051             }, this.el
53052         );
53053             
53054         this.svgID = Roo.id();
53055         this.svgEl = this.signPanel.createChild({
53056               xmlns : 'http://www.w3.org/2000/svg',
53057               tag : 'svg',
53058               id : this.svgID + "-svg",
53059               width: this.width,
53060               height: this.height,
53061               viewBox: '0 0 '+this.width+' '+this.height,
53062               cn : [
53063                 {
53064                     tag: "rect",
53065                     id: this.svgID + "-svg-r",
53066                     width: this.width,
53067                     height: this.height,
53068                     fill: "#ffa"
53069                 },
53070                 {
53071                     tag: "line",
53072                     id: this.svgID + "-svg-l",
53073                     x1: "0", // start
53074                     y1: (this.height*0.8), // start set the line in 80% of height
53075                     x2: this.width, // end
53076                     y2: (this.height*0.8), // end set the line in 80% of height
53077                     'stroke': "#666",
53078                     'stroke-width': "1",
53079                     'stroke-dasharray': "3",
53080                     'shape-rendering': "crispEdges",
53081                     'pointer-events': "none"
53082                 },
53083                 {
53084                     tag: "path",
53085                     id: this.svgID + "-svg-p",
53086                     'stroke': "navy",
53087                     'stroke-width': "3",
53088                     'fill': "none",
53089                     'pointer-events': 'none'
53090                 }
53091               ]
53092         });
53093         this.createSVG();
53094         this.svgBox = this.svgEl.dom.getScreenCTM();
53095     },
53096     createSVG : function(){ 
53097         var svg = this.signPanel;
53098         var r = svg.select('#'+ this.svgID + '-svg-r', true).first().dom;
53099         var t = this;
53100
53101         r.addEventListener('mousedown', function(e) { return t.down(e); }, false);
53102         r.addEventListener('mousemove', function(e) { return t.move(e); }, false);
53103         r.addEventListener('mouseup', function(e) { return t.up(e); }, false);
53104         r.addEventListener('mouseout', function(e) { return t.up(e); }, false);
53105         r.addEventListener('touchstart', function(e) { return t.down(e); }, false);
53106         r.addEventListener('touchmove', function(e) { return t.move(e); }, false);
53107         r.addEventListener('touchend', function(e) { return t.up(e); }, false);
53108         
53109     },
53110     isTouchEvent : function(e){
53111         return e.type.match(/^touch/);
53112     },
53113     getCoords : function (e) {
53114         var pt    = this.svgEl.dom.createSVGPoint();
53115         pt.x = e.clientX; 
53116         pt.y = e.clientY;
53117         if (this.isTouchEvent(e)) {
53118             pt.x =  e.targetTouches[0].clientX;
53119             pt.y = e.targetTouches[0].clientY;
53120         }
53121         var a = this.svgEl.dom.getScreenCTM();
53122         var b = a.inverse();
53123         var mx = pt.matrixTransform(b);
53124         return mx.x + ',' + mx.y;
53125     },
53126     //mouse event headler 
53127     down : function (e) {
53128         this.signatureTmp += 'M' + this.getCoords(e) + ' ';
53129         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr('d', this.signatureTmp);
53130         
53131         this.isMouseDown = true;
53132         
53133         e.preventDefault();
53134     },
53135     move : function (e) {
53136         if (this.isMouseDown) {
53137             this.signatureTmp += 'L' + this.getCoords(e) + ' ';
53138             this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', this.signatureTmp);
53139         }
53140         
53141         e.preventDefault();
53142     },
53143     up : function (e) {
53144         this.isMouseDown = false;
53145         var sp = this.signatureTmp.split(' ');
53146         
53147         if(sp.length > 1){
53148             if(!sp[sp.length-2].match(/^L/)){
53149                 sp.pop();
53150                 sp.pop();
53151                 sp.push("");
53152                 this.signatureTmp = sp.join(" ");
53153             }
53154         }
53155         if(this.getValue() != this.signatureTmp){
53156             this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53157             this.isConfirmed = false;
53158         }
53159         e.preventDefault();
53160     },
53161     
53162     /**
53163      * Protected method that will not generally be called directly. It
53164      * is called when the editor creates its toolbar. Override this method if you need to
53165      * add custom toolbar buttons.
53166      * @param {HtmlEditor} editor
53167      */
53168     createToolbar : function(editor){
53169          function btn(id, toggle, handler){
53170             var xid = fid + '-'+ id ;
53171             return {
53172                 id : xid,
53173                 cmd : id,
53174                 cls : 'x-btn-icon x-edit-'+id,
53175                 enableToggle:toggle !== false,
53176                 scope: editor, // was editor...
53177                 handler:handler||editor.relayBtnCmd,
53178                 clickEvent:'mousedown',
53179                 tooltip: etb.buttonTips[id] || undefined, ///tips ???
53180                 tabIndex:-1
53181             };
53182         }
53183         
53184         
53185         var tb = new Roo.Toolbar(editor.wrap.dom.firstChild);
53186         this.tb = tb;
53187         this.tb.add(
53188            {
53189                 cls : ' x-signature-btn x-signature-'+id,
53190                 scope: editor, // was editor...
53191                 handler: this.reset,
53192                 clickEvent:'mousedown',
53193                 text: this.labels.clear
53194             },
53195             {
53196                  xtype : 'Fill',
53197                  xns: Roo.Toolbar
53198             }, 
53199             {
53200                 cls : '  x-signature-btn x-signature-'+id,
53201                 scope: editor, // was editor...
53202                 handler: this.confirmHandler,
53203                 clickEvent:'mousedown',
53204                 text: this.labels.confirm
53205             }
53206         );
53207     
53208     },
53209     //public
53210     /**
53211      * when user is clicked confirm then show this image.....
53212      * 
53213      * @return {String} Image Data URI
53214      */
53215     getImageDataURI : function(){
53216         var svg = this.svgEl.dom.parentNode.innerHTML;
53217         var src = 'data:image/svg+xml;base64,'+window.btoa(svg);
53218         return src; 
53219     },
53220     /**
53221      * 
53222      * @return {Boolean} this.isConfirmed
53223      */
53224     getConfirmed : function(){
53225         return this.isConfirmed;
53226     },
53227     /**
53228      * 
53229      * @return {Number} this.width
53230      */
53231     getWidth : function(){
53232         return this.width;
53233     },
53234     /**
53235      * 
53236      * @return {Number} this.height
53237      */
53238     getHeight : function(){
53239         return this.height;
53240     },
53241     // private
53242     getSignature : function(){
53243         return this.signatureTmp;
53244     },
53245     // private
53246     reset : function(){
53247         this.signatureTmp = '';
53248         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53249         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', '');
53250         this.isConfirmed = false;
53251         Roo.form.Signature.superclass.reset.call(this);
53252     },
53253     setSignature : function(s){
53254         this.signatureTmp = s;
53255         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#ffa');
53256         this.signPanel.select('#'+ this.svgID + '-svg-p', true).first().attr( 'd', s);
53257         this.setValue(s);
53258         this.isConfirmed = false;
53259         Roo.form.Signature.superclass.reset.call(this);
53260     }, 
53261     test : function(){
53262 //        Roo.log(this.signPanel.dom.contentWindow.up())
53263     },
53264     //private
53265     setConfirmed : function(){
53266         
53267         
53268         
53269 //        Roo.log(Roo.get(this.signPanel.dom.contentWindow.r).attr('fill', '#cfc'));
53270     },
53271     // private
53272     confirmHandler : function(){
53273         if(!this.getSignature()){
53274             return;
53275         }
53276         
53277         this.signPanel.select('#'+ this.svgID + '-svg-r', true).first().attr('fill', '#cfc');
53278         this.setValue(this.getSignature());
53279         this.isConfirmed = true;
53280         
53281         this.fireEvent('confirm', this);
53282     },
53283     // private
53284     // Subclasses should provide the validation implementation by overriding this
53285     validateValue : function(value){
53286         if(this.allowBlank){
53287             return true;
53288         }
53289         
53290         if(this.isConfirmed){
53291             return true;
53292         }
53293         return false;
53294     }
53295 });/*
53296  * Based on:
53297  * Ext JS Library 1.1.1
53298  * Copyright(c) 2006-2007, Ext JS, LLC.
53299  *
53300  * Originally Released Under LGPL - original licence link has changed is not relivant.
53301  *
53302  * Fork - LGPL
53303  * <script type="text/javascript">
53304  */
53305  
53306
53307 /**
53308  * @class Roo.form.ComboBox
53309  * @extends Roo.form.TriggerField
53310  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
53311  * @constructor
53312  * Create a new ComboBox.
53313  * @param {Object} config Configuration options
53314  */
53315 Roo.form.Select = function(config){
53316     Roo.form.Select.superclass.constructor.call(this, config);
53317      
53318 };
53319
53320 Roo.extend(Roo.form.Select , Roo.form.ComboBox, {
53321     /**
53322      * @cfg {String/HTMLElement/Element} transform The id, DOM node or element of an existing select to convert to a ComboBox
53323      */
53324     /**
53325      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
53326      * rendering into an Roo.Editor, defaults to false)
53327      */
53328     /**
53329      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
53330      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
53331      */
53332     /**
53333      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
53334      */
53335     /**
53336      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
53337      * the dropdown list (defaults to undefined, with no header element)
53338      */
53339
53340      /**
53341      * @cfg {String/Roo.Template} tpl The template to use to render the output
53342      */
53343      
53344     // private
53345     defaultAutoCreate : {tag: "select"  },
53346     /**
53347      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
53348      */
53349     listWidth: undefined,
53350     /**
53351      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
53352      * mode = 'remote' or 'text' if mode = 'local')
53353      */
53354     displayField: undefined,
53355     /**
53356      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
53357      * mode = 'remote' or 'value' if mode = 'local'). 
53358      * Note: use of a valueField requires the user make a selection
53359      * in order for a value to be mapped.
53360      */
53361     valueField: undefined,
53362     
53363     
53364     /**
53365      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
53366      * field's data value (defaults to the underlying DOM element's name)
53367      */
53368     hiddenName: undefined,
53369     /**
53370      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
53371      */
53372     listClass: '',
53373     /**
53374      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
53375      */
53376     selectedClass: 'x-combo-selected',
53377     /**
53378      * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
53379      * class 'x-form-trigger' and triggerClass will be <b>appended</b> if specified (defaults to 'x-form-arrow-trigger'
53380      * which displays a downward arrow icon).
53381      */
53382     triggerClass : 'x-form-arrow-trigger',
53383     /**
53384      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
53385      */
53386     shadow:'sides',
53387     /**
53388      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
53389      * anchor positions (defaults to 'tl-bl')
53390      */
53391     listAlign: 'tl-bl?',
53392     /**
53393      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
53394      */
53395     maxHeight: 300,
53396     /**
53397      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
53398      * query specified by the allQuery config option (defaults to 'query')
53399      */
53400     triggerAction: 'query',
53401     /**
53402      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
53403      * (defaults to 4, does not apply if editable = false)
53404      */
53405     minChars : 4,
53406     /**
53407      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
53408      * delay (typeAheadDelay) if it matches a known value (defaults to false)
53409      */
53410     typeAhead: false,
53411     /**
53412      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
53413      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
53414      */
53415     queryDelay: 500,
53416     /**
53417      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
53418      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
53419      */
53420     pageSize: 0,
53421     /**
53422      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
53423      * when editable = true (defaults to false)
53424      */
53425     selectOnFocus:false,
53426     /**
53427      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
53428      */
53429     queryParam: 'query',
53430     /**
53431      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
53432      * when mode = 'remote' (defaults to 'Loading...')
53433      */
53434     loadingText: 'Loading...',
53435     /**
53436      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
53437      */
53438     resizable: false,
53439     /**
53440      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
53441      */
53442     handleHeight : 8,
53443     /**
53444      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
53445      * traditional select (defaults to true)
53446      */
53447     editable: true,
53448     /**
53449      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
53450      */
53451     allQuery: '',
53452     /**
53453      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
53454      */
53455     mode: 'remote',
53456     /**
53457      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
53458      * listWidth has a higher value)
53459      */
53460     minListWidth : 70,
53461     /**
53462      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
53463      * allow the user to set arbitrary text into the field (defaults to false)
53464      */
53465     forceSelection:false,
53466     /**
53467      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
53468      * if typeAhead = true (defaults to 250)
53469      */
53470     typeAheadDelay : 250,
53471     /**
53472      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
53473      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
53474      */
53475     valueNotFoundText : undefined,
53476     
53477     /**
53478      * @cfg {String} defaultValue The value displayed after loading the store.
53479      */
53480     defaultValue: '',
53481     
53482     /**
53483      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
53484      */
53485     blockFocus : false,
53486     
53487     /**
53488      * @cfg {Boolean} disableClear Disable showing of clear button.
53489      */
53490     disableClear : false,
53491     /**
53492      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
53493      */
53494     alwaysQuery : false,
53495     
53496     //private
53497     addicon : false,
53498     editicon: false,
53499     
53500     // element that contains real text value.. (when hidden is used..)
53501      
53502     // private
53503     onRender : function(ct, position){
53504         Roo.form.Field.prototype.onRender.call(this, ct, position);
53505         
53506         if(this.store){
53507             this.store.on('beforeload', this.onBeforeLoad, this);
53508             this.store.on('load', this.onLoad, this);
53509             this.store.on('loadexception', this.onLoadException, this);
53510             this.store.load({});
53511         }
53512         
53513         
53514         
53515     },
53516
53517     // private
53518     initEvents : function(){
53519         //Roo.form.ComboBox.superclass.initEvents.call(this);
53520  
53521     },
53522
53523     onDestroy : function(){
53524        
53525         if(this.store){
53526             this.store.un('beforeload', this.onBeforeLoad, this);
53527             this.store.un('load', this.onLoad, this);
53528             this.store.un('loadexception', this.onLoadException, this);
53529         }
53530         //Roo.form.ComboBox.superclass.onDestroy.call(this);
53531     },
53532
53533     // private
53534     fireKey : function(e){
53535         if(e.isNavKeyPress() && !this.list.isVisible()){
53536             this.fireEvent("specialkey", this, e);
53537         }
53538     },
53539
53540     // private
53541     onResize: function(w, h){
53542         
53543         return; 
53544     
53545         
53546     },
53547
53548     /**
53549      * Allow or prevent the user from directly editing the field text.  If false is passed,
53550      * the user will only be able to select from the items defined in the dropdown list.  This method
53551      * is the runtime equivalent of setting the 'editable' config option at config time.
53552      * @param {Boolean} value True to allow the user to directly edit the field text
53553      */
53554     setEditable : function(value){
53555          
53556     },
53557
53558     // private
53559     onBeforeLoad : function(){
53560         
53561         Roo.log("Select before load");
53562         return;
53563     
53564         this.innerList.update(this.loadingText ?
53565                '<div class="loading-indicator">'+this.loadingText+'</div>' : '');
53566         //this.restrictHeight();
53567         this.selectedIndex = -1;
53568     },
53569
53570     // private
53571     onLoad : function(){
53572
53573     
53574         var dom = this.el.dom;
53575         dom.innerHTML = '';
53576          var od = dom.ownerDocument;
53577          
53578         if (this.emptyText) {
53579             var op = od.createElement('option');
53580             op.setAttribute('value', '');
53581             op.innerHTML = String.format('{0}', this.emptyText);
53582             dom.appendChild(op);
53583         }
53584         if(this.store.getCount() > 0){
53585            
53586             var vf = this.valueField;
53587             var df = this.displayField;
53588             this.store.data.each(function(r) {
53589                 // which colmsn to use... testing - cdoe / title..
53590                 var op = od.createElement('option');
53591                 op.setAttribute('value', r.data[vf]);
53592                 op.innerHTML = String.format('{0}', r.data[df]);
53593                 dom.appendChild(op);
53594             });
53595             if (typeof(this.defaultValue != 'undefined')) {
53596                 this.setValue(this.defaultValue);
53597             }
53598             
53599              
53600         }else{
53601             //this.onEmptyResults();
53602         }
53603         //this.el.focus();
53604     },
53605     // private
53606     onLoadException : function()
53607     {
53608         dom.innerHTML = '';
53609             
53610         Roo.log("Select on load exception");
53611         return;
53612     
53613         this.collapse();
53614         Roo.log(this.store.reader.jsonData);
53615         if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
53616             Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
53617         }
53618         
53619         
53620     },
53621     // private
53622     onTypeAhead : function(){
53623          
53624     },
53625
53626     // private
53627     onSelect : function(record, index){
53628         Roo.log('on select?');
53629         return;
53630         if(this.fireEvent('beforeselect', this, record, index) !== false){
53631             this.setFromData(index > -1 ? record.data : false);
53632             this.collapse();
53633             this.fireEvent('select', this, record, index);
53634         }
53635     },
53636
53637     /**
53638      * Returns the currently selected field value or empty string if no value is set.
53639      * @return {String} value The selected value
53640      */
53641     getValue : function(){
53642         var dom = this.el.dom;
53643         this.value = dom.options[dom.selectedIndex].value;
53644         return this.value;
53645         
53646     },
53647
53648     /**
53649      * Clears any text/value currently set in the field
53650      */
53651     clearValue : function(){
53652         this.value = '';
53653         this.el.dom.selectedIndex = this.emptyText ? 0 : -1;
53654         
53655     },
53656
53657     /**
53658      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
53659      * will be displayed in the field.  If the value does not match the data value of an existing item,
53660      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
53661      * Otherwise the field will be blank (although the value will still be set).
53662      * @param {String} value The value to match
53663      */
53664     setValue : function(v){
53665         var d = this.el.dom;
53666         for (var i =0; i < d.options.length;i++) {
53667             if (v == d.options[i].value) {
53668                 d.selectedIndex = i;
53669                 this.value = v;
53670                 return;
53671             }
53672         }
53673         this.clearValue();
53674     },
53675     /**
53676      * @property {Object} the last set data for the element
53677      */
53678     
53679     lastData : false,
53680     /**
53681      * Sets the value of the field based on a object which is related to the record format for the store.
53682      * @param {Object} value the value to set as. or false on reset?
53683      */
53684     setFromData : function(o){
53685         Roo.log('setfrom data?');
53686          
53687         
53688         
53689     },
53690     // private
53691     reset : function(){
53692         this.clearValue();
53693     },
53694     // private
53695     findRecord : function(prop, value){
53696         
53697         return false;
53698     
53699         var record;
53700         if(this.store.getCount() > 0){
53701             this.store.each(function(r){
53702                 if(r.data[prop] == value){
53703                     record = r;
53704                     return false;
53705                 }
53706                 return true;
53707             });
53708         }
53709         return record;
53710     },
53711     
53712     getName: function()
53713     {
53714         // returns hidden if it's set..
53715         if (!this.rendered) {return ''};
53716         return !this.hiddenName && this.el.dom.name  ? this.el.dom.name : (this.hiddenName || '');
53717         
53718     },
53719      
53720
53721     
53722
53723     // private
53724     onEmptyResults : function(){
53725         Roo.log('empty results');
53726         //this.collapse();
53727     },
53728
53729     /**
53730      * Returns true if the dropdown list is expanded, else false.
53731      */
53732     isExpanded : function(){
53733         return false;
53734     },
53735
53736     /**
53737      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
53738      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53739      * @param {String} value The data value of the item to select
53740      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53741      * selected item if it is not currently in view (defaults to true)
53742      * @return {Boolean} True if the value matched an item in the list, else false
53743      */
53744     selectByValue : function(v, scrollIntoView){
53745         Roo.log('select By Value');
53746         return false;
53747     
53748         if(v !== undefined && v !== null){
53749             var r = this.findRecord(this.valueField || this.displayField, v);
53750             if(r){
53751                 this.select(this.store.indexOf(r), scrollIntoView);
53752                 return true;
53753             }
53754         }
53755         return false;
53756     },
53757
53758     /**
53759      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
53760      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
53761      * @param {Number} index The zero-based index of the list item to select
53762      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
53763      * selected item if it is not currently in view (defaults to true)
53764      */
53765     select : function(index, scrollIntoView){
53766         Roo.log('select ');
53767         return  ;
53768         
53769         this.selectedIndex = index;
53770         this.view.select(index);
53771         if(scrollIntoView !== false){
53772             var el = this.view.getNode(index);
53773             if(el){
53774                 this.innerList.scrollChildIntoView(el, false);
53775             }
53776         }
53777     },
53778
53779       
53780
53781     // private
53782     validateBlur : function(){
53783         
53784         return;
53785         
53786     },
53787
53788     // private
53789     initQuery : function(){
53790         this.doQuery(this.getRawValue());
53791     },
53792
53793     // private
53794     doForce : function(){
53795         if(this.el.dom.value.length > 0){
53796             this.el.dom.value =
53797                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
53798              
53799         }
53800     },
53801
53802     /**
53803      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
53804      * query allowing the query action to be canceled if needed.
53805      * @param {String} query The SQL query to execute
53806      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
53807      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
53808      * saved in the current store (defaults to false)
53809      */
53810     doQuery : function(q, forceAll){
53811         
53812         Roo.log('doQuery?');
53813         if(q === undefined || q === null){
53814             q = '';
53815         }
53816         var qe = {
53817             query: q,
53818             forceAll: forceAll,
53819             combo: this,
53820             cancel:false
53821         };
53822         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
53823             return false;
53824         }
53825         q = qe.query;
53826         forceAll = qe.forceAll;
53827         if(forceAll === true || (q.length >= this.minChars)){
53828             if(this.lastQuery != q || this.alwaysQuery){
53829                 this.lastQuery = q;
53830                 if(this.mode == 'local'){
53831                     this.selectedIndex = -1;
53832                     if(forceAll){
53833                         this.store.clearFilter();
53834                     }else{
53835                         this.store.filter(this.displayField, q);
53836                     }
53837                     this.onLoad();
53838                 }else{
53839                     this.store.baseParams[this.queryParam] = q;
53840                     this.store.load({
53841                         params: this.getParams(q)
53842                     });
53843                     this.expand();
53844                 }
53845             }else{
53846                 this.selectedIndex = -1;
53847                 this.onLoad();   
53848             }
53849         }
53850     },
53851
53852     // private
53853     getParams : function(q){
53854         var p = {};
53855         //p[this.queryParam] = q;
53856         if(this.pageSize){
53857             p.start = 0;
53858             p.limit = this.pageSize;
53859         }
53860         return p;
53861     },
53862
53863     /**
53864      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
53865      */
53866     collapse : function(){
53867         
53868     },
53869
53870     // private
53871     collapseIf : function(e){
53872         
53873     },
53874
53875     /**
53876      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
53877      */
53878     expand : function(){
53879         
53880     } ,
53881
53882     // private
53883      
53884
53885     /** 
53886     * @cfg {Boolean} grow 
53887     * @hide 
53888     */
53889     /** 
53890     * @cfg {Number} growMin 
53891     * @hide 
53892     */
53893     /** 
53894     * @cfg {Number} growMax 
53895     * @hide 
53896     */
53897     /**
53898      * @hide
53899      * @method autoSize
53900      */
53901     
53902     setWidth : function()
53903     {
53904         
53905     },
53906     getResizeEl : function(){
53907         return this.el;
53908     }
53909 });//<script type="text/javasscript">
53910  
53911
53912 /**
53913  * @class Roo.DDView
53914  * A DnD enabled version of Roo.View.
53915  * @param {Element/String} container The Element in which to create the View.
53916  * @param {String} tpl The template string used to create the markup for each element of the View
53917  * @param {Object} config The configuration properties. These include all the config options of
53918  * {@link Roo.View} plus some specific to this class.<br>
53919  * <p>
53920  * Drag/drop is implemented by adding {@link Roo.data.Record}s to the target DDView. If copying is
53921  * not being performed, the original {@link Roo.data.Record} is removed from the source DDView.<br>
53922  * <p>
53923  * The following extra CSS rules are needed to provide insertion point highlighting:<pre><code>
53924 .x-view-drag-insert-above {
53925         border-top:1px dotted #3366cc;
53926 }
53927 .x-view-drag-insert-below {
53928         border-bottom:1px dotted #3366cc;
53929 }
53930 </code></pre>
53931  * 
53932  */
53933  
53934 Roo.DDView = function(container, tpl, config) {
53935     Roo.DDView.superclass.constructor.apply(this, arguments);
53936     this.getEl().setStyle("outline", "0px none");
53937     this.getEl().unselectable();
53938     if (this.dragGroup) {
53939         this.setDraggable(this.dragGroup.split(","));
53940     }
53941     if (this.dropGroup) {
53942         this.setDroppable(this.dropGroup.split(","));
53943     }
53944     if (this.deletable) {
53945         this.setDeletable();
53946     }
53947     this.isDirtyFlag = false;
53948         this.addEvents({
53949                 "drop" : true
53950         });
53951 };
53952
53953 Roo.extend(Roo.DDView, Roo.View, {
53954 /**     @cfg {String/Array} dragGroup The ddgroup name(s) for the View's DragZone. */
53955 /**     @cfg {String/Array} dropGroup The ddgroup name(s) for the View's DropZone. */
53956 /**     @cfg {Boolean} copy Causes drag operations to copy nodes rather than move. */
53957 /**     @cfg {Boolean} allowCopy Causes ctrl/drag operations to copy nodes rather than move. */
53958
53959         isFormField: true,
53960
53961         reset: Roo.emptyFn,
53962         
53963         clearInvalid: Roo.form.Field.prototype.clearInvalid,
53964
53965         validate: function() {
53966                 return true;
53967         },
53968         
53969         destroy: function() {
53970                 this.purgeListeners();
53971                 this.getEl.removeAllListeners();
53972                 this.getEl().remove();
53973                 if (this.dragZone) {
53974                         if (this.dragZone.destroy) {
53975                                 this.dragZone.destroy();
53976                         }
53977                 }
53978                 if (this.dropZone) {
53979                         if (this.dropZone.destroy) {
53980                                 this.dropZone.destroy();
53981                         }
53982                 }
53983         },
53984
53985 /**     Allows this class to be an Roo.form.Field so it can be found using {@link Roo.form.BasicForm#findField}. */
53986         getName: function() {
53987                 return this.name;
53988         },
53989
53990 /**     Loads the View from a JSON string representing the Records to put into the Store. */
53991         setValue: function(v) {
53992                 if (!this.store) {
53993                         throw "DDView.setValue(). DDView must be constructed with a valid Store";
53994                 }
53995                 var data = {};
53996                 data[this.store.reader.meta.root] = v ? [].concat(v) : [];
53997                 this.store.proxy = new Roo.data.MemoryProxy(data);
53998                 this.store.load();
53999         },
54000
54001 /**     @return {String} a parenthesised list of the ids of the Records in the View. */
54002         getValue: function() {
54003                 var result = '(';
54004                 this.store.each(function(rec) {
54005                         result += rec.id + ',';
54006                 });
54007                 return result.substr(0, result.length - 1) + ')';
54008         },
54009         
54010         getIds: function() {
54011                 var i = 0, result = new Array(this.store.getCount());
54012                 this.store.each(function(rec) {
54013                         result[i++] = rec.id;
54014                 });
54015                 return result;
54016         },
54017         
54018         isDirty: function() {
54019                 return this.isDirtyFlag;
54020         },
54021
54022 /**
54023  *      Part of the Roo.dd.DropZone interface. If no target node is found, the
54024  *      whole Element becomes the target, and this causes the drop gesture to append.
54025  */
54026     getTargetFromEvent : function(e) {
54027                 var target = e.getTarget();
54028                 while ((target !== null) && (target.parentNode != this.el.dom)) {
54029                 target = target.parentNode;
54030                 }
54031                 if (!target) {
54032                         target = this.el.dom.lastChild || this.el.dom;
54033                 }
54034                 return target;
54035     },
54036
54037 /**
54038  *      Create the drag data which consists of an object which has the property "ddel" as
54039  *      the drag proxy element. 
54040  */
54041     getDragData : function(e) {
54042         var target = this.findItemFromChild(e.getTarget());
54043                 if(target) {
54044                         this.handleSelection(e);
54045                         var selNodes = this.getSelectedNodes();
54046             var dragData = {
54047                 source: this,
54048                 copy: this.copy || (this.allowCopy && e.ctrlKey),
54049                 nodes: selNodes,
54050                 records: []
54051                         };
54052                         var selectedIndices = this.getSelectedIndexes();
54053                         for (var i = 0; i < selectedIndices.length; i++) {
54054                                 dragData.records.push(this.store.getAt(selectedIndices[i]));
54055                         }
54056                         if (selNodes.length == 1) {
54057                                 dragData.ddel = target.cloneNode(true); // the div element
54058                         } else {
54059                                 var div = document.createElement('div'); // create the multi element drag "ghost"
54060                                 div.className = 'multi-proxy';
54061                                 for (var i = 0, len = selNodes.length; i < len; i++) {
54062                                         div.appendChild(selNodes[i].cloneNode(true));
54063                                 }
54064                                 dragData.ddel = div;
54065                         }
54066             //console.log(dragData)
54067             //console.log(dragData.ddel.innerHTML)
54068                         return dragData;
54069                 }
54070         //console.log('nodragData')
54071                 return false;
54072     },
54073     
54074 /**     Specify to which ddGroup items in this DDView may be dragged. */
54075     setDraggable: function(ddGroup) {
54076         if (ddGroup instanceof Array) {
54077                 Roo.each(ddGroup, this.setDraggable, this);
54078                 return;
54079         }
54080         if (this.dragZone) {
54081                 this.dragZone.addToGroup(ddGroup);
54082         } else {
54083                         this.dragZone = new Roo.dd.DragZone(this.getEl(), {
54084                                 containerScroll: true,
54085                                 ddGroup: ddGroup 
54086
54087                         });
54088 //                      Draggability implies selection. DragZone's mousedown selects the element.
54089                         if (!this.multiSelect) { this.singleSelect = true; }
54090
54091 //                      Wire the DragZone's handlers up to methods in *this*
54092                         this.dragZone.getDragData = this.getDragData.createDelegate(this);
54093                 }
54094     },
54095
54096 /**     Specify from which ddGroup this DDView accepts drops. */
54097     setDroppable: function(ddGroup) {
54098         if (ddGroup instanceof Array) {
54099                 Roo.each(ddGroup, this.setDroppable, this);
54100                 return;
54101         }
54102         if (this.dropZone) {
54103                 this.dropZone.addToGroup(ddGroup);
54104         } else {
54105                         this.dropZone = new Roo.dd.DropZone(this.getEl(), {
54106                                 containerScroll: true,
54107                                 ddGroup: ddGroup
54108                         });
54109
54110 //                      Wire the DropZone's handlers up to methods in *this*
54111                         this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
54112                         this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
54113                         this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
54114                         this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
54115                         this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
54116                 }
54117     },
54118
54119 /**     Decide whether to drop above or below a View node. */
54120     getDropPoint : function(e, n, dd){
54121         if (n == this.el.dom) { return "above"; }
54122                 var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
54123                 var c = t + (b - t) / 2;
54124                 var y = Roo.lib.Event.getPageY(e);
54125                 if(y <= c) {
54126                         return "above";
54127                 }else{
54128                         return "below";
54129                 }
54130     },
54131
54132     onNodeEnter : function(n, dd, e, data){
54133                 return false;
54134     },
54135     
54136     onNodeOver : function(n, dd, e, data){
54137                 var pt = this.getDropPoint(e, n, dd);
54138                 // set the insert point style on the target node
54139                 var dragElClass = this.dropNotAllowed;
54140                 if (pt) {
54141                         var targetElClass;
54142                         if (pt == "above"){
54143                                 dragElClass = n.previousSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-above";
54144                                 targetElClass = "x-view-drag-insert-above";
54145                         } else {
54146                                 dragElClass = n.nextSibling ? "x-tree-drop-ok-between" : "x-tree-drop-ok-below";
54147                                 targetElClass = "x-view-drag-insert-below";
54148                         }
54149                         if (this.lastInsertClass != targetElClass){
54150                                 Roo.fly(n).replaceClass(this.lastInsertClass, targetElClass);
54151                                 this.lastInsertClass = targetElClass;
54152                         }
54153                 }
54154                 return dragElClass;
54155         },
54156
54157     onNodeOut : function(n, dd, e, data){
54158                 this.removeDropIndicators(n);
54159     },
54160
54161     onNodeDrop : function(n, dd, e, data){
54162         if (this.fireEvent("drop", this, n, dd, e, data) === false) {
54163                 return false;
54164         }
54165         var pt = this.getDropPoint(e, n, dd);
54166                 var insertAt = (n == this.el.dom) ? this.nodes.length : n.nodeIndex;
54167                 if (pt == "below") { insertAt++; }
54168                 for (var i = 0; i < data.records.length; i++) {
54169                         var r = data.records[i];
54170                         var dup = this.store.getById(r.id);
54171                         if (dup && (dd != this.dragZone)) {
54172                                 Roo.fly(this.getNode(this.store.indexOf(dup))).frame("red", 1);
54173                         } else {
54174                                 if (data.copy) {
54175                                         this.store.insert(insertAt++, r.copy());
54176                                 } else {
54177                                         data.source.isDirtyFlag = true;
54178                                         r.store.remove(r);
54179                                         this.store.insert(insertAt++, r);
54180                                 }
54181                                 this.isDirtyFlag = true;
54182                         }
54183                 }
54184                 this.dragZone.cachedTarget = null;
54185                 return true;
54186     },
54187
54188     removeDropIndicators : function(n){
54189                 if(n){
54190                         Roo.fly(n).removeClass([
54191                                 "x-view-drag-insert-above",
54192                                 "x-view-drag-insert-below"]);
54193                         this.lastInsertClass = "_noclass";
54194                 }
54195     },
54196
54197 /**
54198  *      Utility method. Add a delete option to the DDView's context menu.
54199  *      @param {String} imageUrl The URL of the "delete" icon image.
54200  */
54201         setDeletable: function(imageUrl) {
54202                 if (!this.singleSelect && !this.multiSelect) {
54203                         this.singleSelect = true;
54204                 }
54205                 var c = this.getContextMenu();
54206                 this.contextMenu.on("itemclick", function(item) {
54207                         switch (item.id) {
54208                                 case "delete":
54209                                         this.remove(this.getSelectedIndexes());
54210                                         break;
54211                         }
54212                 }, this);
54213                 this.contextMenu.add({
54214                         icon: imageUrl,
54215                         id: "delete",
54216                         text: 'Delete'
54217                 });
54218         },
54219         
54220 /**     Return the context menu for this DDView. */
54221         getContextMenu: function() {
54222                 if (!this.contextMenu) {
54223 //                      Create the View's context menu
54224                         this.contextMenu = new Roo.menu.Menu({
54225                                 id: this.id + "-contextmenu"
54226                         });
54227                         this.el.on("contextmenu", this.showContextMenu, this);
54228                 }
54229                 return this.contextMenu;
54230         },
54231         
54232         disableContextMenu: function() {
54233                 if (this.contextMenu) {
54234                         this.el.un("contextmenu", this.showContextMenu, this);
54235                 }
54236         },
54237
54238         showContextMenu: function(e, item) {
54239         item = this.findItemFromChild(e.getTarget());
54240                 if (item) {
54241                         e.stopEvent();
54242                         this.select(this.getNode(item), this.multiSelect && e.ctrlKey, true);
54243                         this.contextMenu.showAt(e.getXY());
54244             }
54245     },
54246
54247 /**
54248  *      Remove {@link Roo.data.Record}s at the specified indices.
54249  *      @param {Array/Number} selectedIndices The index (or Array of indices) of Records to remove.
54250  */
54251     remove: function(selectedIndices) {
54252                 selectedIndices = [].concat(selectedIndices);
54253                 for (var i = 0; i < selectedIndices.length; i++) {
54254                         var rec = this.store.getAt(selectedIndices[i]);
54255                         this.store.remove(rec);
54256                 }
54257     },
54258
54259 /**
54260  *      Double click fires the event, but also, if this is draggable, and there is only one other
54261  *      related DropZone, it transfers the selected node.
54262  */
54263     onDblClick : function(e){
54264         var item = this.findItemFromChild(e.getTarget());
54265         if(item){
54266             if (this.fireEvent("dblclick", this, this.indexOf(item), item, e) === false) {
54267                 return false;
54268             }
54269             if (this.dragGroup) {
54270                     var targets = Roo.dd.DragDropMgr.getRelated(this.dragZone, true);
54271                     while (targets.indexOf(this.dropZone) > -1) {
54272                             targets.remove(this.dropZone);
54273                                 }
54274                     if (targets.length == 1) {
54275                                         this.dragZone.cachedTarget = null;
54276                         var el = Roo.get(targets[0].getEl());
54277                         var box = el.getBox(true);
54278                         targets[0].onNodeDrop(el.dom, {
54279                                 target: el.dom,
54280                                 xy: [box.x, box.y + box.height - 1]
54281                         }, null, this.getDragData(e));
54282                     }
54283                 }
54284         }
54285     },
54286     
54287     handleSelection: function(e) {
54288                 this.dragZone.cachedTarget = null;
54289         var item = this.findItemFromChild(e.getTarget());
54290         if (!item) {
54291                 this.clearSelections(true);
54292                 return;
54293         }
54294                 if (item && (this.multiSelect || this.singleSelect)){
54295                         if(this.multiSelect && e.shiftKey && (!e.ctrlKey) && this.lastSelection){
54296                                 this.select(this.getNodes(this.indexOf(this.lastSelection), item.nodeIndex), false);
54297                         }else if (this.isSelected(this.getNode(item)) && e.ctrlKey){
54298                                 this.unselect(item);
54299                         } else {
54300                                 this.select(item, this.multiSelect && e.ctrlKey);
54301                                 this.lastSelection = item;
54302                         }
54303                 }
54304     },
54305
54306     onItemClick : function(item, index, e){
54307                 if(this.fireEvent("beforeclick", this, index, item, e) === false){
54308                         return false;
54309                 }
54310                 return true;
54311     },
54312
54313     unselect : function(nodeInfo, suppressEvent){
54314                 var node = this.getNode(nodeInfo);
54315                 if(node && this.isSelected(node)){
54316                         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
54317                                 Roo.fly(node).removeClass(this.selectedClass);
54318                                 this.selections.remove(node);
54319                                 if(!suppressEvent){
54320                                         this.fireEvent("selectionchange", this, this.selections);
54321                                 }
54322                         }
54323                 }
54324     }
54325 });
54326 /*
54327  * Based on:
54328  * Ext JS Library 1.1.1
54329  * Copyright(c) 2006-2007, Ext JS, LLC.
54330  *
54331  * Originally Released Under LGPL - original licence link has changed is not relivant.
54332  *
54333  * Fork - LGPL
54334  * <script type="text/javascript">
54335  */
54336  
54337 /**
54338  * @class Roo.LayoutManager
54339  * @extends Roo.util.Observable
54340  * Base class for layout managers.
54341  */
54342 Roo.LayoutManager = function(container, config){
54343     Roo.LayoutManager.superclass.constructor.call(this);
54344     this.el = Roo.get(container);
54345     // ie scrollbar fix
54346     if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
54347         document.body.scroll = "no";
54348     }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
54349         this.el.position('relative');
54350     }
54351     this.id = this.el.id;
54352     this.el.addClass("x-layout-container");
54353     /** false to disable window resize monitoring @type Boolean */
54354     this.monitorWindowResize = true;
54355     this.regions = {};
54356     this.addEvents({
54357         /**
54358          * @event layout
54359          * Fires when a layout is performed. 
54360          * @param {Roo.LayoutManager} this
54361          */
54362         "layout" : true,
54363         /**
54364          * @event regionresized
54365          * Fires when the user resizes a region. 
54366          * @param {Roo.LayoutRegion} region The resized region
54367          * @param {Number} newSize The new size (width for east/west, height for north/south)
54368          */
54369         "regionresized" : true,
54370         /**
54371          * @event regioncollapsed
54372          * Fires when a region is collapsed. 
54373          * @param {Roo.LayoutRegion} region The collapsed region
54374          */
54375         "regioncollapsed" : true,
54376         /**
54377          * @event regionexpanded
54378          * Fires when a region is expanded.  
54379          * @param {Roo.LayoutRegion} region The expanded region
54380          */
54381         "regionexpanded" : true
54382     });
54383     this.updating = false;
54384     Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
54385 };
54386
54387 Roo.extend(Roo.LayoutManager, Roo.util.Observable, {
54388     /**
54389      * Returns true if this layout is currently being updated
54390      * @return {Boolean}
54391      */
54392     isUpdating : function(){
54393         return this.updating; 
54394     },
54395     
54396     /**
54397      * Suspend the LayoutManager from doing auto-layouts while
54398      * making multiple add or remove calls
54399      */
54400     beginUpdate : function(){
54401         this.updating = true;    
54402     },
54403     
54404     /**
54405      * Restore auto-layouts and optionally disable the manager from performing a layout
54406      * @param {Boolean} noLayout true to disable a layout update 
54407      */
54408     endUpdate : function(noLayout){
54409         this.updating = false;
54410         if(!noLayout){
54411             this.layout();
54412         }    
54413     },
54414     
54415     layout: function(){
54416         
54417     },
54418     
54419     onRegionResized : function(region, newSize){
54420         this.fireEvent("regionresized", region, newSize);
54421         this.layout();
54422     },
54423     
54424     onRegionCollapsed : function(region){
54425         this.fireEvent("regioncollapsed", region);
54426     },
54427     
54428     onRegionExpanded : function(region){
54429         this.fireEvent("regionexpanded", region);
54430     },
54431         
54432     /**
54433      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
54434      * performs box-model adjustments.
54435      * @return {Object} The size as an object {width: (the width), height: (the height)}
54436      */
54437     getViewSize : function(){
54438         var size;
54439         if(this.el.dom != document.body){
54440             size = this.el.getSize();
54441         }else{
54442             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
54443         }
54444         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
54445         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
54446         return size;
54447     },
54448     
54449     /**
54450      * Returns the Element this layout is bound to.
54451      * @return {Roo.Element}
54452      */
54453     getEl : function(){
54454         return this.el;
54455     },
54456     
54457     /**
54458      * Returns the specified region.
54459      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
54460      * @return {Roo.LayoutRegion}
54461      */
54462     getRegion : function(target){
54463         return this.regions[target.toLowerCase()];
54464     },
54465     
54466     onWindowResize : function(){
54467         if(this.monitorWindowResize){
54468             this.layout();
54469         }
54470     }
54471 });/*
54472  * Based on:
54473  * Ext JS Library 1.1.1
54474  * Copyright(c) 2006-2007, Ext JS, LLC.
54475  *
54476  * Originally Released Under LGPL - original licence link has changed is not relivant.
54477  *
54478  * Fork - LGPL
54479  * <script type="text/javascript">
54480  */
54481 /**
54482  * @class Roo.BorderLayout
54483  * @extends Roo.LayoutManager
54484  * @children Roo.ContentPanel
54485  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
54486  * please see: <br><br>
54487  * <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>
54488  * <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>
54489  * Example:
54490  <pre><code>
54491  var layout = new Roo.BorderLayout(document.body, {
54492     north: {
54493         initialSize: 25,
54494         titlebar: false
54495     },
54496     west: {
54497         split:true,
54498         initialSize: 200,
54499         minSize: 175,
54500         maxSize: 400,
54501         titlebar: true,
54502         collapsible: true
54503     },
54504     east: {
54505         split:true,
54506         initialSize: 202,
54507         minSize: 175,
54508         maxSize: 400,
54509         titlebar: true,
54510         collapsible: true
54511     },
54512     south: {
54513         split:true,
54514         initialSize: 100,
54515         minSize: 100,
54516         maxSize: 200,
54517         titlebar: true,
54518         collapsible: true
54519     },
54520     center: {
54521         titlebar: true,
54522         autoScroll:true,
54523         resizeTabs: true,
54524         minTabWidth: 50,
54525         preferredTabWidth: 150
54526     }
54527 });
54528
54529 // shorthand
54530 var CP = Roo.ContentPanel;
54531
54532 layout.beginUpdate();
54533 layout.add("north", new CP("north", "North"));
54534 layout.add("south", new CP("south", {title: "South", closable: true}));
54535 layout.add("west", new CP("west", {title: "West"}));
54536 layout.add("east", new CP("autoTabs", {title: "Auto Tabs", closable: true}));
54537 layout.add("center", new CP("center1", {title: "Close Me", closable: true}));
54538 layout.add("center", new CP("center2", {title: "Center Panel", closable: false}));
54539 layout.getRegion("center").showPanel("center1");
54540 layout.endUpdate();
54541 </code></pre>
54542
54543 <b>The container the layout is rendered into can be either the body element or any other element.
54544 If it is not the body element, the container needs to either be an absolute positioned element,
54545 or you will need to add "position:relative" to the css of the container.  You will also need to specify
54546 the container size if it is not the body element.</b>
54547
54548 * @constructor
54549 * Create a new BorderLayout
54550 * @param {String/HTMLElement/Element} container The container this layout is bound to
54551 * @param {Object} config Configuration options
54552  */
54553 Roo.BorderLayout = function(container, config){
54554     config = config || {};
54555     Roo.BorderLayout.superclass.constructor.call(this, container, config);
54556     this.factory = config.factory || Roo.BorderLayout.RegionFactory;
54557     for(var i = 0, len = this.factory.validRegions.length; i < len; i++) {
54558         var target = this.factory.validRegions[i];
54559         if(config[target]){
54560             this.addRegion(target, config[target]);
54561         }
54562     }
54563 };
54564
54565 Roo.extend(Roo.BorderLayout, Roo.LayoutManager, {
54566         
54567         /**
54568          * @cfg {Roo.LayoutRegion} east
54569          */
54570         /**
54571          * @cfg {Roo.LayoutRegion} west
54572          */
54573         /**
54574          * @cfg {Roo.LayoutRegion} north
54575          */
54576         /**
54577          * @cfg {Roo.LayoutRegion} south
54578          */
54579         /**
54580          * @cfg {Roo.LayoutRegion} center
54581          */
54582     /**
54583      * Creates and adds a new region if it doesn't already exist.
54584      * @param {String} target The target region key (north, south, east, west or center).
54585      * @param {Object} config The regions config object
54586      * @return {BorderLayoutRegion} The new region
54587      */
54588     addRegion : function(target, config){
54589         if(!this.regions[target]){
54590             var r = this.factory.create(target, this, config);
54591             this.bindRegion(target, r);
54592         }
54593         return this.regions[target];
54594     },
54595
54596     // private (kinda)
54597     bindRegion : function(name, r){
54598         this.regions[name] = r;
54599         r.on("visibilitychange", this.layout, this);
54600         r.on("paneladded", this.layout, this);
54601         r.on("panelremoved", this.layout, this);
54602         r.on("invalidated", this.layout, this);
54603         r.on("resized", this.onRegionResized, this);
54604         r.on("collapsed", this.onRegionCollapsed, this);
54605         r.on("expanded", this.onRegionExpanded, this);
54606     },
54607
54608     /**
54609      * Performs a layout update.
54610      */
54611     layout : function(){
54612         if(this.updating) {
54613             return;
54614         }
54615         var size = this.getViewSize();
54616         var w = size.width;
54617         var h = size.height;
54618         var centerW = w;
54619         var centerH = h;
54620         var centerY = 0;
54621         var centerX = 0;
54622         //var x = 0, y = 0;
54623
54624         var rs = this.regions;
54625         var north = rs["north"];
54626         var south = rs["south"]; 
54627         var west = rs["west"];
54628         var east = rs["east"];
54629         var center = rs["center"];
54630         //if(this.hideOnLayout){ // not supported anymore
54631             //c.el.setStyle("display", "none");
54632         //}
54633         if(north && north.isVisible()){
54634             var b = north.getBox();
54635             var m = north.getMargins();
54636             b.width = w - (m.left+m.right);
54637             b.x = m.left;
54638             b.y = m.top;
54639             centerY = b.height + b.y + m.bottom;
54640             centerH -= centerY;
54641             north.updateBox(this.safeBox(b));
54642         }
54643         if(south && south.isVisible()){
54644             var b = south.getBox();
54645             var m = south.getMargins();
54646             b.width = w - (m.left+m.right);
54647             b.x = m.left;
54648             var totalHeight = (b.height + m.top + m.bottom);
54649             b.y = h - totalHeight + m.top;
54650             centerH -= totalHeight;
54651             south.updateBox(this.safeBox(b));
54652         }
54653         if(west && west.isVisible()){
54654             var b = west.getBox();
54655             var m = west.getMargins();
54656             b.height = centerH - (m.top+m.bottom);
54657             b.x = m.left;
54658             b.y = centerY + m.top;
54659             var totalWidth = (b.width + m.left + m.right);
54660             centerX += totalWidth;
54661             centerW -= totalWidth;
54662             west.updateBox(this.safeBox(b));
54663         }
54664         if(east && east.isVisible()){
54665             var b = east.getBox();
54666             var m = east.getMargins();
54667             b.height = centerH - (m.top+m.bottom);
54668             var totalWidth = (b.width + m.left + m.right);
54669             b.x = w - totalWidth + m.left;
54670             b.y = centerY + m.top;
54671             centerW -= totalWidth;
54672             east.updateBox(this.safeBox(b));
54673         }
54674         if(center){
54675             var m = center.getMargins();
54676             var centerBox = {
54677                 x: centerX + m.left,
54678                 y: centerY + m.top,
54679                 width: centerW - (m.left+m.right),
54680                 height: centerH - (m.top+m.bottom)
54681             };
54682             //if(this.hideOnLayout){
54683                 //center.el.setStyle("display", "block");
54684             //}
54685             center.updateBox(this.safeBox(centerBox));
54686         }
54687         this.el.repaint();
54688         this.fireEvent("layout", this);
54689     },
54690
54691     // private
54692     safeBox : function(box){
54693         box.width = Math.max(0, box.width);
54694         box.height = Math.max(0, box.height);
54695         return box;
54696     },
54697
54698     /**
54699      * Adds a ContentPanel (or subclass) to this layout.
54700      * @param {String} target The target region key (north, south, east, west or center).
54701      * @param {Roo.ContentPanel} panel The panel to add
54702      * @return {Roo.ContentPanel} The added panel
54703      */
54704     add : function(target, panel){
54705          
54706         target = target.toLowerCase();
54707         return this.regions[target].add(panel);
54708     },
54709
54710     /**
54711      * Remove a ContentPanel (or subclass) to this layout.
54712      * @param {String} target The target region key (north, south, east, west or center).
54713      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
54714      * @return {Roo.ContentPanel} The removed panel
54715      */
54716     remove : function(target, panel){
54717         target = target.toLowerCase();
54718         return this.regions[target].remove(panel);
54719     },
54720
54721     /**
54722      * Searches all regions for a panel with the specified id
54723      * @param {String} panelId
54724      * @return {Roo.ContentPanel} The panel or null if it wasn't found
54725      */
54726     findPanel : function(panelId){
54727         var rs = this.regions;
54728         for(var target in rs){
54729             if(typeof rs[target] != "function"){
54730                 var p = rs[target].getPanel(panelId);
54731                 if(p){
54732                     return p;
54733                 }
54734             }
54735         }
54736         return null;
54737     },
54738
54739     /**
54740      * Searches all regions for a panel with the specified id and activates (shows) it.
54741      * @param {String/ContentPanel} panelId The panels id or the panel itself
54742      * @return {Roo.ContentPanel} The shown panel or null
54743      */
54744     showPanel : function(panelId) {
54745       var rs = this.regions;
54746       for(var target in rs){
54747          var r = rs[target];
54748          if(typeof r != "function"){
54749             if(r.hasPanel(panelId)){
54750                return r.showPanel(panelId);
54751             }
54752          }
54753       }
54754       return null;
54755    },
54756
54757    /**
54758      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
54759      * @param {Roo.state.Provider} provider (optional) An alternate state provider
54760      */
54761     restoreState : function(provider){
54762         if(!provider){
54763             provider = Roo.state.Manager;
54764         }
54765         var sm = new Roo.LayoutStateManager();
54766         sm.init(this, provider);
54767     },
54768
54769     /**
54770      * Adds a batch of multiple ContentPanels dynamically by passing a special regions config object.  This config
54771      * object should contain properties for each region to add ContentPanels to, and each property's value should be
54772      * a valid ContentPanel config object.  Example:
54773      * <pre><code>
54774 // Create the main layout
54775 var layout = new Roo.BorderLayout('main-ct', {
54776     west: {
54777         split:true,
54778         minSize: 175,
54779         titlebar: true
54780     },
54781     center: {
54782         title:'Components'
54783     }
54784 }, 'main-ct');
54785
54786 // Create and add multiple ContentPanels at once via configs
54787 layout.batchAdd({
54788    west: {
54789        id: 'source-files',
54790        autoCreate:true,
54791        title:'Ext Source Files',
54792        autoScroll:true,
54793        fitToFrame:true
54794    },
54795    center : {
54796        el: cview,
54797        autoScroll:true,
54798        fitToFrame:true,
54799        toolbar: tb,
54800        resizeEl:'cbody'
54801    }
54802 });
54803 </code></pre>
54804      * @param {Object} regions An object containing ContentPanel configs by region name
54805      */
54806     batchAdd : function(regions){
54807         this.beginUpdate();
54808         for(var rname in regions){
54809             var lr = this.regions[rname];
54810             if(lr){
54811                 this.addTypedPanels(lr, regions[rname]);
54812             }
54813         }
54814         this.endUpdate();
54815     },
54816
54817     // private
54818     addTypedPanels : function(lr, ps){
54819         if(typeof ps == 'string'){
54820             lr.add(new Roo.ContentPanel(ps));
54821         }
54822         else if(ps instanceof Array){
54823             for(var i =0, len = ps.length; i < len; i++){
54824                 this.addTypedPanels(lr, ps[i]);
54825             }
54826         }
54827         else if(!ps.events){ // raw config?
54828             var el = ps.el;
54829             delete ps.el; // prevent conflict
54830             lr.add(new Roo.ContentPanel(el || Roo.id(), ps));
54831         }
54832         else {  // panel object assumed!
54833             lr.add(ps);
54834         }
54835     },
54836     /**
54837      * Adds a xtype elements to the layout.
54838      * <pre><code>
54839
54840 layout.addxtype({
54841        xtype : 'ContentPanel',
54842        region: 'west',
54843        items: [ .... ]
54844    }
54845 );
54846
54847 layout.addxtype({
54848         xtype : 'NestedLayoutPanel',
54849         region: 'west',
54850         layout: {
54851            center: { },
54852            west: { }   
54853         },
54854         items : [ ... list of content panels or nested layout panels.. ]
54855    }
54856 );
54857 </code></pre>
54858      * @param {Object} cfg Xtype definition of item to add.
54859      */
54860     addxtype : function(cfg)
54861     {
54862         // basically accepts a pannel...
54863         // can accept a layout region..!?!?
54864         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
54865         
54866         if (!cfg.xtype.match(/Panel$/)) {
54867             return false;
54868         }
54869         var ret = false;
54870         
54871         if (typeof(cfg.region) == 'undefined') {
54872             Roo.log("Failed to add Panel, region was not set");
54873             Roo.log(cfg);
54874             return false;
54875         }
54876         var region = cfg.region;
54877         delete cfg.region;
54878         
54879           
54880         var xitems = [];
54881         if (cfg.items) {
54882             xitems = cfg.items;
54883             delete cfg.items;
54884         }
54885         var nb = false;
54886         
54887         switch(cfg.xtype) 
54888         {
54889             case 'ContentPanel':  // ContentPanel (el, cfg)
54890             case 'ScrollPanel':  // ContentPanel (el, cfg)
54891             case 'ViewPanel': 
54892                 if(cfg.autoCreate) {
54893                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54894                 } else {
54895                     var el = this.el.createChild();
54896                     ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
54897                 }
54898                 
54899                 this.add(region, ret);
54900                 break;
54901             
54902             
54903             case 'TreePanel': // our new panel!
54904                 cfg.el = this.el.createChild();
54905                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54906                 this.add(region, ret);
54907                 break;
54908             
54909             case 'NestedLayoutPanel': 
54910                 // create a new Layout (which is  a Border Layout...
54911                 var el = this.el.createChild();
54912                 var clayout = cfg.layout;
54913                 delete cfg.layout;
54914                 clayout.items   = clayout.items  || [];
54915                 // replace this exitems with the clayout ones..
54916                 xitems = clayout.items;
54917                  
54918                 
54919                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
54920                     cfg.background = false;
54921                 }
54922                 var layout = new Roo.BorderLayout(el, clayout);
54923                 
54924                 ret = new Roo[cfg.xtype](layout, cfg); // new panel!!!!!
54925                 //console.log('adding nested layout panel '  + cfg.toSource());
54926                 this.add(region, ret);
54927                 nb = {}; /// find first...
54928                 break;
54929                 
54930             case 'GridPanel': 
54931             
54932                 // needs grid and region
54933                 
54934                 //var el = this.getRegion(region).el.createChild();
54935                 var el = this.el.createChild();
54936                 // create the grid first...
54937                 
54938                 var grid = new Roo.grid[cfg.grid.xtype](el, cfg.grid);
54939                 delete cfg.grid;
54940                 if (region == 'center' && this.active ) {
54941                     cfg.background = false;
54942                 }
54943                 ret = new Roo[cfg.xtype](grid, cfg); // new panel!!!!!
54944                 
54945                 this.add(region, ret);
54946                 if (cfg.background) {
54947                     ret.on('activate', function(gp) {
54948                         if (!gp.grid.rendered) {
54949                             gp.grid.render();
54950                         }
54951                     });
54952                 } else {
54953                     grid.render();
54954                 }
54955                 break;
54956            
54957            
54958            
54959                 
54960                 
54961                 
54962             default:
54963                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
54964                     
54965                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
54966                     this.add(region, ret);
54967                 } else {
54968                 
54969                     alert("Can not add '" + cfg.xtype + "' to BorderLayout");
54970                     return null;
54971                 }
54972                 
54973              // GridPanel (grid, cfg)
54974             
54975         }
54976         this.beginUpdate();
54977         // add children..
54978         var region = '';
54979         var abn = {};
54980         Roo.each(xitems, function(i)  {
54981             region = nb && i.region ? i.region : false;
54982             
54983             var add = ret.addxtype(i);
54984            
54985             if (region) {
54986                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
54987                 if (!i.background) {
54988                     abn[region] = nb[region] ;
54989                 }
54990             }
54991             
54992         });
54993         this.endUpdate();
54994
54995         // make the last non-background panel active..
54996         //if (nb) { Roo.log(abn); }
54997         if (nb) {
54998             
54999             for(var r in abn) {
55000                 region = this.getRegion(r);
55001                 if (region) {
55002                     // tried using nb[r], but it does not work..
55003                      
55004                     region.showPanel(abn[r]);
55005                    
55006                 }
55007             }
55008         }
55009         return ret;
55010         
55011     }
55012 });
55013
55014 /**
55015  * Shortcut for creating a new BorderLayout object and adding one or more ContentPanels to it in a single step, handling
55016  * the beginUpdate and endUpdate calls internally.  The key to this method is the <b>panels</b> property that can be
55017  * provided with each region config, which allows you to add ContentPanel configs in addition to the region configs
55018  * during creation.  The following code is equivalent to the constructor-based example at the beginning of this class:
55019  * <pre><code>
55020 // shorthand
55021 var CP = Roo.ContentPanel;
55022
55023 var layout = Roo.BorderLayout.create({
55024     north: {
55025         initialSize: 25,
55026         titlebar: false,
55027         panels: [new CP("north", "North")]
55028     },
55029     west: {
55030         split:true,
55031         initialSize: 200,
55032         minSize: 175,
55033         maxSize: 400,
55034         titlebar: true,
55035         collapsible: true,
55036         panels: [new CP("west", {title: "West"})]
55037     },
55038     east: {
55039         split:true,
55040         initialSize: 202,
55041         minSize: 175,
55042         maxSize: 400,
55043         titlebar: true,
55044         collapsible: true,
55045         panels: [new CP("autoTabs", {title: "Auto Tabs", closable: true})]
55046     },
55047     south: {
55048         split:true,
55049         initialSize: 100,
55050         minSize: 100,
55051         maxSize: 200,
55052         titlebar: true,
55053         collapsible: true,
55054         panels: [new CP("south", {title: "South", closable: true})]
55055     },
55056     center: {
55057         titlebar: true,
55058         autoScroll:true,
55059         resizeTabs: true,
55060         minTabWidth: 50,
55061         preferredTabWidth: 150,
55062         panels: [
55063             new CP("center1", {title: "Close Me", closable: true}),
55064             new CP("center2", {title: "Center Panel", closable: false})
55065         ]
55066     }
55067 }, document.body);
55068
55069 layout.getRegion("center").showPanel("center1");
55070 </code></pre>
55071  * @param config
55072  * @param targetEl
55073  */
55074 Roo.BorderLayout.create = function(config, targetEl){
55075     var layout = new Roo.BorderLayout(targetEl || document.body, config);
55076     layout.beginUpdate();
55077     var regions = Roo.BorderLayout.RegionFactory.validRegions;
55078     for(var j = 0, jlen = regions.length; j < jlen; j++){
55079         var lr = regions[j];
55080         if(layout.regions[lr] && config[lr].panels){
55081             var r = layout.regions[lr];
55082             var ps = config[lr].panels;
55083             layout.addTypedPanels(r, ps);
55084         }
55085     }
55086     layout.endUpdate();
55087     return layout;
55088 };
55089
55090 // private
55091 Roo.BorderLayout.RegionFactory = {
55092     // private
55093     validRegions : ["north","south","east","west","center"],
55094
55095     // private
55096     create : function(target, mgr, config){
55097         target = target.toLowerCase();
55098         if(config.lightweight || config.basic){
55099             return new Roo.BasicLayoutRegion(mgr, config, target);
55100         }
55101         switch(target){
55102             case "north":
55103                 return new Roo.NorthLayoutRegion(mgr, config);
55104             case "south":
55105                 return new Roo.SouthLayoutRegion(mgr, config);
55106             case "east":
55107                 return new Roo.EastLayoutRegion(mgr, config);
55108             case "west":
55109                 return new Roo.WestLayoutRegion(mgr, config);
55110             case "center":
55111                 return new Roo.CenterLayoutRegion(mgr, config);
55112         }
55113         throw 'Layout region "'+target+'" not supported.';
55114     }
55115 };/*
55116  * Based on:
55117  * Ext JS Library 1.1.1
55118  * Copyright(c) 2006-2007, Ext JS, LLC.
55119  *
55120  * Originally Released Under LGPL - original licence link has changed is not relivant.
55121  *
55122  * Fork - LGPL
55123  * <script type="text/javascript">
55124  */
55125  
55126 /**
55127  * @class Roo.BasicLayoutRegion
55128  * @extends Roo.util.Observable
55129  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
55130  * and does not have a titlebar, tabs or any other features. All it does is size and position 
55131  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
55132  */
55133 Roo.BasicLayoutRegion = function(mgr, config, pos, skipConfig){
55134     this.mgr = mgr;
55135     this.position  = pos;
55136     this.events = {
55137         /**
55138          * @scope Roo.BasicLayoutRegion
55139          */
55140         
55141         /**
55142          * @event beforeremove
55143          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
55144          * @param {Roo.LayoutRegion} this
55145          * @param {Roo.ContentPanel} panel The panel
55146          * @param {Object} e The cancel event object
55147          */
55148         "beforeremove" : true,
55149         /**
55150          * @event invalidated
55151          * Fires when the layout for this region is changed.
55152          * @param {Roo.LayoutRegion} this
55153          */
55154         "invalidated" : true,
55155         /**
55156          * @event visibilitychange
55157          * Fires when this region is shown or hidden 
55158          * @param {Roo.LayoutRegion} this
55159          * @param {Boolean} visibility true or false
55160          */
55161         "visibilitychange" : true,
55162         /**
55163          * @event paneladded
55164          * Fires when a panel is added. 
55165          * @param {Roo.LayoutRegion} this
55166          * @param {Roo.ContentPanel} panel The panel
55167          */
55168         "paneladded" : true,
55169         /**
55170          * @event panelremoved
55171          * Fires when a panel is removed. 
55172          * @param {Roo.LayoutRegion} this
55173          * @param {Roo.ContentPanel} panel The panel
55174          */
55175         "panelremoved" : true,
55176         /**
55177          * @event beforecollapse
55178          * Fires when this region before collapse.
55179          * @param {Roo.LayoutRegion} this
55180          */
55181         "beforecollapse" : true,
55182         /**
55183          * @event collapsed
55184          * Fires when this region is collapsed.
55185          * @param {Roo.LayoutRegion} this
55186          */
55187         "collapsed" : true,
55188         /**
55189          * @event expanded
55190          * Fires when this region is expanded.
55191          * @param {Roo.LayoutRegion} this
55192          */
55193         "expanded" : true,
55194         /**
55195          * @event slideshow
55196          * Fires when this region is slid into view.
55197          * @param {Roo.LayoutRegion} this
55198          */
55199         "slideshow" : true,
55200         /**
55201          * @event slidehide
55202          * Fires when this region slides out of view. 
55203          * @param {Roo.LayoutRegion} this
55204          */
55205         "slidehide" : true,
55206         /**
55207          * @event panelactivated
55208          * Fires when a panel is activated. 
55209          * @param {Roo.LayoutRegion} this
55210          * @param {Roo.ContentPanel} panel The activated panel
55211          */
55212         "panelactivated" : true,
55213         /**
55214          * @event resized
55215          * Fires when the user resizes this region. 
55216          * @param {Roo.LayoutRegion} this
55217          * @param {Number} newSize The new size (width for east/west, height for north/south)
55218          */
55219         "resized" : true
55220     };
55221     /** A collection of panels in this region. @type Roo.util.MixedCollection */
55222     this.panels = new Roo.util.MixedCollection();
55223     this.panels.getKey = this.getPanelId.createDelegate(this);
55224     this.box = null;
55225     this.activePanel = null;
55226     // ensure listeners are added...
55227     
55228     if (config.listeners || config.events) {
55229         Roo.BasicLayoutRegion.superclass.constructor.call(this, {
55230             listeners : config.listeners || {},
55231             events : config.events || {}
55232         });
55233     }
55234     
55235     if(skipConfig !== true){
55236         this.applyConfig(config);
55237     }
55238 };
55239
55240 Roo.extend(Roo.BasicLayoutRegion, Roo.util.Observable, {
55241     getPanelId : function(p){
55242         return p.getId();
55243     },
55244     
55245     applyConfig : function(config){
55246         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55247         this.config = config;
55248         
55249     },
55250     
55251     /**
55252      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
55253      * the width, for horizontal (north, south) the height.
55254      * @param {Number} newSize The new width or height
55255      */
55256     resizeTo : function(newSize){
55257         var el = this.el ? this.el :
55258                  (this.activePanel ? this.activePanel.getEl() : null);
55259         if(el){
55260             switch(this.position){
55261                 case "east":
55262                 case "west":
55263                     el.setWidth(newSize);
55264                     this.fireEvent("resized", this, newSize);
55265                 break;
55266                 case "north":
55267                 case "south":
55268                     el.setHeight(newSize);
55269                     this.fireEvent("resized", this, newSize);
55270                 break;                
55271             }
55272         }
55273     },
55274     
55275     getBox : function(){
55276         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
55277     },
55278     
55279     getMargins : function(){
55280         return this.margins;
55281     },
55282     
55283     updateBox : function(box){
55284         this.box = box;
55285         var el = this.activePanel.getEl();
55286         el.dom.style.left = box.x + "px";
55287         el.dom.style.top = box.y + "px";
55288         this.activePanel.setSize(box.width, box.height);
55289     },
55290     
55291     /**
55292      * Returns the container element for this region.
55293      * @return {Roo.Element}
55294      */
55295     getEl : function(){
55296         return this.activePanel;
55297     },
55298     
55299     /**
55300      * Returns true if this region is currently visible.
55301      * @return {Boolean}
55302      */
55303     isVisible : function(){
55304         return this.activePanel ? true : false;
55305     },
55306     
55307     setActivePanel : function(panel){
55308         panel = this.getPanel(panel);
55309         if(this.activePanel && this.activePanel != panel){
55310             this.activePanel.setActiveState(false);
55311             this.activePanel.getEl().setLeftTop(-10000,-10000);
55312         }
55313         this.activePanel = panel;
55314         panel.setActiveState(true);
55315         if(this.box){
55316             panel.setSize(this.box.width, this.box.height);
55317         }
55318         this.fireEvent("panelactivated", this, panel);
55319         this.fireEvent("invalidated");
55320     },
55321     
55322     /**
55323      * Show the specified panel.
55324      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
55325      * @return {Roo.ContentPanel} The shown panel or null
55326      */
55327     showPanel : function(panel){
55328         if(panel = this.getPanel(panel)){
55329             this.setActivePanel(panel);
55330         }
55331         return panel;
55332     },
55333     
55334     /**
55335      * Get the active panel for this region.
55336      * @return {Roo.ContentPanel} The active panel or null
55337      */
55338     getActivePanel : function(){
55339         return this.activePanel;
55340     },
55341     
55342     /**
55343      * Add the passed ContentPanel(s)
55344      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55345      * @return {Roo.ContentPanel} The panel added (if only one was added)
55346      */
55347     add : function(panel){
55348         if(arguments.length > 1){
55349             for(var i = 0, len = arguments.length; i < len; i++) {
55350                 this.add(arguments[i]);
55351             }
55352             return null;
55353         }
55354         if(this.hasPanel(panel)){
55355             this.showPanel(panel);
55356             return panel;
55357         }
55358         var el = panel.getEl();
55359         if(el.dom.parentNode != this.mgr.el.dom){
55360             this.mgr.el.dom.appendChild(el.dom);
55361         }
55362         if(panel.setRegion){
55363             panel.setRegion(this);
55364         }
55365         this.panels.add(panel);
55366         el.setStyle("position", "absolute");
55367         if(!panel.background){
55368             this.setActivePanel(panel);
55369             if(this.config.initialSize && this.panels.getCount()==1){
55370                 this.resizeTo(this.config.initialSize);
55371             }
55372         }
55373         this.fireEvent("paneladded", this, panel);
55374         return panel;
55375     },
55376     
55377     /**
55378      * Returns true if the panel is in this region.
55379      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55380      * @return {Boolean}
55381      */
55382     hasPanel : function(panel){
55383         if(typeof panel == "object"){ // must be panel obj
55384             panel = panel.getId();
55385         }
55386         return this.getPanel(panel) ? true : false;
55387     },
55388     
55389     /**
55390      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55391      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55392      * @param {Boolean} preservePanel Overrides the config preservePanel option
55393      * @return {Roo.ContentPanel} The panel that was removed
55394      */
55395     remove : function(panel, preservePanel){
55396         panel = this.getPanel(panel);
55397         if(!panel){
55398             return null;
55399         }
55400         var e = {};
55401         this.fireEvent("beforeremove", this, panel, e);
55402         if(e.cancel === true){
55403             return null;
55404         }
55405         var panelId = panel.getId();
55406         this.panels.removeKey(panelId);
55407         return panel;
55408     },
55409     
55410     /**
55411      * Returns the panel specified or null if it's not in this region.
55412      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
55413      * @return {Roo.ContentPanel}
55414      */
55415     getPanel : function(id){
55416         if(typeof id == "object"){ // must be panel obj
55417             return id;
55418         }
55419         return this.panels.get(id);
55420     },
55421     
55422     /**
55423      * Returns this regions position (north/south/east/west/center).
55424      * @return {String} 
55425      */
55426     getPosition: function(){
55427         return this.position;    
55428     }
55429 });/*
55430  * Based on:
55431  * Ext JS Library 1.1.1
55432  * Copyright(c) 2006-2007, Ext JS, LLC.
55433  *
55434  * Originally Released Under LGPL - original licence link has changed is not relivant.
55435  *
55436  * Fork - LGPL
55437  * <script type="text/javascript">
55438  */
55439  
55440 /**
55441  * @class Roo.LayoutRegion
55442  * @extends Roo.BasicLayoutRegion
55443  * This class represents a region in a layout manager.
55444  * @cfg {Boolean}   collapsible     False to disable collapsing (defaults to true)
55445  * @cfg {Boolean}   collapsed       True to set the initial display to collapsed (defaults to false)
55446  * @cfg {Boolean}   floatable       False to disable floating (defaults to true)
55447  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
55448  * @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})
55449  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
55450  * @cfg {String}    collapsedTitle  Optional string message to display in the collapsed block of a north or south region
55451  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
55452  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
55453  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
55454  * @cfg {String}    title           The title for the region (overrides panel titles)
55455  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
55456  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
55457  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
55458  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
55459  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
55460  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
55461  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
55462  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
55463  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
55464  * @cfg {Boolean}   showPin         True to show a pin button
55465  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
55466  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
55467  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
55468  * @cfg {Number}    width           For East/West panels
55469  * @cfg {Number}    height          For North/South panels
55470  * @cfg {Boolean}   split           To show the splitter
55471  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
55472  */
55473 Roo.LayoutRegion = function(mgr, config, pos){
55474     Roo.LayoutRegion.superclass.constructor.call(this, mgr, config, pos, true);
55475     var dh = Roo.DomHelper;
55476     /** This region's container element 
55477     * @type Roo.Element */
55478     this.el = dh.append(mgr.el.dom, {tag: "div", cls: "x-layout-panel x-layout-panel-" + this.position}, true);
55479     /** This region's title element 
55480     * @type Roo.Element */
55481
55482     this.titleEl = dh.append(this.el.dom, {tag: "div", unselectable: "on", cls: "x-unselectable x-layout-panel-hd x-layout-title-"+this.position, children:[
55483         {tag: "span", cls: "x-unselectable x-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
55484         {tag: "div", cls: "x-unselectable x-layout-panel-hd-tools", unselectable: "on"}
55485     ]}, true);
55486     this.titleEl.enableDisplayMode();
55487     /** This region's title text element 
55488     * @type HTMLElement */
55489     this.titleTextEl = this.titleEl.dom.firstChild;
55490     this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
55491     this.closeBtn = this.createTool(this.tools.dom, "x-layout-close");
55492     this.closeBtn.enableDisplayMode();
55493     this.closeBtn.on("click", this.closeClicked, this);
55494     this.closeBtn.hide();
55495
55496     this.createBody(config);
55497     this.visible = true;
55498     this.collapsed = false;
55499
55500     if(config.hideWhenEmpty){
55501         this.hide();
55502         this.on("paneladded", this.validateVisibility, this);
55503         this.on("panelremoved", this.validateVisibility, this);
55504     }
55505     this.applyConfig(config);
55506 };
55507
55508 Roo.extend(Roo.LayoutRegion, Roo.BasicLayoutRegion, {
55509
55510     createBody : function(){
55511         /** This region's body element 
55512         * @type Roo.Element */
55513         this.bodyEl = this.el.createChild({tag: "div", cls: "x-layout-panel-body"});
55514     },
55515
55516     applyConfig : function(c){
55517         if(c.collapsible && this.position != "center" && !this.collapsedEl){
55518             var dh = Roo.DomHelper;
55519             if(c.titlebar !== false){
55520                 this.collapseBtn = this.createTool(this.tools.dom, "x-layout-collapse-"+this.position);
55521                 this.collapseBtn.on("click", this.collapse, this);
55522                 this.collapseBtn.enableDisplayMode();
55523
55524                 if(c.showPin === true || this.showPin){
55525                     this.stickBtn = this.createTool(this.tools.dom, "x-layout-stick");
55526                     this.stickBtn.enableDisplayMode();
55527                     this.stickBtn.on("click", this.expand, this);
55528                     this.stickBtn.hide();
55529                 }
55530             }
55531             /** This region's collapsed element
55532             * @type Roo.Element */
55533             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
55534                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
55535             ]}, true);
55536             if(c.floatable !== false){
55537                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
55538                this.collapsedEl.on("click", this.collapseClick, this);
55539             }
55540
55541             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
55542                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
55543                    id: "message", unselectable: "on", style:{"float":"left"}});
55544                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
55545              }
55546             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
55547             this.expandBtn.on("click", this.expand, this);
55548         }
55549         if(this.collapseBtn){
55550             this.collapseBtn.setVisible(c.collapsible == true);
55551         }
55552         this.cmargins = c.cmargins || this.cmargins ||
55553                          (this.position == "west" || this.position == "east" ?
55554                              {top: 0, left: 2, right:2, bottom: 0} :
55555                              {top: 2, left: 0, right:0, bottom: 2});
55556         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
55557         this.bottomTabs = c.tabPosition != "top";
55558         this.autoScroll = c.autoScroll || false;
55559         if(this.autoScroll){
55560             this.bodyEl.setStyle("overflow", "auto");
55561         }else{
55562             this.bodyEl.setStyle("overflow", "hidden");
55563         }
55564         //if(c.titlebar !== false){
55565             if((!c.titlebar && !c.title) || c.titlebar === false){
55566                 this.titleEl.hide();
55567             }else{
55568                 this.titleEl.show();
55569                 if(c.title){
55570                     this.titleTextEl.innerHTML = c.title;
55571                 }
55572             }
55573         //}
55574         this.duration = c.duration || .30;
55575         this.slideDuration = c.slideDuration || .45;
55576         this.config = c;
55577         if(c.collapsed){
55578             this.collapse(true);
55579         }
55580         if(c.hidden){
55581             this.hide();
55582         }
55583     },
55584     /**
55585      * Returns true if this region is currently visible.
55586      * @return {Boolean}
55587      */
55588     isVisible : function(){
55589         return this.visible;
55590     },
55591
55592     /**
55593      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
55594      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
55595      */
55596     setCollapsedTitle : function(title){
55597         title = title || "&#160;";
55598         if(this.collapsedTitleTextEl){
55599             this.collapsedTitleTextEl.innerHTML = title;
55600         }
55601     },
55602
55603     getBox : function(){
55604         var b;
55605         if(!this.collapsed){
55606             b = this.el.getBox(false, true);
55607         }else{
55608             b = this.collapsedEl.getBox(false, true);
55609         }
55610         return b;
55611     },
55612
55613     getMargins : function(){
55614         return this.collapsed ? this.cmargins : this.margins;
55615     },
55616
55617     highlight : function(){
55618         this.el.addClass("x-layout-panel-dragover");
55619     },
55620
55621     unhighlight : function(){
55622         this.el.removeClass("x-layout-panel-dragover");
55623     },
55624
55625     updateBox : function(box){
55626         this.box = box;
55627         if(!this.collapsed){
55628             this.el.dom.style.left = box.x + "px";
55629             this.el.dom.style.top = box.y + "px";
55630             this.updateBody(box.width, box.height);
55631         }else{
55632             this.collapsedEl.dom.style.left = box.x + "px";
55633             this.collapsedEl.dom.style.top = box.y + "px";
55634             this.collapsedEl.setSize(box.width, box.height);
55635         }
55636         if(this.tabs){
55637             this.tabs.autoSizeTabs();
55638         }
55639     },
55640
55641     updateBody : function(w, h){
55642         if(w !== null){
55643             this.el.setWidth(w);
55644             w -= this.el.getBorderWidth("rl");
55645             if(this.config.adjustments){
55646                 w += this.config.adjustments[0];
55647             }
55648         }
55649         if(h !== null){
55650             this.el.setHeight(h);
55651             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
55652             h -= this.el.getBorderWidth("tb");
55653             if(this.config.adjustments){
55654                 h += this.config.adjustments[1];
55655             }
55656             this.bodyEl.setHeight(h);
55657             if(this.tabs){
55658                 h = this.tabs.syncHeight(h);
55659             }
55660         }
55661         if(this.panelSize){
55662             w = w !== null ? w : this.panelSize.width;
55663             h = h !== null ? h : this.panelSize.height;
55664         }
55665         if(this.activePanel){
55666             var el = this.activePanel.getEl();
55667             w = w !== null ? w : el.getWidth();
55668             h = h !== null ? h : el.getHeight();
55669             this.panelSize = {width: w, height: h};
55670             this.activePanel.setSize(w, h);
55671         }
55672         if(Roo.isIE && this.tabs){
55673             this.tabs.el.repaint();
55674         }
55675     },
55676
55677     /**
55678      * Returns the container element for this region.
55679      * @return {Roo.Element}
55680      */
55681     getEl : function(){
55682         return this.el;
55683     },
55684
55685     /**
55686      * Hides this region.
55687      */
55688     hide : function(){
55689         if(!this.collapsed){
55690             this.el.dom.style.left = "-2000px";
55691             this.el.hide();
55692         }else{
55693             this.collapsedEl.dom.style.left = "-2000px";
55694             this.collapsedEl.hide();
55695         }
55696         this.visible = false;
55697         this.fireEvent("visibilitychange", this, false);
55698     },
55699
55700     /**
55701      * Shows this region if it was previously hidden.
55702      */
55703     show : function(){
55704         if(!this.collapsed){
55705             this.el.show();
55706         }else{
55707             this.collapsedEl.show();
55708         }
55709         this.visible = true;
55710         this.fireEvent("visibilitychange", this, true);
55711     },
55712
55713     closeClicked : function(){
55714         if(this.activePanel){
55715             this.remove(this.activePanel);
55716         }
55717     },
55718
55719     collapseClick : function(e){
55720         if(this.isSlid){
55721            e.stopPropagation();
55722            this.slideIn();
55723         }else{
55724            e.stopPropagation();
55725            this.slideOut();
55726         }
55727     },
55728
55729     /**
55730      * Collapses this region.
55731      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
55732      */
55733     collapse : function(skipAnim, skipCheck){
55734         if(this.collapsed) {
55735             return;
55736         }
55737         
55738         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
55739             
55740             this.collapsed = true;
55741             if(this.split){
55742                 this.split.el.hide();
55743             }
55744             if(this.config.animate && skipAnim !== true){
55745                 this.fireEvent("invalidated", this);
55746                 this.animateCollapse();
55747             }else{
55748                 this.el.setLocation(-20000,-20000);
55749                 this.el.hide();
55750                 this.collapsedEl.show();
55751                 this.fireEvent("collapsed", this);
55752                 this.fireEvent("invalidated", this);
55753             }
55754         }
55755         
55756     },
55757
55758     animateCollapse : function(){
55759         // overridden
55760     },
55761
55762     /**
55763      * Expands this region if it was previously collapsed.
55764      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
55765      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
55766      */
55767     expand : function(e, skipAnim){
55768         if(e) {
55769             e.stopPropagation();
55770         }
55771         if(!this.collapsed || this.el.hasActiveFx()) {
55772             return;
55773         }
55774         if(this.isSlid){
55775             this.afterSlideIn();
55776             skipAnim = true;
55777         }
55778         this.collapsed = false;
55779         if(this.config.animate && skipAnim !== true){
55780             this.animateExpand();
55781         }else{
55782             this.el.show();
55783             if(this.split){
55784                 this.split.el.show();
55785             }
55786             this.collapsedEl.setLocation(-2000,-2000);
55787             this.collapsedEl.hide();
55788             this.fireEvent("invalidated", this);
55789             this.fireEvent("expanded", this);
55790         }
55791     },
55792
55793     animateExpand : function(){
55794         // overridden
55795     },
55796
55797     initTabs : function()
55798     {
55799         this.bodyEl.setStyle("overflow", "hidden");
55800         var ts = new Roo.TabPanel(
55801                 this.bodyEl.dom,
55802                 {
55803                     tabPosition: this.bottomTabs ? 'bottom' : 'top',
55804                     disableTooltips: this.config.disableTabTips,
55805                     toolbar : this.config.toolbar
55806                 }
55807         );
55808         if(this.config.hideTabs){
55809             ts.stripWrap.setDisplayed(false);
55810         }
55811         this.tabs = ts;
55812         ts.resizeTabs = this.config.resizeTabs === true;
55813         ts.minTabWidth = this.config.minTabWidth || 40;
55814         ts.maxTabWidth = this.config.maxTabWidth || 250;
55815         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
55816         ts.monitorResize = false;
55817         ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
55818         ts.bodyEl.addClass('x-layout-tabs-body');
55819         this.panels.each(this.initPanelAsTab, this);
55820     },
55821
55822     initPanelAsTab : function(panel){
55823         var ti = this.tabs.addTab(panel.getEl().id, panel.getTitle(), null,
55824                     this.config.closeOnTab && panel.isClosable());
55825         if(panel.tabTip !== undefined){
55826             ti.setTooltip(panel.tabTip);
55827         }
55828         ti.on("activate", function(){
55829               this.setActivePanel(panel);
55830         }, this);
55831         if(this.config.closeOnTab){
55832             ti.on("beforeclose", function(t, e){
55833                 e.cancel = true;
55834                 this.remove(panel);
55835             }, this);
55836         }
55837         return ti;
55838     },
55839
55840     updatePanelTitle : function(panel, title){
55841         if(this.activePanel == panel){
55842             this.updateTitle(title);
55843         }
55844         if(this.tabs){
55845             var ti = this.tabs.getTab(panel.getEl().id);
55846             ti.setText(title);
55847             if(panel.tabTip !== undefined){
55848                 ti.setTooltip(panel.tabTip);
55849             }
55850         }
55851     },
55852
55853     updateTitle : function(title){
55854         if(this.titleTextEl && !this.config.title){
55855             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
55856         }
55857     },
55858
55859     setActivePanel : function(panel){
55860         panel = this.getPanel(panel);
55861         if(this.activePanel && this.activePanel != panel){
55862             this.activePanel.setActiveState(false);
55863         }
55864         this.activePanel = panel;
55865         panel.setActiveState(true);
55866         if(this.panelSize){
55867             panel.setSize(this.panelSize.width, this.panelSize.height);
55868         }
55869         if(this.closeBtn){
55870             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
55871         }
55872         this.updateTitle(panel.getTitle());
55873         if(this.tabs){
55874             this.fireEvent("invalidated", this);
55875         }
55876         this.fireEvent("panelactivated", this, panel);
55877     },
55878
55879     /**
55880      * Shows the specified panel.
55881      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
55882      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
55883      */
55884     showPanel : function(panel)
55885     {
55886         panel = this.getPanel(panel);
55887         if(panel){
55888             if(this.tabs){
55889                 var tab = this.tabs.getTab(panel.getEl().id);
55890                 if(tab.isHidden()){
55891                     this.tabs.unhideTab(tab.id);
55892                 }
55893                 tab.activate();
55894             }else{
55895                 this.setActivePanel(panel);
55896             }
55897         }
55898         return panel;
55899     },
55900
55901     /**
55902      * Get the active panel for this region.
55903      * @return {Roo.ContentPanel} The active panel or null
55904      */
55905     getActivePanel : function(){
55906         return this.activePanel;
55907     },
55908
55909     validateVisibility : function(){
55910         if(this.panels.getCount() < 1){
55911             this.updateTitle("&#160;");
55912             this.closeBtn.hide();
55913             this.hide();
55914         }else{
55915             if(!this.isVisible()){
55916                 this.show();
55917             }
55918         }
55919     },
55920
55921     /**
55922      * Adds the passed ContentPanel(s) to this region.
55923      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
55924      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
55925      */
55926     add : function(panel){
55927         if(arguments.length > 1){
55928             for(var i = 0, len = arguments.length; i < len; i++) {
55929                 this.add(arguments[i]);
55930             }
55931             return null;
55932         }
55933         if(this.hasPanel(panel)){
55934             this.showPanel(panel);
55935             return panel;
55936         }
55937         panel.setRegion(this);
55938         this.panels.add(panel);
55939         if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
55940             this.bodyEl.dom.appendChild(panel.getEl().dom);
55941             if(panel.background !== true){
55942                 this.setActivePanel(panel);
55943             }
55944             this.fireEvent("paneladded", this, panel);
55945             return panel;
55946         }
55947         if(!this.tabs){
55948             this.initTabs();
55949         }else{
55950             this.initPanelAsTab(panel);
55951         }
55952         if(panel.background !== true){
55953             this.tabs.activate(panel.getEl().id);
55954         }
55955         this.fireEvent("paneladded", this, panel);
55956         return panel;
55957     },
55958
55959     /**
55960      * Hides the tab for the specified panel.
55961      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55962      */
55963     hidePanel : function(panel){
55964         if(this.tabs && (panel = this.getPanel(panel))){
55965             this.tabs.hideTab(panel.getEl().id);
55966         }
55967     },
55968
55969     /**
55970      * Unhides the tab for a previously hidden panel.
55971      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55972      */
55973     unhidePanel : function(panel){
55974         if(this.tabs && (panel = this.getPanel(panel))){
55975             this.tabs.unhideTab(panel.getEl().id);
55976         }
55977     },
55978
55979     clearPanels : function(){
55980         while(this.panels.getCount() > 0){
55981              this.remove(this.panels.first());
55982         }
55983     },
55984
55985     /**
55986      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
55987      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
55988      * @param {Boolean} preservePanel Overrides the config preservePanel option
55989      * @return {Roo.ContentPanel} The panel that was removed
55990      */
55991     remove : function(panel, preservePanel){
55992         panel = this.getPanel(panel);
55993         if(!panel){
55994             return null;
55995         }
55996         var e = {};
55997         this.fireEvent("beforeremove", this, panel, e);
55998         if(e.cancel === true){
55999             return null;
56000         }
56001         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
56002         var panelId = panel.getId();
56003         this.panels.removeKey(panelId);
56004         if(preservePanel){
56005             document.body.appendChild(panel.getEl().dom);
56006         }
56007         if(this.tabs){
56008             this.tabs.removeTab(panel.getEl().id);
56009         }else if (!preservePanel){
56010             this.bodyEl.dom.removeChild(panel.getEl().dom);
56011         }
56012         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
56013             var p = this.panels.first();
56014             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
56015             tempEl.appendChild(p.getEl().dom);
56016             this.bodyEl.update("");
56017             this.bodyEl.dom.appendChild(p.getEl().dom);
56018             tempEl = null;
56019             this.updateTitle(p.getTitle());
56020             this.tabs = null;
56021             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
56022             this.setActivePanel(p);
56023         }
56024         panel.setRegion(null);
56025         if(this.activePanel == panel){
56026             this.activePanel = null;
56027         }
56028         if(this.config.autoDestroy !== false && preservePanel !== true){
56029             try{panel.destroy();}catch(e){}
56030         }
56031         this.fireEvent("panelremoved", this, panel);
56032         return panel;
56033     },
56034
56035     /**
56036      * Returns the TabPanel component used by this region
56037      * @return {Roo.TabPanel}
56038      */
56039     getTabs : function(){
56040         return this.tabs;
56041     },
56042
56043     createTool : function(parentEl, className){
56044         var btn = Roo.DomHelper.append(parentEl, {tag: "div", cls: "x-layout-tools-button",
56045             children: [{tag: "div", cls: "x-layout-tools-button-inner " + className, html: "&#160;"}]}, true);
56046         btn.addClassOnOver("x-layout-tools-button-over");
56047         return btn;
56048     }
56049 });/*
56050  * Based on:
56051  * Ext JS Library 1.1.1
56052  * Copyright(c) 2006-2007, Ext JS, LLC.
56053  *
56054  * Originally Released Under LGPL - original licence link has changed is not relivant.
56055  *
56056  * Fork - LGPL
56057  * <script type="text/javascript">
56058  */
56059  
56060
56061
56062 /**
56063  * @class Roo.SplitLayoutRegion
56064  * @extends Roo.LayoutRegion
56065  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
56066  */
56067 Roo.SplitLayoutRegion = function(mgr, config, pos, cursor){
56068     this.cursor = cursor;
56069     Roo.SplitLayoutRegion.superclass.constructor.call(this, mgr, config, pos);
56070 };
56071
56072 Roo.extend(Roo.SplitLayoutRegion, Roo.LayoutRegion, {
56073     splitTip : "Drag to resize.",
56074     collapsibleSplitTip : "Drag to resize. Double click to hide.",
56075     useSplitTips : false,
56076
56077     applyConfig : function(config){
56078         Roo.SplitLayoutRegion.superclass.applyConfig.call(this, config);
56079         if(config.split){
56080             if(!this.split){
56081                 var splitEl = Roo.DomHelper.append(this.mgr.el.dom, 
56082                         {tag: "div", id: this.el.id + "-split", cls: "x-layout-split x-layout-split-"+this.position, html: "&#160;"});
56083                 /** The SplitBar for this region 
56084                 * @type Roo.SplitBar */
56085                 this.split = new Roo.SplitBar(splitEl, this.el, this.orientation);
56086                 this.split.on("moved", this.onSplitMove, this);
56087                 this.split.useShim = config.useShim === true;
56088                 this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
56089                 if(this.useSplitTips){
56090                     this.split.el.dom.title = config.collapsible ? this.collapsibleSplitTip : this.splitTip;
56091                 }
56092                 if(config.collapsible){
56093                     this.split.el.on("dblclick", this.collapse,  this);
56094                 }
56095             }
56096             if(typeof config.minSize != "undefined"){
56097                 this.split.minSize = config.minSize;
56098             }
56099             if(typeof config.maxSize != "undefined"){
56100                 this.split.maxSize = config.maxSize;
56101             }
56102             if(config.hideWhenEmpty || config.hidden || config.collapsed){
56103                 this.hideSplitter();
56104             }
56105         }
56106     },
56107
56108     getHMaxSize : function(){
56109          var cmax = this.config.maxSize || 10000;
56110          var center = this.mgr.getRegion("center");
56111          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
56112     },
56113
56114     getVMaxSize : function(){
56115          var cmax = this.config.maxSize || 10000;
56116          var center = this.mgr.getRegion("center");
56117          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
56118     },
56119
56120     onSplitMove : function(split, newSize){
56121         this.fireEvent("resized", this, newSize);
56122     },
56123     
56124     /** 
56125      * Returns the {@link Roo.SplitBar} for this region.
56126      * @return {Roo.SplitBar}
56127      */
56128     getSplitBar : function(){
56129         return this.split;
56130     },
56131     
56132     hide : function(){
56133         this.hideSplitter();
56134         Roo.SplitLayoutRegion.superclass.hide.call(this);
56135     },
56136
56137     hideSplitter : function(){
56138         if(this.split){
56139             this.split.el.setLocation(-2000,-2000);
56140             this.split.el.hide();
56141         }
56142     },
56143
56144     show : function(){
56145         if(this.split){
56146             this.split.el.show();
56147         }
56148         Roo.SplitLayoutRegion.superclass.show.call(this);
56149     },
56150     
56151     beforeSlide: function(){
56152         if(Roo.isGecko){// firefox overflow auto bug workaround
56153             this.bodyEl.clip();
56154             if(this.tabs) {
56155                 this.tabs.bodyEl.clip();
56156             }
56157             if(this.activePanel){
56158                 this.activePanel.getEl().clip();
56159                 
56160                 if(this.activePanel.beforeSlide){
56161                     this.activePanel.beforeSlide();
56162                 }
56163             }
56164         }
56165     },
56166     
56167     afterSlide : function(){
56168         if(Roo.isGecko){// firefox overflow auto bug workaround
56169             this.bodyEl.unclip();
56170             if(this.tabs) {
56171                 this.tabs.bodyEl.unclip();
56172             }
56173             if(this.activePanel){
56174                 this.activePanel.getEl().unclip();
56175                 if(this.activePanel.afterSlide){
56176                     this.activePanel.afterSlide();
56177                 }
56178             }
56179         }
56180     },
56181
56182     initAutoHide : function(){
56183         if(this.autoHide !== false){
56184             if(!this.autoHideHd){
56185                 var st = new Roo.util.DelayedTask(this.slideIn, this);
56186                 this.autoHideHd = {
56187                     "mouseout": function(e){
56188                         if(!e.within(this.el, true)){
56189                             st.delay(500);
56190                         }
56191                     },
56192                     "mouseover" : function(e){
56193                         st.cancel();
56194                     },
56195                     scope : this
56196                 };
56197             }
56198             this.el.on(this.autoHideHd);
56199         }
56200     },
56201
56202     clearAutoHide : function(){
56203         if(this.autoHide !== false){
56204             this.el.un("mouseout", this.autoHideHd.mouseout);
56205             this.el.un("mouseover", this.autoHideHd.mouseover);
56206         }
56207     },
56208
56209     clearMonitor : function(){
56210         Roo.get(document).un("click", this.slideInIf, this);
56211     },
56212
56213     // these names are backwards but not changed for compat
56214     slideOut : function(){
56215         if(this.isSlid || this.el.hasActiveFx()){
56216             return;
56217         }
56218         this.isSlid = true;
56219         if(this.collapseBtn){
56220             this.collapseBtn.hide();
56221         }
56222         this.closeBtnState = this.closeBtn.getStyle('display');
56223         this.closeBtn.hide();
56224         if(this.stickBtn){
56225             this.stickBtn.show();
56226         }
56227         this.el.show();
56228         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
56229         this.beforeSlide();
56230         this.el.setStyle("z-index", 10001);
56231         this.el.slideIn(this.getSlideAnchor(), {
56232             callback: function(){
56233                 this.afterSlide();
56234                 this.initAutoHide();
56235                 Roo.get(document).on("click", this.slideInIf, this);
56236                 this.fireEvent("slideshow", this);
56237             },
56238             scope: this,
56239             block: true
56240         });
56241     },
56242
56243     afterSlideIn : function(){
56244         this.clearAutoHide();
56245         this.isSlid = false;
56246         this.clearMonitor();
56247         this.el.setStyle("z-index", "");
56248         if(this.collapseBtn){
56249             this.collapseBtn.show();
56250         }
56251         this.closeBtn.setStyle('display', this.closeBtnState);
56252         if(this.stickBtn){
56253             this.stickBtn.hide();
56254         }
56255         this.fireEvent("slidehide", this);
56256     },
56257
56258     slideIn : function(cb){
56259         if(!this.isSlid || this.el.hasActiveFx()){
56260             Roo.callback(cb);
56261             return;
56262         }
56263         this.isSlid = false;
56264         this.beforeSlide();
56265         this.el.slideOut(this.getSlideAnchor(), {
56266             callback: function(){
56267                 this.el.setLeftTop(-10000, -10000);
56268                 this.afterSlide();
56269                 this.afterSlideIn();
56270                 Roo.callback(cb);
56271             },
56272             scope: this,
56273             block: true
56274         });
56275     },
56276     
56277     slideInIf : function(e){
56278         if(!e.within(this.el)){
56279             this.slideIn();
56280         }
56281     },
56282
56283     animateCollapse : function(){
56284         this.beforeSlide();
56285         this.el.setStyle("z-index", 20000);
56286         var anchor = this.getSlideAnchor();
56287         this.el.slideOut(anchor, {
56288             callback : function(){
56289                 this.el.setStyle("z-index", "");
56290                 this.collapsedEl.slideIn(anchor, {duration:.3});
56291                 this.afterSlide();
56292                 this.el.setLocation(-10000,-10000);
56293                 this.el.hide();
56294                 this.fireEvent("collapsed", this);
56295             },
56296             scope: this,
56297             block: true
56298         });
56299     },
56300
56301     animateExpand : function(){
56302         this.beforeSlide();
56303         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
56304         this.el.setStyle("z-index", 20000);
56305         this.collapsedEl.hide({
56306             duration:.1
56307         });
56308         this.el.slideIn(this.getSlideAnchor(), {
56309             callback : function(){
56310                 this.el.setStyle("z-index", "");
56311                 this.afterSlide();
56312                 if(this.split){
56313                     this.split.el.show();
56314                 }
56315                 this.fireEvent("invalidated", this);
56316                 this.fireEvent("expanded", this);
56317             },
56318             scope: this,
56319             block: true
56320         });
56321     },
56322
56323     anchors : {
56324         "west" : "left",
56325         "east" : "right",
56326         "north" : "top",
56327         "south" : "bottom"
56328     },
56329
56330     sanchors : {
56331         "west" : "l",
56332         "east" : "r",
56333         "north" : "t",
56334         "south" : "b"
56335     },
56336
56337     canchors : {
56338         "west" : "tl-tr",
56339         "east" : "tr-tl",
56340         "north" : "tl-bl",
56341         "south" : "bl-tl"
56342     },
56343
56344     getAnchor : function(){
56345         return this.anchors[this.position];
56346     },
56347
56348     getCollapseAnchor : function(){
56349         return this.canchors[this.position];
56350     },
56351
56352     getSlideAnchor : function(){
56353         return this.sanchors[this.position];
56354     },
56355
56356     getAlignAdj : function(){
56357         var cm = this.cmargins;
56358         switch(this.position){
56359             case "west":
56360                 return [0, 0];
56361             break;
56362             case "east":
56363                 return [0, 0];
56364             break;
56365             case "north":
56366                 return [0, 0];
56367             break;
56368             case "south":
56369                 return [0, 0];
56370             break;
56371         }
56372     },
56373
56374     getExpandAdj : function(){
56375         var c = this.collapsedEl, cm = this.cmargins;
56376         switch(this.position){
56377             case "west":
56378                 return [-(cm.right+c.getWidth()+cm.left), 0];
56379             break;
56380             case "east":
56381                 return [cm.right+c.getWidth()+cm.left, 0];
56382             break;
56383             case "north":
56384                 return [0, -(cm.top+cm.bottom+c.getHeight())];
56385             break;
56386             case "south":
56387                 return [0, cm.top+cm.bottom+c.getHeight()];
56388             break;
56389         }
56390     }
56391 });/*
56392  * Based on:
56393  * Ext JS Library 1.1.1
56394  * Copyright(c) 2006-2007, Ext JS, LLC.
56395  *
56396  * Originally Released Under LGPL - original licence link has changed is not relivant.
56397  *
56398  * Fork - LGPL
56399  * <script type="text/javascript">
56400  */
56401 /*
56402  * These classes are private internal classes
56403  */
56404 Roo.CenterLayoutRegion = function(mgr, config){
56405     Roo.LayoutRegion.call(this, mgr, config, "center");
56406     this.visible = true;
56407     this.minWidth = config.minWidth || 20;
56408     this.minHeight = config.minHeight || 20;
56409 };
56410
56411 Roo.extend(Roo.CenterLayoutRegion, Roo.LayoutRegion, {
56412     hide : function(){
56413         // center panel can't be hidden
56414     },
56415     
56416     show : function(){
56417         // center panel can't be hidden
56418     },
56419     
56420     getMinWidth: function(){
56421         return this.minWidth;
56422     },
56423     
56424     getMinHeight: function(){
56425         return this.minHeight;
56426     }
56427 });
56428
56429
56430 Roo.NorthLayoutRegion = function(mgr, config){
56431     Roo.LayoutRegion.call(this, mgr, config, "north", "n-resize");
56432     if(this.split){
56433         this.split.placement = Roo.SplitBar.TOP;
56434         this.split.orientation = Roo.SplitBar.VERTICAL;
56435         this.split.el.addClass("x-layout-split-v");
56436     }
56437     var size = config.initialSize || config.height;
56438     if(typeof size != "undefined"){
56439         this.el.setHeight(size);
56440     }
56441 };
56442 Roo.extend(Roo.NorthLayoutRegion, Roo.SplitLayoutRegion, {
56443     orientation: Roo.SplitBar.VERTICAL,
56444     getBox : function(){
56445         if(this.collapsed){
56446             return this.collapsedEl.getBox();
56447         }
56448         var box = this.el.getBox();
56449         if(this.split){
56450             box.height += this.split.el.getHeight();
56451         }
56452         return box;
56453     },
56454     
56455     updateBox : function(box){
56456         if(this.split && !this.collapsed){
56457             box.height -= this.split.el.getHeight();
56458             this.split.el.setLeft(box.x);
56459             this.split.el.setTop(box.y+box.height);
56460             this.split.el.setWidth(box.width);
56461         }
56462         if(this.collapsed){
56463             this.updateBody(box.width, null);
56464         }
56465         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56466     }
56467 });
56468
56469 Roo.SouthLayoutRegion = function(mgr, config){
56470     Roo.SplitLayoutRegion.call(this, mgr, config, "south", "s-resize");
56471     if(this.split){
56472         this.split.placement = Roo.SplitBar.BOTTOM;
56473         this.split.orientation = Roo.SplitBar.VERTICAL;
56474         this.split.el.addClass("x-layout-split-v");
56475     }
56476     var size = config.initialSize || config.height;
56477     if(typeof size != "undefined"){
56478         this.el.setHeight(size);
56479     }
56480 };
56481 Roo.extend(Roo.SouthLayoutRegion, Roo.SplitLayoutRegion, {
56482     orientation: Roo.SplitBar.VERTICAL,
56483     getBox : function(){
56484         if(this.collapsed){
56485             return this.collapsedEl.getBox();
56486         }
56487         var box = this.el.getBox();
56488         if(this.split){
56489             var sh = this.split.el.getHeight();
56490             box.height += sh;
56491             box.y -= sh;
56492         }
56493         return box;
56494     },
56495     
56496     updateBox : function(box){
56497         if(this.split && !this.collapsed){
56498             var sh = this.split.el.getHeight();
56499             box.height -= sh;
56500             box.y += sh;
56501             this.split.el.setLeft(box.x);
56502             this.split.el.setTop(box.y-sh);
56503             this.split.el.setWidth(box.width);
56504         }
56505         if(this.collapsed){
56506             this.updateBody(box.width, null);
56507         }
56508         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56509     }
56510 });
56511
56512 Roo.EastLayoutRegion = function(mgr, config){
56513     Roo.SplitLayoutRegion.call(this, mgr, config, "east", "e-resize");
56514     if(this.split){
56515         this.split.placement = Roo.SplitBar.RIGHT;
56516         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56517         this.split.el.addClass("x-layout-split-h");
56518     }
56519     var size = config.initialSize || config.width;
56520     if(typeof size != "undefined"){
56521         this.el.setWidth(size);
56522     }
56523 };
56524 Roo.extend(Roo.EastLayoutRegion, Roo.SplitLayoutRegion, {
56525     orientation: Roo.SplitBar.HORIZONTAL,
56526     getBox : function(){
56527         if(this.collapsed){
56528             return this.collapsedEl.getBox();
56529         }
56530         var box = this.el.getBox();
56531         if(this.split){
56532             var sw = this.split.el.getWidth();
56533             box.width += sw;
56534             box.x -= sw;
56535         }
56536         return box;
56537     },
56538
56539     updateBox : function(box){
56540         if(this.split && !this.collapsed){
56541             var sw = this.split.el.getWidth();
56542             box.width -= sw;
56543             this.split.el.setLeft(box.x);
56544             this.split.el.setTop(box.y);
56545             this.split.el.setHeight(box.height);
56546             box.x += sw;
56547         }
56548         if(this.collapsed){
56549             this.updateBody(null, box.height);
56550         }
56551         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56552     }
56553 });
56554
56555 Roo.WestLayoutRegion = function(mgr, config){
56556     Roo.SplitLayoutRegion.call(this, mgr, config, "west", "w-resize");
56557     if(this.split){
56558         this.split.placement = Roo.SplitBar.LEFT;
56559         this.split.orientation = Roo.SplitBar.HORIZONTAL;
56560         this.split.el.addClass("x-layout-split-h");
56561     }
56562     var size = config.initialSize || config.width;
56563     if(typeof size != "undefined"){
56564         this.el.setWidth(size);
56565     }
56566 };
56567 Roo.extend(Roo.WestLayoutRegion, Roo.SplitLayoutRegion, {
56568     orientation: Roo.SplitBar.HORIZONTAL,
56569     getBox : function(){
56570         if(this.collapsed){
56571             return this.collapsedEl.getBox();
56572         }
56573         var box = this.el.getBox();
56574         if(this.split){
56575             box.width += this.split.el.getWidth();
56576         }
56577         return box;
56578     },
56579     
56580     updateBox : function(box){
56581         if(this.split && !this.collapsed){
56582             var sw = this.split.el.getWidth();
56583             box.width -= sw;
56584             this.split.el.setLeft(box.x+box.width);
56585             this.split.el.setTop(box.y);
56586             this.split.el.setHeight(box.height);
56587         }
56588         if(this.collapsed){
56589             this.updateBody(null, box.height);
56590         }
56591         Roo.LayoutRegion.prototype.updateBox.call(this, box);
56592     }
56593 });
56594 /*
56595  * Based on:
56596  * Ext JS Library 1.1.1
56597  * Copyright(c) 2006-2007, Ext JS, LLC.
56598  *
56599  * Originally Released Under LGPL - original licence link has changed is not relivant.
56600  *
56601  * Fork - LGPL
56602  * <script type="text/javascript">
56603  */
56604  
56605  
56606 /*
56607  * Private internal class for reading and applying state
56608  */
56609 Roo.LayoutStateManager = function(layout){
56610      // default empty state
56611      this.state = {
56612         north: {},
56613         south: {},
56614         east: {},
56615         west: {}       
56616     };
56617 };
56618
56619 Roo.LayoutStateManager.prototype = {
56620     init : function(layout, provider){
56621         this.provider = provider;
56622         var state = provider.get(layout.id+"-layout-state");
56623         if(state){
56624             var wasUpdating = layout.isUpdating();
56625             if(!wasUpdating){
56626                 layout.beginUpdate();
56627             }
56628             for(var key in state){
56629                 if(typeof state[key] != "function"){
56630                     var rstate = state[key];
56631                     var r = layout.getRegion(key);
56632                     if(r && rstate){
56633                         if(rstate.size){
56634                             r.resizeTo(rstate.size);
56635                         }
56636                         if(rstate.collapsed == true){
56637                             r.collapse(true);
56638                         }else{
56639                             r.expand(null, true);
56640                         }
56641                     }
56642                 }
56643             }
56644             if(!wasUpdating){
56645                 layout.endUpdate();
56646             }
56647             this.state = state; 
56648         }
56649         this.layout = layout;
56650         layout.on("regionresized", this.onRegionResized, this);
56651         layout.on("regioncollapsed", this.onRegionCollapsed, this);
56652         layout.on("regionexpanded", this.onRegionExpanded, this);
56653     },
56654     
56655     storeState : function(){
56656         this.provider.set(this.layout.id+"-layout-state", this.state);
56657     },
56658     
56659     onRegionResized : function(region, newSize){
56660         this.state[region.getPosition()].size = newSize;
56661         this.storeState();
56662     },
56663     
56664     onRegionCollapsed : function(region){
56665         this.state[region.getPosition()].collapsed = true;
56666         this.storeState();
56667     },
56668     
56669     onRegionExpanded : function(region){
56670         this.state[region.getPosition()].collapsed = false;
56671         this.storeState();
56672     }
56673 };/*
56674  * Based on:
56675  * Ext JS Library 1.1.1
56676  * Copyright(c) 2006-2007, Ext JS, LLC.
56677  *
56678  * Originally Released Under LGPL - original licence link has changed is not relivant.
56679  *
56680  * Fork - LGPL
56681  * <script type="text/javascript">
56682  */
56683 /**
56684  * @class Roo.ContentPanel
56685  * @extends Roo.util.Observable
56686  * @children Roo.form.Form Roo.JsonView Roo.View
56687  * @parent Roo.BorderLayout Roo.LayoutDialog builder-top
56688  * A basic ContentPanel element.
56689  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
56690  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
56691  * @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
56692  * @cfg {Boolean}   closable      True if the panel can be closed/removed
56693  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
56694  * @cfg {String|HTMLElement|Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
56695  * @cfg {Roo.Toolbar}   toolbar       A toolbar for this panel
56696  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
56697  * @cfg {String} title          The title for this panel
56698  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
56699  * @cfg {String} url            Calls {@link #setUrl} with this value
56700  * @cfg {String} region [required]   (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
56701  * @cfg {String|Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
56702  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
56703  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
56704  * @cfg {String}    style  Extra style to add to the content panel
56705  * @cfg {Roo.menu.Menu} menu  popup menu
56706
56707  * @constructor
56708  * Create a new ContentPanel.
56709  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
56710  * @param {String/Object} config A string to set only the title or a config object
56711  * @param {String} content (optional) Set the HTML content for this panel
56712  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
56713  */
56714 Roo.ContentPanel = function(el, config, content){
56715     
56716      
56717     /*
56718     if(el.autoCreate || el.xtype){ // xtype is available if this is called from factory
56719         config = el;
56720         el = Roo.id();
56721     }
56722     if (config && config.parentLayout) { 
56723         el = config.parentLayout.el.createChild(); 
56724     }
56725     */
56726     if(el.autoCreate){ // xtype is available if this is called from factory
56727         config = el;
56728         el = Roo.id();
56729     }
56730     this.el = Roo.get(el);
56731     if(!this.el && config && config.autoCreate){
56732         if(typeof config.autoCreate == "object"){
56733             if(!config.autoCreate.id){
56734                 config.autoCreate.id = config.id||el;
56735             }
56736             this.el = Roo.DomHelper.append(document.body,
56737                         config.autoCreate, true);
56738         }else{
56739             this.el = Roo.DomHelper.append(document.body,
56740                         {tag: "div", cls: "x-layout-inactive-content", id: config.id||el}, true);
56741         }
56742     }
56743     
56744     
56745     this.closable = false;
56746     this.loaded = false;
56747     this.active = false;
56748     if(typeof config == "string"){
56749         this.title = config;
56750     }else{
56751         Roo.apply(this, config);
56752     }
56753     
56754     if (this.toolbar && !this.toolbar.el && this.toolbar.xtype) {
56755         this.wrapEl = this.el.wrap();
56756         this.toolbar.container = this.el.insertSibling(false, 'before');
56757         this.toolbar = new Roo.Toolbar(this.toolbar);
56758     }
56759     
56760     // xtype created footer. - not sure if will work as we normally have to render first..
56761     if (this.footer && !this.footer.el && this.footer.xtype) {
56762         if (!this.wrapEl) {
56763             this.wrapEl = this.el.wrap();
56764         }
56765     
56766         this.footer.container = this.wrapEl.createChild();
56767          
56768         this.footer = Roo.factory(this.footer, Roo);
56769         
56770     }
56771     
56772     if(this.resizeEl){
56773         this.resizeEl = Roo.get(this.resizeEl, true);
56774     }else{
56775         this.resizeEl = this.el;
56776     }
56777     // handle view.xtype
56778     
56779  
56780     
56781     
56782     this.addEvents({
56783         /**
56784          * @event activate
56785          * Fires when this panel is activated. 
56786          * @param {Roo.ContentPanel} this
56787          */
56788         "activate" : true,
56789         /**
56790          * @event deactivate
56791          * Fires when this panel is activated. 
56792          * @param {Roo.ContentPanel} this
56793          */
56794         "deactivate" : true,
56795
56796         /**
56797          * @event resize
56798          * Fires when this panel is resized if fitToFrame is true.
56799          * @param {Roo.ContentPanel} this
56800          * @param {Number} width The width after any component adjustments
56801          * @param {Number} height The height after any component adjustments
56802          */
56803         "resize" : true,
56804         
56805          /**
56806          * @event render
56807          * Fires when this tab is created
56808          * @param {Roo.ContentPanel} this
56809          */
56810         "render" : true
56811          
56812         
56813     });
56814     
56815
56816     
56817     
56818     if(this.autoScroll){
56819         this.resizeEl.setStyle("overflow", "auto");
56820     } else {
56821         // fix randome scrolling
56822         this.el.on('scroll', function() {
56823             Roo.log('fix random scolling');
56824             this.scrollTo('top',0); 
56825         });
56826     }
56827     content = content || this.content;
56828     if(content){
56829         this.setContent(content);
56830     }
56831     if(config && config.url){
56832         this.setUrl(this.url, this.params, this.loadOnce);
56833     }
56834     
56835     
56836     
56837     Roo.ContentPanel.superclass.constructor.call(this);
56838     
56839     if (this.view && typeof(this.view.xtype) != 'undefined') {
56840         this.view.el = this.el.appendChild(document.createElement("div"));
56841         this.view = Roo.factory(this.view); 
56842         this.view.render  &&  this.view.render(false, '');  
56843     }
56844     
56845     
56846     this.fireEvent('render', this);
56847 };
56848
56849 Roo.extend(Roo.ContentPanel, Roo.util.Observable, {
56850     tabTip:'',
56851     setRegion : function(region){
56852         this.region = region;
56853         if(region){
56854            this.el.replaceClass("x-layout-inactive-content", "x-layout-active-content");
56855         }else{
56856            this.el.replaceClass("x-layout-active-content", "x-layout-inactive-content");
56857         } 
56858     },
56859     
56860     /**
56861      * Returns the toolbar for this Panel if one was configured. 
56862      * @return {Roo.Toolbar} 
56863      */
56864     getToolbar : function(){
56865         return this.toolbar;
56866     },
56867     
56868     setActiveState : function(active){
56869         this.active = active;
56870         if(!active){
56871             this.fireEvent("deactivate", this);
56872         }else{
56873             this.fireEvent("activate", this);
56874         }
56875     },
56876     /**
56877      * Updates this panel's element
56878      * @param {String} content The new content
56879      * @param {Boolean} loadScripts (optional) true to look for and process scripts
56880     */
56881     setContent : function(content, loadScripts){
56882         this.el.update(content, loadScripts);
56883     },
56884
56885     ignoreResize : function(w, h){
56886         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
56887             return true;
56888         }else{
56889             this.lastSize = {width: w, height: h};
56890             return false;
56891         }
56892     },
56893     /**
56894      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
56895      * @return {Roo.UpdateManager} The UpdateManager
56896      */
56897     getUpdateManager : function(){
56898         return this.el.getUpdateManager();
56899     },
56900      /**
56901      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
56902      * @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:
56903 <pre><code>
56904 panel.load({
56905     url: "your-url.php",
56906     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
56907     callback: yourFunction,
56908     scope: yourObject, //(optional scope)
56909     discardUrl: false,
56910     nocache: false,
56911     text: "Loading...",
56912     timeout: 30,
56913     scripts: false
56914 });
56915 </code></pre>
56916      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
56917      * 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.
56918      * @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}
56919      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
56920      * @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.
56921      * @return {Roo.ContentPanel} this
56922      */
56923     load : function(){
56924         var um = this.el.getUpdateManager();
56925         um.update.apply(um, arguments);
56926         return this;
56927     },
56928
56929
56930     /**
56931      * 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.
56932      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
56933      * @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)
56934      * @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)
56935      * @return {Roo.UpdateManager} The UpdateManager
56936      */
56937     setUrl : function(url, params, loadOnce){
56938         if(this.refreshDelegate){
56939             this.removeListener("activate", this.refreshDelegate);
56940         }
56941         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
56942         this.on("activate", this.refreshDelegate);
56943         return this.el.getUpdateManager();
56944     },
56945     
56946     _handleRefresh : function(url, params, loadOnce){
56947         if(!loadOnce || !this.loaded){
56948             var updater = this.el.getUpdateManager();
56949             updater.update(url, params, this._setLoaded.createDelegate(this));
56950         }
56951     },
56952     
56953     _setLoaded : function(){
56954         this.loaded = true;
56955     }, 
56956     
56957     /**
56958      * Returns this panel's id
56959      * @return {String} 
56960      */
56961     getId : function(){
56962         return this.el.id;
56963     },
56964     
56965     /** 
56966      * Returns this panel's element - used by regiosn to add.
56967      * @return {Roo.Element} 
56968      */
56969     getEl : function(){
56970         return this.wrapEl || this.el;
56971     },
56972     
56973     adjustForComponents : function(width, height)
56974     {
56975         //Roo.log('adjustForComponents ');
56976         if(this.resizeEl != this.el){
56977             width -= this.el.getFrameWidth('lr');
56978             height -= this.el.getFrameWidth('tb');
56979         }
56980         if(this.toolbar){
56981             var te = this.toolbar.getEl();
56982             height -= te.getHeight();
56983             te.setWidth(width);
56984         }
56985         if(this.footer){
56986             var te = this.footer.getEl();
56987             //Roo.log("footer:" + te.getHeight());
56988             
56989             height -= te.getHeight();
56990             te.setWidth(width);
56991         }
56992         
56993         
56994         if(this.adjustments){
56995             width += this.adjustments[0];
56996             height += this.adjustments[1];
56997         }
56998         return {"width": width, "height": height};
56999     },
57000     
57001     setSize : function(width, height){
57002         if(this.fitToFrame && !this.ignoreResize(width, height)){
57003             if(this.fitContainer && this.resizeEl != this.el){
57004                 this.el.setSize(width, height);
57005             }
57006             var size = this.adjustForComponents(width, height);
57007             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
57008             this.fireEvent('resize', this, size.width, size.height);
57009         }
57010     },
57011     
57012     /**
57013      * Returns this panel's title
57014      * @return {String} 
57015      */
57016     getTitle : function(){
57017         return this.title;
57018     },
57019     
57020     /**
57021      * Set this panel's title
57022      * @param {String} title
57023      */
57024     setTitle : function(title){
57025         this.title = title;
57026         if(this.region){
57027             this.region.updatePanelTitle(this, title);
57028         }
57029     },
57030     
57031     /**
57032      * Returns true is this panel was configured to be closable
57033      * @return {Boolean} 
57034      */
57035     isClosable : function(){
57036         return this.closable;
57037     },
57038     
57039     beforeSlide : function(){
57040         this.el.clip();
57041         this.resizeEl.clip();
57042     },
57043     
57044     afterSlide : function(){
57045         this.el.unclip();
57046         this.resizeEl.unclip();
57047     },
57048     
57049     /**
57050      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
57051      *   Will fail silently if the {@link #setUrl} method has not been called.
57052      *   This does not activate the panel, just updates its content.
57053      */
57054     refresh : function(){
57055         if(this.refreshDelegate){
57056            this.loaded = false;
57057            this.refreshDelegate();
57058         }
57059     },
57060     
57061     /**
57062      * Destroys this panel
57063      */
57064     destroy : function(){
57065         this.el.removeAllListeners();
57066         var tempEl = document.createElement("span");
57067         tempEl.appendChild(this.el.dom);
57068         tempEl.innerHTML = "";
57069         this.el.remove();
57070         this.el = null;
57071     },
57072     
57073     /**
57074      * form - if the content panel contains a form - this is a reference to it.
57075      * @type {Roo.form.Form}
57076      */
57077     form : false,
57078     /**
57079      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
57080      *    This contains a reference to it.
57081      * @type {Roo.View}
57082      */
57083     view : false,
57084     
57085       /**
57086      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
57087      * <pre><code>
57088
57089 layout.addxtype({
57090        xtype : 'Form',
57091        items: [ .... ]
57092    }
57093 );
57094
57095 </code></pre>
57096      * @param {Object} cfg Xtype definition of item to add.
57097      */
57098     
57099     addxtype : function(cfg) {
57100         // add form..
57101         if (cfg.xtype.match(/^Form$/)) {
57102             
57103             var el;
57104             //if (this.footer) {
57105             //    el = this.footer.container.insertSibling(false, 'before');
57106             //} else {
57107                 el = this.el.createChild();
57108             //}
57109
57110             this.form = new  Roo.form.Form(cfg);
57111             
57112             
57113             if ( this.form.allItems.length) {
57114                 this.form.render(el.dom);
57115             }
57116             return this.form;
57117         }
57118         // should only have one of theses..
57119         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
57120             // views.. should not be just added - used named prop 'view''
57121             
57122             cfg.el = this.el.appendChild(document.createElement("div"));
57123             // factory?
57124             
57125             var ret = new Roo.factory(cfg);
57126              
57127              ret.render && ret.render(false, ''); // render blank..
57128             this.view = ret;
57129             return ret;
57130         }
57131         return false;
57132     }
57133 });
57134
57135 /**
57136  * @class Roo.GridPanel
57137  * @extends Roo.ContentPanel
57138  * @constructor
57139  * Create a new GridPanel.
57140  * @param {Roo.grid.Grid} grid The grid for this panel
57141  * @param {String/Object} config A string to set only the panel's title, or a config object
57142  */
57143 Roo.GridPanel = function(grid, config){
57144     
57145   
57146     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
57147         {tag: "div", cls: "x-layout-grid-wrapper x-layout-inactive-content"}, true);
57148         
57149     this.wrapper.dom.appendChild(grid.getGridEl().dom);
57150     
57151     Roo.GridPanel.superclass.constructor.call(this, this.wrapper, config);
57152     
57153     if(this.toolbar){
57154         this.toolbar.el.insertBefore(this.wrapper.dom.firstChild);
57155     }
57156     // xtype created footer. - not sure if will work as we normally have to render first..
57157     if (this.footer && !this.footer.el && this.footer.xtype) {
57158         
57159         this.footer.container = this.grid.getView().getFooterPanel(true);
57160         this.footer.dataSource = this.grid.dataSource;
57161         this.footer = Roo.factory(this.footer, Roo);
57162         
57163     }
57164     
57165     grid.monitorWindowResize = false; // turn off autosizing
57166     grid.autoHeight = false;
57167     grid.autoWidth = false;
57168     this.grid = grid;
57169     this.grid.getGridEl().replaceClass("x-layout-inactive-content", "x-layout-component-panel");
57170 };
57171
57172 Roo.extend(Roo.GridPanel, Roo.ContentPanel, {
57173     getId : function(){
57174         return this.grid.id;
57175     },
57176     
57177     /**
57178      * Returns the grid for this panel
57179      * @return {Roo.grid.Grid} 
57180      */
57181     getGrid : function(){
57182         return this.grid;    
57183     },
57184     
57185     setSize : function(width, height){
57186         if(!this.ignoreResize(width, height)){
57187             var grid = this.grid;
57188             var size = this.adjustForComponents(width, height);
57189             grid.getGridEl().setSize(size.width, size.height);
57190             grid.autoSize();
57191         }
57192     },
57193     
57194     beforeSlide : function(){
57195         this.grid.getView().scroller.clip();
57196     },
57197     
57198     afterSlide : function(){
57199         this.grid.getView().scroller.unclip();
57200     },
57201     
57202     destroy : function(){
57203         this.grid.destroy();
57204         delete this.grid;
57205         Roo.GridPanel.superclass.destroy.call(this); 
57206     }
57207 });
57208
57209
57210 /**
57211  * @class Roo.NestedLayoutPanel
57212  * @extends Roo.ContentPanel
57213  * @constructor
57214  * Create a new NestedLayoutPanel.
57215  * 
57216  * 
57217  * @param {Roo.BorderLayout} layout [required] The layout for this panel
57218  * @param {String/Object} config A string to set only the title or a config object
57219  */
57220 Roo.NestedLayoutPanel = function(layout, config)
57221 {
57222     // construct with only one argument..
57223     /* FIXME - implement nicer consturctors
57224     if (layout.layout) {
57225         config = layout;
57226         layout = config.layout;
57227         delete config.layout;
57228     }
57229     if (layout.xtype && !layout.getEl) {
57230         // then layout needs constructing..
57231         layout = Roo.factory(layout, Roo);
57232     }
57233     */
57234     
57235     
57236     Roo.NestedLayoutPanel.superclass.constructor.call(this, layout.getEl(), config);
57237     
57238     layout.monitorWindowResize = false; // turn off autosizing
57239     this.layout = layout;
57240     this.layout.getEl().addClass("x-layout-nested-layout");
57241     
57242     
57243     
57244     
57245 };
57246
57247 Roo.extend(Roo.NestedLayoutPanel, Roo.ContentPanel, {
57248
57249     setSize : function(width, height){
57250         if(!this.ignoreResize(width, height)){
57251             var size = this.adjustForComponents(width, height);
57252             var el = this.layout.getEl();
57253             el.setSize(size.width, size.height);
57254             var touch = el.dom.offsetWidth;
57255             this.layout.layout();
57256             // ie requires a double layout on the first pass
57257             if(Roo.isIE && !this.initialized){
57258                 this.initialized = true;
57259                 this.layout.layout();
57260             }
57261         }
57262     },
57263     
57264     // activate all subpanels if not currently active..
57265     
57266     setActiveState : function(active){
57267         this.active = active;
57268         if(!active){
57269             this.fireEvent("deactivate", this);
57270             return;
57271         }
57272         
57273         this.fireEvent("activate", this);
57274         // not sure if this should happen before or after..
57275         if (!this.layout) {
57276             return; // should not happen..
57277         }
57278         var reg = false;
57279         for (var r in this.layout.regions) {
57280             reg = this.layout.getRegion(r);
57281             if (reg.getActivePanel()) {
57282                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
57283                 reg.setActivePanel(reg.getActivePanel());
57284                 continue;
57285             }
57286             if (!reg.panels.length) {
57287                 continue;
57288             }
57289             reg.showPanel(reg.getPanel(0));
57290         }
57291         
57292         
57293         
57294         
57295     },
57296     
57297     /**
57298      * Returns the nested BorderLayout for this panel
57299      * @return {Roo.BorderLayout} 
57300      */
57301     getLayout : function(){
57302         return this.layout;
57303     },
57304     
57305      /**
57306      * Adds a xtype elements to the layout of the nested panel
57307      * <pre><code>
57308
57309 panel.addxtype({
57310        xtype : 'ContentPanel',
57311        region: 'west',
57312        items: [ .... ]
57313    }
57314 );
57315
57316 panel.addxtype({
57317         xtype : 'NestedLayoutPanel',
57318         region: 'west',
57319         layout: {
57320            center: { },
57321            west: { }   
57322         },
57323         items : [ ... list of content panels or nested layout panels.. ]
57324    }
57325 );
57326 </code></pre>
57327      * @param {Object} cfg Xtype definition of item to add.
57328      */
57329     addxtype : function(cfg) {
57330         return this.layout.addxtype(cfg);
57331     
57332     }
57333 });
57334
57335 Roo.ScrollPanel = function(el, config, content){
57336     config = config || {};
57337     config.fitToFrame = true;
57338     Roo.ScrollPanel.superclass.constructor.call(this, el, config, content);
57339     
57340     this.el.dom.style.overflow = "hidden";
57341     var wrap = this.el.wrap({cls: "x-scroller x-layout-inactive-content"});
57342     this.el.removeClass("x-layout-inactive-content");
57343     this.el.on("mousewheel", this.onWheel, this);
57344
57345     var up = wrap.createChild({cls: "x-scroller-up", html: "&#160;"}, this.el.dom);
57346     var down = wrap.createChild({cls: "x-scroller-down", html: "&#160;"});
57347     up.unselectable(); down.unselectable();
57348     up.on("click", this.scrollUp, this);
57349     down.on("click", this.scrollDown, this);
57350     up.addClassOnOver("x-scroller-btn-over");
57351     down.addClassOnOver("x-scroller-btn-over");
57352     up.addClassOnClick("x-scroller-btn-click");
57353     down.addClassOnClick("x-scroller-btn-click");
57354     this.adjustments = [0, -(up.getHeight() + down.getHeight())];
57355
57356     this.resizeEl = this.el;
57357     this.el = wrap; this.up = up; this.down = down;
57358 };
57359
57360 Roo.extend(Roo.ScrollPanel, Roo.ContentPanel, {
57361     increment : 100,
57362     wheelIncrement : 5,
57363     scrollUp : function(){
57364         this.resizeEl.scroll("up", this.increment, {callback: this.afterScroll, scope: this});
57365     },
57366
57367     scrollDown : function(){
57368         this.resizeEl.scroll("down", this.increment, {callback: this.afterScroll, scope: this});
57369     },
57370
57371     afterScroll : function(){
57372         var el = this.resizeEl;
57373         var t = el.dom.scrollTop, h = el.dom.scrollHeight, ch = el.dom.clientHeight;
57374         this.up[t == 0 ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57375         this.down[h - t <= ch ? "addClass" : "removeClass"]("x-scroller-btn-disabled");
57376     },
57377
57378     setSize : function(){
57379         Roo.ScrollPanel.superclass.setSize.apply(this, arguments);
57380         this.afterScroll();
57381     },
57382
57383     onWheel : function(e){
57384         var d = e.getWheelDelta();
57385         this.resizeEl.dom.scrollTop -= (d*this.wheelIncrement);
57386         this.afterScroll();
57387         e.stopEvent();
57388     },
57389
57390     setContent : function(content, loadScripts){
57391         this.resizeEl.update(content, loadScripts);
57392     }
57393
57394 });
57395
57396
57397
57398 /**
57399  * @class Roo.TreePanel
57400  * @extends Roo.ContentPanel
57401  * Treepanel component
57402  * 
57403  * @constructor
57404  * Create a new TreePanel. - defaults to fit/scoll contents.
57405  * @param {String/Object} config A string to set only the panel's title, or a config object
57406  */
57407 Roo.TreePanel = function(config){
57408     var el = config.el;
57409     var tree = config.tree;
57410     delete config.tree; 
57411     delete config.el; // hopefull!
57412     
57413     // wrapper for IE7 strict & safari scroll issue
57414     
57415     var treeEl = el.createChild();
57416     config.resizeEl = treeEl;
57417     
57418     
57419     
57420     Roo.TreePanel.superclass.constructor.call(this, el, config);
57421  
57422  
57423     this.tree = new Roo.tree.TreePanel(treeEl , tree);
57424     //console.log(tree);
57425     this.on('activate', function()
57426     {
57427         if (this.tree.rendered) {
57428             return;
57429         }
57430         //console.log('render tree');
57431         this.tree.render();
57432     });
57433     // this should not be needed.. - it's actually the 'el' that resizes?
57434     // actuall it breaks the containerScroll - dragging nodes auto scroll at top
57435     
57436     //this.on('resize',  function (cp, w, h) {
57437     //        this.tree.innerCt.setWidth(w);
57438     //        this.tree.innerCt.setHeight(h);
57439     //        //this.tree.innerCt.setStyle('overflow-y', 'auto');
57440     //});
57441
57442         
57443     
57444 };
57445
57446 Roo.extend(Roo.TreePanel, Roo.ContentPanel, {   
57447     fitToFrame : true,
57448     autoScroll : true,
57449     /*
57450      * @cfg {Roo.tree.TreePanel} tree [required] The tree TreePanel, with config etc.
57451      */
57452     tree : false
57453
57454 });
57455
57456
57457
57458
57459
57460
57461
57462
57463
57464
57465
57466 /*
57467  * Based on:
57468  * Ext JS Library 1.1.1
57469  * Copyright(c) 2006-2007, Ext JS, LLC.
57470  *
57471  * Originally Released Under LGPL - original licence link has changed is not relivant.
57472  *
57473  * Fork - LGPL
57474  * <script type="text/javascript">
57475  */
57476  
57477
57478 /**
57479  * @class Roo.ReaderLayout
57480  * @extends Roo.BorderLayout
57481  * This is a pre-built layout that represents a classic, 5-pane application.  It consists of a header, a primary
57482  * center region containing two nested regions (a top one for a list view and one for item preview below),
57483  * and regions on either side that can be used for navigation, application commands, informational displays, etc.
57484  * The setup and configuration work exactly the same as it does for a {@link Roo.BorderLayout} - this class simply
57485  * expedites the setup of the overall layout and regions for this common application style.
57486  * Example:
57487  <pre><code>
57488 var reader = new Roo.ReaderLayout();
57489 var CP = Roo.ContentPanel;  // shortcut for adding
57490
57491 reader.beginUpdate();
57492 reader.add("north", new CP("north", "North"));
57493 reader.add("west", new CP("west", {title: "West"}));
57494 reader.add("east", new CP("east", {title: "East"}));
57495
57496 reader.regions.listView.add(new CP("listView", "List"));
57497 reader.regions.preview.add(new CP("preview", "Preview"));
57498 reader.endUpdate();
57499 </code></pre>
57500 * @constructor
57501 * Create a new ReaderLayout
57502 * @param {Object} config Configuration options
57503 * @param {String/HTMLElement/Element} container (optional) The container this layout is bound to (defaults to
57504 * document.body if omitted)
57505 */
57506 Roo.ReaderLayout = function(config, renderTo){
57507     var c = config || {size:{}};
57508     Roo.ReaderLayout.superclass.constructor.call(this, renderTo || document.body, {
57509         north: c.north !== false ? Roo.apply({
57510             split:false,
57511             initialSize: 32,
57512             titlebar: false
57513         }, c.north) : false,
57514         west: c.west !== false ? Roo.apply({
57515             split:true,
57516             initialSize: 200,
57517             minSize: 175,
57518             maxSize: 400,
57519             titlebar: true,
57520             collapsible: true,
57521             animate: true,
57522             margins:{left:5,right:0,bottom:5,top:5},
57523             cmargins:{left:5,right:5,bottom:5,top:5}
57524         }, c.west) : false,
57525         east: c.east !== false ? Roo.apply({
57526             split:true,
57527             initialSize: 200,
57528             minSize: 175,
57529             maxSize: 400,
57530             titlebar: true,
57531             collapsible: true,
57532             animate: true,
57533             margins:{left:0,right:5,bottom:5,top:5},
57534             cmargins:{left:5,right:5,bottom:5,top:5}
57535         }, c.east) : false,
57536         center: Roo.apply({
57537             tabPosition: 'top',
57538             autoScroll:false,
57539             closeOnTab: true,
57540             titlebar:false,
57541             margins:{left:c.west!==false ? 0 : 5,right:c.east!==false ? 0 : 5,bottom:5,top:2}
57542         }, c.center)
57543     });
57544
57545     this.el.addClass('x-reader');
57546
57547     this.beginUpdate();
57548
57549     var inner = new Roo.BorderLayout(Roo.get(document.body).createChild(), {
57550         south: c.preview !== false ? Roo.apply({
57551             split:true,
57552             initialSize: 200,
57553             minSize: 100,
57554             autoScroll:true,
57555             collapsible:true,
57556             titlebar: true,
57557             cmargins:{top:5,left:0, right:0, bottom:0}
57558         }, c.preview) : false,
57559         center: Roo.apply({
57560             autoScroll:false,
57561             titlebar:false,
57562             minHeight:200
57563         }, c.listView)
57564     });
57565     this.add('center', new Roo.NestedLayoutPanel(inner,
57566             Roo.apply({title: c.mainTitle || '',tabTip:''},c.innerPanelCfg)));
57567
57568     this.endUpdate();
57569
57570     this.regions.preview = inner.getRegion('south');
57571     this.regions.listView = inner.getRegion('center');
57572 };
57573
57574 Roo.extend(Roo.ReaderLayout, Roo.BorderLayout);/*
57575  * Based on:
57576  * Ext JS Library 1.1.1
57577  * Copyright(c) 2006-2007, Ext JS, LLC.
57578  *
57579  * Originally Released Under LGPL - original licence link has changed is not relivant.
57580  *
57581  * Fork - LGPL
57582  * <script type="text/javascript">
57583  */
57584  
57585 /**
57586  * @class Roo.grid.Grid
57587  * @extends Roo.util.Observable
57588  * This class represents the primary interface of a component based grid control.
57589  * <br><br>Usage:<pre><code>
57590  var grid = new Roo.grid.Grid("my-container-id", {
57591      ds: myDataStore,
57592      cm: myColModel,
57593      selModel: mySelectionModel,
57594      autoSizeColumns: true,
57595      monitorWindowResize: false,
57596      trackMouseOver: true
57597  });
57598  // set any options
57599  grid.render();
57600  * </code></pre>
57601  * <b>Common Problems:</b><br/>
57602  * - Grid does not resize properly when going smaller: Setting overflow hidden on the container
57603  * element will correct this<br/>
57604  * - If you get el.style[camel]= NaNpx or -2px or something related, be certain you have given your container element
57605  * dimensions. The grid adapts to your container's size, if your container has no size defined then the results
57606  * are unpredictable.<br/>
57607  * - Do not render the grid into an element with display:none. Try using visibility:hidden. Otherwise there is no way for the
57608  * grid to calculate dimensions/offsets.<br/>
57609   * @constructor
57610  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
57611  * The container MUST have some type of size defined for the grid to fill. The container will be
57612  * automatically set to position relative if it isn't already.
57613  * @param {Object} config A config object that sets properties on this grid.
57614  */
57615 Roo.grid.Grid = function(container, config){
57616         // initialize the container
57617         this.container = Roo.get(container);
57618         this.container.update("");
57619         this.container.setStyle("overflow", "hidden");
57620     this.container.addClass('x-grid-container');
57621
57622     this.id = this.container.id;
57623
57624     Roo.apply(this, config);
57625     // check and correct shorthanded configs
57626     if(this.ds){
57627         this.dataSource = this.ds;
57628         delete this.ds;
57629     }
57630     if(this.cm){
57631         this.colModel = this.cm;
57632         delete this.cm;
57633     }
57634     if(this.sm){
57635         this.selModel = this.sm;
57636         delete this.sm;
57637     }
57638
57639     if (this.selModel) {
57640         this.selModel = Roo.factory(this.selModel, Roo.grid);
57641         this.sm = this.selModel;
57642         this.sm.xmodule = this.xmodule || false;
57643     }
57644     if (typeof(this.colModel.config) == 'undefined') {
57645         this.colModel = new Roo.grid.ColumnModel(this.colModel);
57646         this.cm = this.colModel;
57647         this.cm.xmodule = this.xmodule || false;
57648     }
57649     if (this.dataSource) {
57650         this.dataSource= Roo.factory(this.dataSource, Roo.data);
57651         this.ds = this.dataSource;
57652         this.ds.xmodule = this.xmodule || false;
57653          
57654     }
57655     
57656     
57657     
57658     if(this.width){
57659         this.container.setWidth(this.width);
57660     }
57661
57662     if(this.height){
57663         this.container.setHeight(this.height);
57664     }
57665     /** @private */
57666         this.addEvents({
57667         // raw events
57668         /**
57669          * @event click
57670          * The raw click event for the entire grid.
57671          * @param {Roo.EventObject} e
57672          */
57673         "click" : true,
57674         /**
57675          * @event dblclick
57676          * The raw dblclick event for the entire grid.
57677          * @param {Roo.EventObject} e
57678          */
57679         "dblclick" : true,
57680         /**
57681          * @event contextmenu
57682          * The raw contextmenu event for the entire grid.
57683          * @param {Roo.EventObject} e
57684          */
57685         "contextmenu" : true,
57686         /**
57687          * @event mousedown
57688          * The raw mousedown event for the entire grid.
57689          * @param {Roo.EventObject} e
57690          */
57691         "mousedown" : true,
57692         /**
57693          * @event mouseup
57694          * The raw mouseup event for the entire grid.
57695          * @param {Roo.EventObject} e
57696          */
57697         "mouseup" : true,
57698         /**
57699          * @event mouseover
57700          * The raw mouseover event for the entire grid.
57701          * @param {Roo.EventObject} e
57702          */
57703         "mouseover" : true,
57704         /**
57705          * @event mouseout
57706          * The raw mouseout event for the entire grid.
57707          * @param {Roo.EventObject} e
57708          */
57709         "mouseout" : true,
57710         /**
57711          * @event keypress
57712          * The raw keypress event for the entire grid.
57713          * @param {Roo.EventObject} e
57714          */
57715         "keypress" : true,
57716         /**
57717          * @event keydown
57718          * The raw keydown event for the entire grid.
57719          * @param {Roo.EventObject} e
57720          */
57721         "keydown" : true,
57722
57723         // custom events
57724
57725         /**
57726          * @event cellclick
57727          * Fires when a cell is clicked
57728          * @param {Grid} this
57729          * @param {Number} rowIndex
57730          * @param {Number} columnIndex
57731          * @param {Roo.EventObject} e
57732          */
57733         "cellclick" : true,
57734         /**
57735          * @event celldblclick
57736          * Fires when a cell is double clicked
57737          * @param {Grid} this
57738          * @param {Number} rowIndex
57739          * @param {Number} columnIndex
57740          * @param {Roo.EventObject} e
57741          */
57742         "celldblclick" : true,
57743         /**
57744          * @event rowclick
57745          * Fires when a row is clicked
57746          * @param {Grid} this
57747          * @param {Number} rowIndex
57748          * @param {Roo.EventObject} e
57749          */
57750         "rowclick" : true,
57751         /**
57752          * @event rowdblclick
57753          * Fires when a row is double clicked
57754          * @param {Grid} this
57755          * @param {Number} rowIndex
57756          * @param {Roo.EventObject} e
57757          */
57758         "rowdblclick" : true,
57759         /**
57760          * @event headerclick
57761          * Fires when a header is clicked
57762          * @param {Grid} this
57763          * @param {Number} columnIndex
57764          * @param {Roo.EventObject} e
57765          */
57766         "headerclick" : true,
57767         /**
57768          * @event headerdblclick
57769          * Fires when a header cell is double clicked
57770          * @param {Grid} this
57771          * @param {Number} columnIndex
57772          * @param {Roo.EventObject} e
57773          */
57774         "headerdblclick" : true,
57775         /**
57776          * @event rowcontextmenu
57777          * Fires when a row is right clicked
57778          * @param {Grid} this
57779          * @param {Number} rowIndex
57780          * @param {Roo.EventObject} e
57781          */
57782         "rowcontextmenu" : true,
57783         /**
57784          * @event cellcontextmenu
57785          * Fires when a cell is right clicked
57786          * @param {Grid} this
57787          * @param {Number} rowIndex
57788          * @param {Number} cellIndex
57789          * @param {Roo.EventObject} e
57790          */
57791          "cellcontextmenu" : true,
57792         /**
57793          * @event headercontextmenu
57794          * Fires when a header is right clicked
57795          * @param {Grid} this
57796          * @param {Number} columnIndex
57797          * @param {Roo.EventObject} e
57798          */
57799         "headercontextmenu" : true,
57800         /**
57801          * @event bodyscroll
57802          * Fires when the body element is scrolled
57803          * @param {Number} scrollLeft
57804          * @param {Number} scrollTop
57805          */
57806         "bodyscroll" : true,
57807         /**
57808          * @event columnresize
57809          * Fires when the user resizes a column
57810          * @param {Number} columnIndex
57811          * @param {Number} newSize
57812          */
57813         "columnresize" : true,
57814         /**
57815          * @event columnmove
57816          * Fires when the user moves a column
57817          * @param {Number} oldIndex
57818          * @param {Number} newIndex
57819          */
57820         "columnmove" : true,
57821         /**
57822          * @event startdrag
57823          * Fires when row(s) start being dragged
57824          * @param {Grid} this
57825          * @param {Roo.GridDD} dd The drag drop object
57826          * @param {event} e The raw browser event
57827          */
57828         "startdrag" : true,
57829         /**
57830          * @event enddrag
57831          * Fires when a drag operation is complete
57832          * @param {Grid} this
57833          * @param {Roo.GridDD} dd The drag drop object
57834          * @param {event} e The raw browser event
57835          */
57836         "enddrag" : true,
57837         /**
57838          * @event dragdrop
57839          * Fires when dragged row(s) are dropped on a valid DD target
57840          * @param {Grid} this
57841          * @param {Roo.GridDD} dd The drag drop object
57842          * @param {String} targetId The target drag drop object
57843          * @param {event} e The raw browser event
57844          */
57845         "dragdrop" : true,
57846         /**
57847          * @event dragover
57848          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
57849          * @param {Grid} this
57850          * @param {Roo.GridDD} dd The drag drop object
57851          * @param {String} targetId The target drag drop object
57852          * @param {event} e The raw browser event
57853          */
57854         "dragover" : true,
57855         /**
57856          * @event dragenter
57857          *  Fires when the dragged row(s) first cross another DD target while being dragged
57858          * @param {Grid} this
57859          * @param {Roo.GridDD} dd The drag drop object
57860          * @param {String} targetId The target drag drop object
57861          * @param {event} e The raw browser event
57862          */
57863         "dragenter" : true,
57864         /**
57865          * @event dragout
57866          * Fires when the dragged row(s) leave another DD target while being dragged
57867          * @param {Grid} this
57868          * @param {Roo.GridDD} dd The drag drop object
57869          * @param {String} targetId The target drag drop object
57870          * @param {event} e The raw browser event
57871          */
57872         "dragout" : true,
57873         /**
57874          * @event rowclass
57875          * Fires when a row is rendered, so you can change add a style to it.
57876          * @param {GridView} gridview   The grid view
57877          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
57878          */
57879         'rowclass' : true,
57880
57881         /**
57882          * @event render
57883          * Fires when the grid is rendered
57884          * @param {Grid} grid
57885          */
57886         'render' : true
57887     });
57888
57889     Roo.grid.Grid.superclass.constructor.call(this);
57890 };
57891 Roo.extend(Roo.grid.Grid, Roo.util.Observable, {
57892     
57893     /**
57894          * @cfg {Roo.grid.AbstractSelectionModel} sm The selection Model (default = Roo.grid.RowSelectionModel)
57895          */
57896         /**
57897          * @cfg {Roo.grid.GridView} view  The view that renders the grid (default = Roo.grid.GridView)
57898          */
57899         /**
57900          * @cfg {Roo.grid.ColumnModel} cm[] The columns of the grid
57901          */
57902         /**
57903          * @cfg {Roo.grid.Store} ds The data store for the grid
57904          */
57905         /**
57906          * @cfg {Roo.Toolbar} toolbar a toolbar for buttons etc.
57907          */
57908         /**
57909      * @cfg {String} ddGroup - drag drop group.
57910      */
57911       /**
57912      * @cfg {String} dragGroup - drag group (?? not sure if needed.)
57913      */
57914
57915     /**
57916      * @cfg {Number} minColumnWidth The minimum width a column can be resized to. Default is 25.
57917      */
57918     minColumnWidth : 25,
57919
57920     /**
57921      * @cfg {Boolean} autoSizeColumns True to automatically resize the columns to fit their content
57922      * <b>on initial render.</b> It is more efficient to explicitly size the columns
57923      * through the ColumnModel's {@link Roo.grid.ColumnModel#width} config option.  Default is false.
57924      */
57925     autoSizeColumns : false,
57926
57927     /**
57928      * @cfg {Boolean} autoSizeHeaders True to measure headers with column data when auto sizing columns. Default is true.
57929      */
57930     autoSizeHeaders : true,
57931
57932     /**
57933      * @cfg {Boolean} monitorWindowResize True to autoSize the grid when the window resizes. Default is true.
57934      */
57935     monitorWindowResize : true,
57936
57937     /**
57938      * @cfg {Boolean} maxRowsToMeasure If autoSizeColumns is on, maxRowsToMeasure can be used to limit the number of
57939      * rows measured to get a columns size. Default is 0 (all rows).
57940      */
57941     maxRowsToMeasure : 0,
57942
57943     /**
57944      * @cfg {Boolean} trackMouseOver True to highlight rows when the mouse is over. Default is true.
57945      */
57946     trackMouseOver : true,
57947
57948     /**
57949     * @cfg {Boolean} enableDrag  True to enable drag of rows. Default is false. (double check if this is needed?)
57950     */
57951       /**
57952     * @cfg {Boolean} enableDrop  True to enable drop of elements. Default is false. (double check if this is needed?)
57953     */
57954     
57955     /**
57956     * @cfg {Boolean} enableDragDrop True to enable drag and drop of rows. Default is false.
57957     */
57958     enableDragDrop : false,
57959     
57960     /**
57961     * @cfg {Boolean} enableColumnMove True to enable drag and drop reorder of columns. Default is true.
57962     */
57963     enableColumnMove : true,
57964     
57965     /**
57966     * @cfg {Boolean} enableColumnHide True to enable hiding of columns with the header context menu. Default is true.
57967     */
57968     enableColumnHide : true,
57969     
57970     /**
57971     * @cfg {Boolean} enableRowHeightSync True to manually sync row heights across locked and not locked rows. Default is false.
57972     */
57973     enableRowHeightSync : false,
57974     
57975     /**
57976     * @cfg {Boolean} stripeRows True to stripe the rows.  Default is true.
57977     */
57978     stripeRows : true,
57979     
57980     /**
57981     * @cfg {Boolean} autoHeight True to fit the height of the grid container to the height of the data. Default is false.
57982     */
57983     autoHeight : false,
57984
57985     /**
57986      * @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.
57987      */
57988     autoExpandColumn : false,
57989
57990     /**
57991     * @cfg {Number} autoExpandMin The minimum width the autoExpandColumn can have (if enabled).
57992     * Default is 50.
57993     */
57994     autoExpandMin : 50,
57995
57996     /**
57997     * @cfg {Number} autoExpandMax The maximum width the autoExpandColumn can have (if enabled). Default is 1000.
57998     */
57999     autoExpandMax : 1000,
58000
58001     /**
58002     * @cfg {Object} view The {@link Roo.grid.GridView} used by the grid. This can be set before a call to render().
58003     */
58004     view : null,
58005
58006     /**
58007     * @cfg {Object} loadMask An {@link Roo.LoadMask} config or true to mask the grid while loading. Default is false.
58008     */
58009     loadMask : false,
58010     /**
58011     * @cfg {Roo.dd.DropTarget} dropTarget An {@link Roo.dd.DropTarget} config
58012     */
58013     dropTarget: false,
58014     
58015    
58016     
58017     // private
58018     rendered : false,
58019
58020     /**
58021     * @cfg {Boolean} autoWidth True to set the grid's width to the default total width of the grid's columns instead
58022     * of a fixed width. Default is false.
58023     */
58024     /**
58025     * @cfg {Number} maxHeight Sets the maximum height of the grid - ignored if autoHeight is not on.
58026     */
58027     
58028     
58029     /**
58030     * @cfg {String} ddText Configures the text is the drag proxy (defaults to "%0 selected row(s)").
58031     * %0 is replaced with the number of selected rows.
58032     */
58033     ddText : "{0} selected row{1}",
58034     
58035     
58036     /**
58037      * Called once after all setup has been completed and the grid is ready to be rendered.
58038      * @return {Roo.grid.Grid} this
58039      */
58040     render : function()
58041     {
58042         var c = this.container;
58043         // try to detect autoHeight/width mode
58044         if((!c.dom.offsetHeight || c.dom.offsetHeight < 20) || c.getStyle("height") == "auto"){
58045             this.autoHeight = true;
58046         }
58047         var view = this.getView();
58048         view.init(this);
58049
58050         c.on("click", this.onClick, this);
58051         c.on("dblclick", this.onDblClick, this);
58052         c.on("contextmenu", this.onContextMenu, this);
58053         c.on("keydown", this.onKeyDown, this);
58054         if (Roo.isTouch) {
58055             c.on("touchstart", this.onTouchStart, this);
58056         }
58057
58058         this.relayEvents(c, ["mousedown","mouseup","mouseover","mouseout","keypress"]);
58059
58060         this.getSelectionModel().init(this);
58061
58062         view.render();
58063
58064         if(this.loadMask){
58065             this.loadMask = new Roo.LoadMask(this.container,
58066                     Roo.apply({store:this.dataSource}, this.loadMask));
58067         }
58068         
58069         
58070         if (this.toolbar && this.toolbar.xtype) {
58071             this.toolbar.container = this.getView().getHeaderPanel(true);
58072             this.toolbar = new Roo.Toolbar(this.toolbar);
58073         }
58074         if (this.footer && this.footer.xtype) {
58075             this.footer.dataSource = this.getDataSource();
58076             this.footer.container = this.getView().getFooterPanel(true);
58077             this.footer = Roo.factory(this.footer, Roo);
58078         }
58079         if (this.dropTarget && this.dropTarget.xtype) {
58080             delete this.dropTarget.xtype;
58081             this.dropTarget =  new Roo.dd.DropTarget(this.getView().mainBody, this.dropTarget);
58082         }
58083         
58084         
58085         this.rendered = true;
58086         this.fireEvent('render', this);
58087         return this;
58088     },
58089
58090     /**
58091      * Reconfigures the grid to use a different Store and Column Model.
58092      * The View will be bound to the new objects and refreshed.
58093      * @param {Roo.data.Store} dataSource The new {@link Roo.data.Store} object
58094      * @param {Roo.grid.ColumnModel} The new {@link Roo.grid.ColumnModel} object
58095      */
58096     reconfigure : function(dataSource, colModel){
58097         if(this.loadMask){
58098             this.loadMask.destroy();
58099             this.loadMask = new Roo.LoadMask(this.container,
58100                     Roo.apply({store:dataSource}, this.loadMask));
58101         }
58102         this.view.bind(dataSource, colModel);
58103         this.dataSource = dataSource;
58104         this.colModel = colModel;
58105         this.view.refresh(true);
58106     },
58107     /**
58108      * addColumns
58109      * Add's a column, default at the end..
58110      
58111      * @param {int} position to add (default end)
58112      * @param {Array} of objects of column configuration see {@link Roo.grid.ColumnModel} 
58113      */
58114     addColumns : function(pos, ar)
58115     {
58116         
58117         for (var i =0;i< ar.length;i++) {
58118             var cfg = ar[i];
58119             cfg.id = typeof(cfg.id) == 'undefined' ? Roo.id() : cfg.id; // don't normally use this..
58120             this.cm.lookup[cfg.id] = cfg;
58121         }
58122         
58123         
58124         if (typeof(pos) == 'undefined' || pos >= this.cm.config.length) {
58125             pos = this.cm.config.length; //this.cm.config.push(cfg);
58126         } 
58127         pos = Math.max(0,pos);
58128         ar.unshift(0);
58129         ar.unshift(pos);
58130         this.cm.config.splice.apply(this.cm.config, ar);
58131         
58132         
58133         
58134         this.view.generateRules(this.cm);
58135         this.view.refresh(true);
58136         
58137     },
58138     
58139     
58140     
58141     
58142     // private
58143     onKeyDown : function(e){
58144         this.fireEvent("keydown", e);
58145     },
58146
58147     /**
58148      * Destroy this grid.
58149      * @param {Boolean} removeEl True to remove the element
58150      */
58151     destroy : function(removeEl, keepListeners){
58152         if(this.loadMask){
58153             this.loadMask.destroy();
58154         }
58155         var c = this.container;
58156         c.removeAllListeners();
58157         this.view.destroy();
58158         this.colModel.purgeListeners();
58159         if(!keepListeners){
58160             this.purgeListeners();
58161         }
58162         c.update("");
58163         if(removeEl === true){
58164             c.remove();
58165         }
58166     },
58167
58168     // private
58169     processEvent : function(name, e){
58170         // does this fire select???
58171         //Roo.log('grid:processEvent '  + name);
58172         
58173         if (name != 'touchstart' ) {
58174             this.fireEvent(name, e);    
58175         }
58176         
58177         var t = e.getTarget();
58178         var v = this.view;
58179         var header = v.findHeaderIndex(t);
58180         if(header !== false){
58181             var ename = name == 'touchstart' ? 'click' : name;
58182              
58183             this.fireEvent("header" + ename, this, header, e);
58184         }else{
58185             var row = v.findRowIndex(t);
58186             var cell = v.findCellIndex(t);
58187             if (name == 'touchstart') {
58188                 // first touch is always a click.
58189                 // hopefull this happens after selection is updated.?
58190                 name = false;
58191                 
58192                 if (typeof(this.selModel.getSelectedCell) != 'undefined') {
58193                     var cs = this.selModel.getSelectedCell();
58194                     if (row == cs[0] && cell == cs[1]){
58195                         name = 'dblclick';
58196                     }
58197                 }
58198                 if (typeof(this.selModel.getSelections) != 'undefined') {
58199                     var cs = this.selModel.getSelections();
58200                     var ds = this.dataSource;
58201                     if (cs.length == 1 && ds.getAt(row) == cs[0]){
58202                         name = 'dblclick';
58203                     }
58204                 }
58205                 if (!name) {
58206                     return;
58207                 }
58208             }
58209             
58210             
58211             if(row !== false){
58212                 this.fireEvent("row" + name, this, row, e);
58213                 if(cell !== false){
58214                     this.fireEvent("cell" + name, this, row, cell, e);
58215                 }
58216             }
58217         }
58218     },
58219
58220     // private
58221     onClick : function(e){
58222         this.processEvent("click", e);
58223     },
58224    // private
58225     onTouchStart : function(e){
58226         this.processEvent("touchstart", e);
58227     },
58228
58229     // private
58230     onContextMenu : function(e, t){
58231         this.processEvent("contextmenu", e);
58232     },
58233
58234     // private
58235     onDblClick : function(e){
58236         this.processEvent("dblclick", e);
58237     },
58238
58239     // private
58240     walkCells : function(row, col, step, fn, scope){
58241         var cm = this.colModel, clen = cm.getColumnCount();
58242         var ds = this.dataSource, rlen = ds.getCount(), first = true;
58243         if(step < 0){
58244             if(col < 0){
58245                 row--;
58246                 first = false;
58247             }
58248             while(row >= 0){
58249                 if(!first){
58250                     col = clen-1;
58251                 }
58252                 first = false;
58253                 while(col >= 0){
58254                     if(fn.call(scope || this, row, col, cm) === true){
58255                         return [row, col];
58256                     }
58257                     col--;
58258                 }
58259                 row--;
58260             }
58261         } else {
58262             if(col >= clen){
58263                 row++;
58264                 first = false;
58265             }
58266             while(row < rlen){
58267                 if(!first){
58268                     col = 0;
58269                 }
58270                 first = false;
58271                 while(col < clen){
58272                     if(fn.call(scope || this, row, col, cm) === true){
58273                         return [row, col];
58274                     }
58275                     col++;
58276                 }
58277                 row++;
58278             }
58279         }
58280         return null;
58281     },
58282
58283     // private
58284     getSelections : function(){
58285         return this.selModel.getSelections();
58286     },
58287
58288     /**
58289      * Causes the grid to manually recalculate its dimensions. Generally this is done automatically,
58290      * but if manual update is required this method will initiate it.
58291      */
58292     autoSize : function(){
58293         if(this.rendered){
58294             this.view.layout();
58295             if(this.view.adjustForScroll){
58296                 this.view.adjustForScroll();
58297             }
58298         }
58299     },
58300
58301     /**
58302      * Returns the grid's underlying element.
58303      * @return {Element} The element
58304      */
58305     getGridEl : function(){
58306         return this.container;
58307     },
58308
58309     // private for compatibility, overridden by editor grid
58310     stopEditing : function(){},
58311
58312     /**
58313      * Returns the grid's SelectionModel.
58314      * @return {SelectionModel}
58315      */
58316     getSelectionModel : function(){
58317         if(!this.selModel){
58318             this.selModel = new Roo.grid.RowSelectionModel();
58319         }
58320         return this.selModel;
58321     },
58322
58323     /**
58324      * Returns the grid's DataSource.
58325      * @return {DataSource}
58326      */
58327     getDataSource : function(){
58328         return this.dataSource;
58329     },
58330
58331     /**
58332      * Returns the grid's ColumnModel.
58333      * @return {ColumnModel}
58334      */
58335     getColumnModel : function(){
58336         return this.colModel;
58337     },
58338
58339     /**
58340      * Returns the grid's GridView object.
58341      * @return {GridView}
58342      */
58343     getView : function(){
58344         if(!this.view){
58345             this.view = new Roo.grid.GridView(this.viewConfig);
58346             this.relayEvents(this.view, [
58347                 "beforerowremoved", "beforerowsinserted",
58348                 "beforerefresh", "rowremoved",
58349                 "rowsinserted", "rowupdated" ,"refresh"
58350             ]);
58351         }
58352         return this.view;
58353     },
58354     /**
58355      * Called to get grid's drag proxy text, by default returns this.ddText.
58356      * Override this to put something different in the dragged text.
58357      * @return {String}
58358      */
58359     getDragDropText : function(){
58360         var count = this.selModel.getCount();
58361         return String.format(this.ddText, count, count == 1 ? '' : 's');
58362     }
58363 });
58364 /*
58365  * Based on:
58366  * Ext JS Library 1.1.1
58367  * Copyright(c) 2006-2007, Ext JS, LLC.
58368  *
58369  * Originally Released Under LGPL - original licence link has changed is not relivant.
58370  *
58371  * Fork - LGPL
58372  * <script type="text/javascript">
58373  */
58374  /**
58375  * @class Roo.grid.AbstractGridView
58376  * @extends Roo.util.Observable
58377  * @abstract
58378  * Abstract base class for grid Views
58379  * @constructor
58380  */
58381 Roo.grid.AbstractGridView = function(){
58382         this.grid = null;
58383         
58384         this.events = {
58385             "beforerowremoved" : true,
58386             "beforerowsinserted" : true,
58387             "beforerefresh" : true,
58388             "rowremoved" : true,
58389             "rowsinserted" : true,
58390             "rowupdated" : true,
58391             "refresh" : true
58392         };
58393     Roo.grid.AbstractGridView.superclass.constructor.call(this);
58394 };
58395
58396 Roo.extend(Roo.grid.AbstractGridView, Roo.util.Observable, {
58397     rowClass : "x-grid-row",
58398     cellClass : "x-grid-cell",
58399     tdClass : "x-grid-td",
58400     hdClass : "x-grid-hd",
58401     splitClass : "x-grid-hd-split",
58402     
58403     init: function(grid){
58404         this.grid = grid;
58405                 var cid = this.grid.getGridEl().id;
58406         this.colSelector = "#" + cid + " ." + this.cellClass + "-";
58407         this.tdSelector = "#" + cid + " ." + this.tdClass + "-";
58408         this.hdSelector = "#" + cid + " ." + this.hdClass + "-";
58409         this.splitSelector = "#" + cid + " ." + this.splitClass + "-";
58410         },
58411         
58412     getColumnRenderers : function(){
58413         var renderers = [];
58414         var cm = this.grid.colModel;
58415         var colCount = cm.getColumnCount();
58416         for(var i = 0; i < colCount; i++){
58417             renderers[i] = cm.getRenderer(i);
58418         }
58419         return renderers;
58420     },
58421     
58422     getColumnIds : function(){
58423         var ids = [];
58424         var cm = this.grid.colModel;
58425         var colCount = cm.getColumnCount();
58426         for(var i = 0; i < colCount; i++){
58427             ids[i] = cm.getColumnId(i);
58428         }
58429         return ids;
58430     },
58431     
58432     getDataIndexes : function(){
58433         if(!this.indexMap){
58434             this.indexMap = this.buildIndexMap();
58435         }
58436         return this.indexMap.colToData;
58437     },
58438     
58439     getColumnIndexByDataIndex : function(dataIndex){
58440         if(!this.indexMap){
58441             this.indexMap = this.buildIndexMap();
58442         }
58443         return this.indexMap.dataToCol[dataIndex];
58444     },
58445     
58446     /**
58447      * Set a css style for a column dynamically. 
58448      * @param {Number} colIndex The index of the column
58449      * @param {String} name The css property name
58450      * @param {String} value The css value
58451      */
58452     setCSSStyle : function(colIndex, name, value){
58453         var selector = "#" + this.grid.id + " .x-grid-col-" + colIndex;
58454         Roo.util.CSS.updateRule(selector, name, value);
58455     },
58456     
58457     generateRules : function(cm){
58458         var ruleBuf = [], rulesId = this.grid.id + '-cssrules';
58459         Roo.util.CSS.removeStyleSheet(rulesId);
58460         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
58461             var cid = cm.getColumnId(i);
58462             ruleBuf.push(this.colSelector, cid, " {\n", cm.config[i].css, "}\n",
58463                          this.tdSelector, cid, " {\n}\n",
58464                          this.hdSelector, cid, " {\n}\n",
58465                          this.splitSelector, cid, " {\n}\n");
58466         }
58467         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
58468     }
58469 });/*
58470  * Based on:
58471  * Ext JS Library 1.1.1
58472  * Copyright(c) 2006-2007, Ext JS, LLC.
58473  *
58474  * Originally Released Under LGPL - original licence link has changed is not relivant.
58475  *
58476  * Fork - LGPL
58477  * <script type="text/javascript">
58478  */
58479
58480 // private
58481 // This is a support class used internally by the Grid components
58482 Roo.grid.HeaderDragZone = function(grid, hd, hd2){
58483     this.grid = grid;
58484     this.view = grid.getView();
58485     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58486     Roo.grid.HeaderDragZone.superclass.constructor.call(this, hd);
58487     if(hd2){
58488         this.setHandleElId(Roo.id(hd));
58489         this.setOuterHandleElId(Roo.id(hd2));
58490     }
58491     this.scroll = false;
58492 };
58493 Roo.extend(Roo.grid.HeaderDragZone, Roo.dd.DragZone, {
58494     maxDragWidth: 120,
58495     getDragData : function(e){
58496         var t = Roo.lib.Event.getTarget(e);
58497         var h = this.view.findHeaderCell(t);
58498         if(h){
58499             return {ddel: h.firstChild, header:h};
58500         }
58501         return false;
58502     },
58503
58504     onInitDrag : function(e){
58505         this.view.headersDisabled = true;
58506         var clone = this.dragData.ddel.cloneNode(true);
58507         clone.id = Roo.id();
58508         clone.style.width = Math.min(this.dragData.header.offsetWidth,this.maxDragWidth) + "px";
58509         this.proxy.update(clone);
58510         return true;
58511     },
58512
58513     afterValidDrop : function(){
58514         var v = this.view;
58515         setTimeout(function(){
58516             v.headersDisabled = false;
58517         }, 50);
58518     },
58519
58520     afterInvalidDrop : function(){
58521         var v = this.view;
58522         setTimeout(function(){
58523             v.headersDisabled = false;
58524         }, 50);
58525     }
58526 });
58527 /*
58528  * Based on:
58529  * Ext JS Library 1.1.1
58530  * Copyright(c) 2006-2007, Ext JS, LLC.
58531  *
58532  * Originally Released Under LGPL - original licence link has changed is not relivant.
58533  *
58534  * Fork - LGPL
58535  * <script type="text/javascript">
58536  */
58537 // private
58538 // This is a support class used internally by the Grid components
58539 Roo.grid.HeaderDropZone = function(grid, hd, hd2){
58540     this.grid = grid;
58541     this.view = grid.getView();
58542     // split the proxies so they don't interfere with mouse events
58543     this.proxyTop = Roo.DomHelper.append(document.body, {
58544         cls:"col-move-top", html:"&#160;"
58545     }, true);
58546     this.proxyBottom = Roo.DomHelper.append(document.body, {
58547         cls:"col-move-bottom", html:"&#160;"
58548     }, true);
58549     this.proxyTop.hide = this.proxyBottom.hide = function(){
58550         this.setLeftTop(-100,-100);
58551         this.setStyle("visibility", "hidden");
58552     };
58553     this.ddGroup = "gridHeader" + this.grid.getGridEl().id;
58554     // temporarily disabled
58555     //Roo.dd.ScrollManager.register(this.view.scroller.dom);
58556     Roo.grid.HeaderDropZone.superclass.constructor.call(this, grid.getGridEl().dom);
58557 };
58558 Roo.extend(Roo.grid.HeaderDropZone, Roo.dd.DropZone, {
58559     proxyOffsets : [-4, -9],
58560     fly: Roo.Element.fly,
58561
58562     getTargetFromEvent : function(e){
58563         var t = Roo.lib.Event.getTarget(e);
58564         var cindex = this.view.findCellIndex(t);
58565         if(cindex !== false){
58566             return this.view.getHeaderCell(cindex);
58567         }
58568         return null;
58569     },
58570
58571     nextVisible : function(h){
58572         var v = this.view, cm = this.grid.colModel;
58573         h = h.nextSibling;
58574         while(h){
58575             if(!cm.isHidden(v.getCellIndex(h))){
58576                 return h;
58577             }
58578             h = h.nextSibling;
58579         }
58580         return null;
58581     },
58582
58583     prevVisible : function(h){
58584         var v = this.view, cm = this.grid.colModel;
58585         h = h.prevSibling;
58586         while(h){
58587             if(!cm.isHidden(v.getCellIndex(h))){
58588                 return h;
58589             }
58590             h = h.prevSibling;
58591         }
58592         return null;
58593     },
58594
58595     positionIndicator : function(h, n, e){
58596         var x = Roo.lib.Event.getPageX(e);
58597         var r = Roo.lib.Dom.getRegion(n.firstChild);
58598         var px, pt, py = r.top + this.proxyOffsets[1];
58599         if((r.right - x) <= (r.right-r.left)/2){
58600             px = r.right+this.view.borderWidth;
58601             pt = "after";
58602         }else{
58603             px = r.left;
58604             pt = "before";
58605         }
58606         var oldIndex = this.view.getCellIndex(h);
58607         var newIndex = this.view.getCellIndex(n);
58608
58609         if(this.grid.colModel.isFixed(newIndex)){
58610             return false;
58611         }
58612
58613         var locked = this.grid.colModel.isLocked(newIndex);
58614
58615         if(pt == "after"){
58616             newIndex++;
58617         }
58618         if(oldIndex < newIndex){
58619             newIndex--;
58620         }
58621         if(oldIndex == newIndex && (locked == this.grid.colModel.isLocked(oldIndex))){
58622             return false;
58623         }
58624         px +=  this.proxyOffsets[0];
58625         this.proxyTop.setLeftTop(px, py);
58626         this.proxyTop.show();
58627         if(!this.bottomOffset){
58628             this.bottomOffset = this.view.mainHd.getHeight();
58629         }
58630         this.proxyBottom.setLeftTop(px, py+this.proxyTop.dom.offsetHeight+this.bottomOffset);
58631         this.proxyBottom.show();
58632         return pt;
58633     },
58634
58635     onNodeEnter : function(n, dd, e, data){
58636         if(data.header != n){
58637             this.positionIndicator(data.header, n, e);
58638         }
58639     },
58640
58641     onNodeOver : function(n, dd, e, data){
58642         var result = false;
58643         if(data.header != n){
58644             result = this.positionIndicator(data.header, n, e);
58645         }
58646         if(!result){
58647             this.proxyTop.hide();
58648             this.proxyBottom.hide();
58649         }
58650         return result ? this.dropAllowed : this.dropNotAllowed;
58651     },
58652
58653     onNodeOut : function(n, dd, e, data){
58654         this.proxyTop.hide();
58655         this.proxyBottom.hide();
58656     },
58657
58658     onNodeDrop : function(n, dd, e, data){
58659         var h = data.header;
58660         if(h != n){
58661             var cm = this.grid.colModel;
58662             var x = Roo.lib.Event.getPageX(e);
58663             var r = Roo.lib.Dom.getRegion(n.firstChild);
58664             var pt = (r.right - x) <= ((r.right-r.left)/2) ? "after" : "before";
58665             var oldIndex = this.view.getCellIndex(h);
58666             var newIndex = this.view.getCellIndex(n);
58667             var locked = cm.isLocked(newIndex);
58668             if(pt == "after"){
58669                 newIndex++;
58670             }
58671             if(oldIndex < newIndex){
58672                 newIndex--;
58673             }
58674             if(oldIndex == newIndex && (locked == cm.isLocked(oldIndex))){
58675                 return false;
58676             }
58677             cm.setLocked(oldIndex, locked, true);
58678             cm.moveColumn(oldIndex, newIndex);
58679             this.grid.fireEvent("columnmove", oldIndex, newIndex);
58680             return true;
58681         }
58682         return false;
58683     }
58684 });
58685 /*
58686  * Based on:
58687  * Ext JS Library 1.1.1
58688  * Copyright(c) 2006-2007, Ext JS, LLC.
58689  *
58690  * Originally Released Under LGPL - original licence link has changed is not relivant.
58691  *
58692  * Fork - LGPL
58693  * <script type="text/javascript">
58694  */
58695   
58696 /**
58697  * @class Roo.grid.GridView
58698  * @extends Roo.util.Observable
58699  *
58700  * @constructor
58701  * @param {Object} config
58702  */
58703 Roo.grid.GridView = function(config){
58704     Roo.grid.GridView.superclass.constructor.call(this);
58705     this.el = null;
58706
58707     Roo.apply(this, config);
58708 };
58709
58710 Roo.extend(Roo.grid.GridView, Roo.grid.AbstractGridView, {
58711
58712     unselectable :  'unselectable="on"',
58713     unselectableCls :  'x-unselectable',
58714     
58715     
58716     rowClass : "x-grid-row",
58717
58718     cellClass : "x-grid-col",
58719
58720     tdClass : "x-grid-td",
58721
58722     hdClass : "x-grid-hd",
58723
58724     splitClass : "x-grid-split",
58725
58726     sortClasses : ["sort-asc", "sort-desc"],
58727
58728     enableMoveAnim : false,
58729
58730     hlColor: "C3DAF9",
58731
58732     dh : Roo.DomHelper,
58733
58734     fly : Roo.Element.fly,
58735
58736     css : Roo.util.CSS,
58737
58738     borderWidth: 1,
58739
58740     splitOffset: 3,
58741
58742     scrollIncrement : 22,
58743
58744     cellRE: /(?:.*?)x-grid-(?:hd|cell|csplit)-(?:[\d]+)-([\d]+)(?:.*?)/,
58745
58746     findRE: /\s?(?:x-grid-hd|x-grid-col|x-grid-csplit)\s/,
58747
58748     bind : function(ds, cm){
58749         if(this.ds){
58750             this.ds.un("load", this.onLoad, this);
58751             this.ds.un("datachanged", this.onDataChange, this);
58752             this.ds.un("add", this.onAdd, this);
58753             this.ds.un("remove", this.onRemove, this);
58754             this.ds.un("update", this.onUpdate, this);
58755             this.ds.un("clear", this.onClear, this);
58756         }
58757         if(ds){
58758             ds.on("load", this.onLoad, this);
58759             ds.on("datachanged", this.onDataChange, this);
58760             ds.on("add", this.onAdd, this);
58761             ds.on("remove", this.onRemove, this);
58762             ds.on("update", this.onUpdate, this);
58763             ds.on("clear", this.onClear, this);
58764         }
58765         this.ds = ds;
58766
58767         if(this.cm){
58768             this.cm.un("widthchange", this.onColWidthChange, this);
58769             this.cm.un("headerchange", this.onHeaderChange, this);
58770             this.cm.un("hiddenchange", this.onHiddenChange, this);
58771             this.cm.un("columnmoved", this.onColumnMove, this);
58772             this.cm.un("columnlockchange", this.onColumnLock, this);
58773         }
58774         if(cm){
58775             this.generateRules(cm);
58776             cm.on("widthchange", this.onColWidthChange, this);
58777             cm.on("headerchange", this.onHeaderChange, this);
58778             cm.on("hiddenchange", this.onHiddenChange, this);
58779             cm.on("columnmoved", this.onColumnMove, this);
58780             cm.on("columnlockchange", this.onColumnLock, this);
58781         }
58782         this.cm = cm;
58783     },
58784
58785     init: function(grid){
58786         Roo.grid.GridView.superclass.init.call(this, grid);
58787
58788         this.bind(grid.dataSource, grid.colModel);
58789
58790         grid.on("headerclick", this.handleHeaderClick, this);
58791
58792         if(grid.trackMouseOver){
58793             grid.on("mouseover", this.onRowOver, this);
58794             grid.on("mouseout", this.onRowOut, this);
58795         }
58796         grid.cancelTextSelection = function(){};
58797         this.gridId = grid.id;
58798
58799         var tpls = this.templates || {};
58800
58801         if(!tpls.master){
58802             tpls.master = new Roo.Template(
58803                '<div class="x-grid" hidefocus="true">',
58804                 '<a href="#" class="x-grid-focus" tabIndex="-1"></a>',
58805                   '<div class="x-grid-topbar"></div>',
58806                   '<div class="x-grid-scroller"><div></div></div>',
58807                   '<div class="x-grid-locked">',
58808                       '<div class="x-grid-header">{lockedHeader}</div>',
58809                       '<div class="x-grid-body">{lockedBody}</div>',
58810                   "</div>",
58811                   '<div class="x-grid-viewport">',
58812                       '<div class="x-grid-header">{header}</div>',
58813                       '<div class="x-grid-body">{body}</div>',
58814                   "</div>",
58815                   '<div class="x-grid-bottombar"></div>',
58816                  
58817                   '<div class="x-grid-resize-proxy">&#160;</div>',
58818                "</div>"
58819             );
58820             tpls.master.disableformats = true;
58821         }
58822
58823         if(!tpls.header){
58824             tpls.header = new Roo.Template(
58825                '<table border="0" cellspacing="0" cellpadding="0">',
58826                '<tbody><tr class="x-grid-hd-row">{cells}</tr></tbody>',
58827                "</table>{splits}"
58828             );
58829             tpls.header.disableformats = true;
58830         }
58831         tpls.header.compile();
58832
58833         if(!tpls.hcell){
58834             tpls.hcell = new Roo.Template(
58835                 '<td class="x-grid-hd x-grid-td-{id} {cellId}"><div title="{title}" class="x-grid-hd-inner x-grid-hd-{id}">',
58836                 '<div class="x-grid-hd-text ' + this.unselectableCls +  '" ' + this.unselectable +'>{value}<img class="x-grid-sort-icon" src="', Roo.BLANK_IMAGE_URL, '" /></div>',
58837                 "</div></td>"
58838              );
58839              tpls.hcell.disableFormats = true;
58840         }
58841         tpls.hcell.compile();
58842
58843         if(!tpls.hsplit){
58844             tpls.hsplit = new Roo.Template('<div class="x-grid-split {splitId} x-grid-split-{id}" style="{style} ' +
58845                                             this.unselectableCls +  '" ' + this.unselectable +'>&#160;</div>');
58846             tpls.hsplit.disableFormats = true;
58847         }
58848         tpls.hsplit.compile();
58849
58850         if(!tpls.body){
58851             tpls.body = new Roo.Template(
58852                '<table border="0" cellspacing="0" cellpadding="0">',
58853                "<tbody>{rows}</tbody>",
58854                "</table>"
58855             );
58856             tpls.body.disableFormats = true;
58857         }
58858         tpls.body.compile();
58859
58860         if(!tpls.row){
58861             tpls.row = new Roo.Template('<tr class="x-grid-row {alt}">{cells}</tr>');
58862             tpls.row.disableFormats = true;
58863         }
58864         tpls.row.compile();
58865
58866         if(!tpls.cell){
58867             tpls.cell = new Roo.Template(
58868                 '<td class="x-grid-col x-grid-td-{id} {cellId} {css}" tabIndex="0">',
58869                 '<div class="x-grid-col-{id} x-grid-cell-inner"><div class="x-grid-cell-text ' +
58870                     this.unselectableCls +  '" ' + this.unselectable +'" {attr}>{value}</div></div>',
58871                 "</td>"
58872             );
58873             tpls.cell.disableFormats = true;
58874         }
58875         tpls.cell.compile();
58876
58877         this.templates = tpls;
58878     },
58879
58880     // remap these for backwards compat
58881     onColWidthChange : function(){
58882         this.updateColumns.apply(this, arguments);
58883     },
58884     onHeaderChange : function(){
58885         this.updateHeaders.apply(this, arguments);
58886     }, 
58887     onHiddenChange : function(){
58888         this.handleHiddenChange.apply(this, arguments);
58889     },
58890     onColumnMove : function(){
58891         this.handleColumnMove.apply(this, arguments);
58892     },
58893     onColumnLock : function(){
58894         this.handleLockChange.apply(this, arguments);
58895     },
58896
58897     onDataChange : function(){
58898         this.refresh();
58899         this.updateHeaderSortState();
58900     },
58901
58902     onClear : function(){
58903         this.refresh();
58904     },
58905
58906     onUpdate : function(ds, record){
58907         this.refreshRow(record);
58908     },
58909
58910     refreshRow : function(record){
58911         var ds = this.ds, index;
58912         if(typeof record == 'number'){
58913             index = record;
58914             record = ds.getAt(index);
58915         }else{
58916             index = ds.indexOf(record);
58917         }
58918         this.insertRows(ds, index, index, true);
58919         this.onRemove(ds, record, index+1, true);
58920         this.syncRowHeights(index, index);
58921         this.layout();
58922         this.fireEvent("rowupdated", this, index, record);
58923     },
58924
58925     onAdd : function(ds, records, index){
58926         this.insertRows(ds, index, index + (records.length-1));
58927     },
58928
58929     onRemove : function(ds, record, index, isUpdate){
58930         if(isUpdate !== true){
58931             this.fireEvent("beforerowremoved", this, index, record);
58932         }
58933         var bt = this.getBodyTable(), lt = this.getLockedTable();
58934         if(bt.rows[index]){
58935             bt.firstChild.removeChild(bt.rows[index]);
58936         }
58937         if(lt.rows[index]){
58938             lt.firstChild.removeChild(lt.rows[index]);
58939         }
58940         if(isUpdate !== true){
58941             this.stripeRows(index);
58942             this.syncRowHeights(index, index);
58943             this.layout();
58944             this.fireEvent("rowremoved", this, index, record);
58945         }
58946     },
58947
58948     onLoad : function(){
58949         this.scrollToTop();
58950     },
58951
58952     /**
58953      * Scrolls the grid to the top
58954      */
58955     scrollToTop : function(){
58956         if(this.scroller){
58957             this.scroller.dom.scrollTop = 0;
58958             this.syncScroll();
58959         }
58960     },
58961
58962     /**
58963      * Gets a panel in the header of the grid that can be used for toolbars etc.
58964      * After modifying the contents of this panel a call to grid.autoSize() may be
58965      * required to register any changes in size.
58966      * @param {Boolean} doShow By default the header is hidden. Pass true to show the panel
58967      * @return Roo.Element
58968      */
58969     getHeaderPanel : function(doShow){
58970         if(doShow){
58971             this.headerPanel.show();
58972         }
58973         return this.headerPanel;
58974     },
58975
58976     /**
58977      * Gets a panel in the footer of the grid that can be used for toolbars etc.
58978      * After modifying the contents of this panel a call to grid.autoSize() may be
58979      * required to register any changes in size.
58980      * @param {Boolean} doShow By default the footer is hidden. Pass true to show the panel
58981      * @return Roo.Element
58982      */
58983     getFooterPanel : function(doShow){
58984         if(doShow){
58985             this.footerPanel.show();
58986         }
58987         return this.footerPanel;
58988     },
58989
58990     initElements : function(){
58991         var E = Roo.Element;
58992         var el = this.grid.getGridEl().dom.firstChild;
58993         var cs = el.childNodes;
58994
58995         this.el = new E(el);
58996         
58997          this.focusEl = new E(el.firstChild);
58998         this.focusEl.swallowEvent("click", true);
58999         
59000         this.headerPanel = new E(cs[1]);
59001         this.headerPanel.enableDisplayMode("block");
59002
59003         this.scroller = new E(cs[2]);
59004         this.scrollSizer = new E(this.scroller.dom.firstChild);
59005
59006         this.lockedWrap = new E(cs[3]);
59007         this.lockedHd = new E(this.lockedWrap.dom.firstChild);
59008         this.lockedBody = new E(this.lockedWrap.dom.childNodes[1]);
59009
59010         this.mainWrap = new E(cs[4]);
59011         this.mainHd = new E(this.mainWrap.dom.firstChild);
59012         this.mainBody = new E(this.mainWrap.dom.childNodes[1]);
59013
59014         this.footerPanel = new E(cs[5]);
59015         this.footerPanel.enableDisplayMode("block");
59016
59017         this.resizeProxy = new E(cs[6]);
59018
59019         this.headerSelector = String.format(
59020            '#{0} td.x-grid-hd, #{1} td.x-grid-hd',
59021            this.lockedHd.id, this.mainHd.id
59022         );
59023
59024         this.splitterSelector = String.format(
59025            '#{0} div.x-grid-split, #{1} div.x-grid-split',
59026            this.idToCssName(this.lockedHd.id), this.idToCssName(this.mainHd.id)
59027         );
59028     },
59029     idToCssName : function(s)
59030     {
59031         return s.replace(/[^a-z0-9]+/ig, '-');
59032     },
59033
59034     getHeaderCell : function(index){
59035         return Roo.DomQuery.select(this.headerSelector)[index];
59036     },
59037
59038     getHeaderCellMeasure : function(index){
59039         return this.getHeaderCell(index).firstChild;
59040     },
59041
59042     getHeaderCellText : function(index){
59043         return this.getHeaderCell(index).firstChild.firstChild;
59044     },
59045
59046     getLockedTable : function(){
59047         return this.lockedBody.dom.firstChild;
59048     },
59049
59050     getBodyTable : function(){
59051         return this.mainBody.dom.firstChild;
59052     },
59053
59054     getLockedRow : function(index){
59055         return this.getLockedTable().rows[index];
59056     },
59057
59058     getRow : function(index){
59059         return this.getBodyTable().rows[index];
59060     },
59061
59062     getRowComposite : function(index){
59063         if(!this.rowEl){
59064             this.rowEl = new Roo.CompositeElementLite();
59065         }
59066         var els = [], lrow, mrow;
59067         if(lrow = this.getLockedRow(index)){
59068             els.push(lrow);
59069         }
59070         if(mrow = this.getRow(index)){
59071             els.push(mrow);
59072         }
59073         this.rowEl.elements = els;
59074         return this.rowEl;
59075     },
59076     /**
59077      * Gets the 'td' of the cell
59078      * 
59079      * @param {Integer} rowIndex row to select
59080      * @param {Integer} colIndex column to select
59081      * 
59082      * @return {Object} 
59083      */
59084     getCell : function(rowIndex, colIndex){
59085         var locked = this.cm.getLockedCount();
59086         var source;
59087         if(colIndex < locked){
59088             source = this.lockedBody.dom.firstChild;
59089         }else{
59090             source = this.mainBody.dom.firstChild;
59091             colIndex -= locked;
59092         }
59093         return source.rows[rowIndex].childNodes[colIndex];
59094     },
59095
59096     getCellText : function(rowIndex, colIndex){
59097         return this.getCell(rowIndex, colIndex).firstChild.firstChild;
59098     },
59099
59100     getCellBox : function(cell){
59101         var b = this.fly(cell).getBox();
59102         if(Roo.isOpera){ // opera fails to report the Y
59103             b.y = cell.offsetTop + this.mainBody.getY();
59104         }
59105         return b;
59106     },
59107
59108     getCellIndex : function(cell){
59109         var id = String(cell.className).match(this.cellRE);
59110         if(id){
59111             return parseInt(id[1], 10);
59112         }
59113         return 0;
59114     },
59115
59116     findHeaderIndex : function(n){
59117         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59118         return r ? this.getCellIndex(r) : false;
59119     },
59120
59121     findHeaderCell : function(n){
59122         var r = Roo.fly(n).findParent("td." + this.hdClass, 6);
59123         return r ? r : false;
59124     },
59125
59126     findRowIndex : function(n){
59127         if(!n){
59128             return false;
59129         }
59130         var r = Roo.fly(n).findParent("tr." + this.rowClass, 6);
59131         return r ? r.rowIndex : false;
59132     },
59133
59134     findCellIndex : function(node){
59135         var stop = this.el.dom;
59136         while(node && node != stop){
59137             if(this.findRE.test(node.className)){
59138                 return this.getCellIndex(node);
59139             }
59140             node = node.parentNode;
59141         }
59142         return false;
59143     },
59144
59145     getColumnId : function(index){
59146         return this.cm.getColumnId(index);
59147     },
59148
59149     getSplitters : function()
59150     {
59151         if(this.splitterSelector){
59152            return Roo.DomQuery.select(this.splitterSelector);
59153         }else{
59154             return null;
59155       }
59156     },
59157
59158     getSplitter : function(index){
59159         return this.getSplitters()[index];
59160     },
59161
59162     onRowOver : function(e, t){
59163         var row;
59164         if((row = this.findRowIndex(t)) !== false){
59165             this.getRowComposite(row).addClass("x-grid-row-over");
59166         }
59167     },
59168
59169     onRowOut : function(e, t){
59170         var row;
59171         if((row = this.findRowIndex(t)) !== false && row !== this.findRowIndex(e.getRelatedTarget())){
59172             this.getRowComposite(row).removeClass("x-grid-row-over");
59173         }
59174     },
59175
59176     renderHeaders : function(){
59177         var cm = this.cm;
59178         var ct = this.templates.hcell, ht = this.templates.header, st = this.templates.hsplit;
59179         var cb = [], lb = [], sb = [], lsb = [], p = {};
59180         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59181             p.cellId = "x-grid-hd-0-" + i;
59182             p.splitId = "x-grid-csplit-0-" + i;
59183             p.id = cm.getColumnId(i);
59184             p.value = cm.getColumnHeader(i) || "";
59185             p.title = cm.getColumnTooltip(i) || (''+p.value).match(/\</)  ? '' :  p.value  || "";
59186             p.style = (this.grid.enableColumnResize === false || !cm.isResizable(i) || cm.isFixed(i)) ? 'cursor:default' : '';
59187             if(!cm.isLocked(i)){
59188                 cb[cb.length] = ct.apply(p);
59189                 sb[sb.length] = st.apply(p);
59190             }else{
59191                 lb[lb.length] = ct.apply(p);
59192                 lsb[lsb.length] = st.apply(p);
59193             }
59194         }
59195         return [ht.apply({cells: lb.join(""), splits:lsb.join("")}),
59196                 ht.apply({cells: cb.join(""), splits:sb.join("")})];
59197     },
59198
59199     updateHeaders : function(){
59200         var html = this.renderHeaders();
59201         this.lockedHd.update(html[0]);
59202         this.mainHd.update(html[1]);
59203     },
59204
59205     /**
59206      * Focuses the specified row.
59207      * @param {Number} row The row index
59208      */
59209     focusRow : function(row)
59210     {
59211         //Roo.log('GridView.focusRow');
59212         var x = this.scroller.dom.scrollLeft;
59213         this.focusCell(row, 0, false);
59214         this.scroller.dom.scrollLeft = x;
59215     },
59216
59217     /**
59218      * Focuses the specified cell.
59219      * @param {Number} row The row index
59220      * @param {Number} col The column index
59221      * @param {Boolean} hscroll false to disable horizontal scrolling
59222      */
59223     focusCell : function(row, col, hscroll)
59224     {
59225         //Roo.log('GridView.focusCell');
59226         var el = this.ensureVisible(row, col, hscroll);
59227         this.focusEl.alignTo(el, "tl-tl");
59228         if(Roo.isGecko){
59229             this.focusEl.focus();
59230         }else{
59231             this.focusEl.focus.defer(1, this.focusEl);
59232         }
59233     },
59234
59235     /**
59236      * Scrolls the specified cell into view
59237      * @param {Number} row The row index
59238      * @param {Number} col The column index
59239      * @param {Boolean} hscroll false to disable horizontal scrolling
59240      */
59241     ensureVisible : function(row, col, hscroll)
59242     {
59243         //Roo.log('GridView.ensureVisible,' + row + ',' + col);
59244         //return null; //disable for testing.
59245         if(typeof row != "number"){
59246             row = row.rowIndex;
59247         }
59248         if(row < 0 && row >= this.ds.getCount()){
59249             return  null;
59250         }
59251         col = (col !== undefined ? col : 0);
59252         var cm = this.grid.colModel;
59253         while(cm.isHidden(col)){
59254             col++;
59255         }
59256
59257         var el = this.getCell(row, col);
59258         if(!el){
59259             return null;
59260         }
59261         var c = this.scroller.dom;
59262
59263         var ctop = parseInt(el.offsetTop, 10);
59264         var cleft = parseInt(el.offsetLeft, 10);
59265         var cbot = ctop + el.offsetHeight;
59266         var cright = cleft + el.offsetWidth;
59267         
59268         var ch = c.clientHeight - this.mainHd.dom.offsetHeight;
59269         var stop = parseInt(c.scrollTop, 10);
59270         var sleft = parseInt(c.scrollLeft, 10);
59271         var sbot = stop + ch;
59272         var sright = sleft + c.clientWidth;
59273         /*
59274         Roo.log('GridView.ensureVisible:' +
59275                 ' ctop:' + ctop +
59276                 ' c.clientHeight:' + c.clientHeight +
59277                 ' this.mainHd.dom.offsetHeight:' + this.mainHd.dom.offsetHeight +
59278                 ' stop:' + stop +
59279                 ' cbot:' + cbot +
59280                 ' sbot:' + sbot +
59281                 ' ch:' + ch  
59282                 );
59283         */
59284         if(ctop < stop){
59285             c.scrollTop = ctop;
59286             //Roo.log("set scrolltop to ctop DISABLE?");
59287         }else if(cbot > sbot){
59288             //Roo.log("set scrolltop to cbot-ch");
59289             c.scrollTop = cbot-ch;
59290         }
59291         
59292         if(hscroll !== false){
59293             if(cleft < sleft){
59294                 c.scrollLeft = cleft;
59295             }else if(cright > sright){
59296                 c.scrollLeft = cright-c.clientWidth;
59297             }
59298         }
59299          
59300         return el;
59301     },
59302
59303     updateColumns : function(){
59304         this.grid.stopEditing();
59305         var cm = this.grid.colModel, colIds = this.getColumnIds();
59306         //var totalWidth = cm.getTotalWidth();
59307         var pos = 0;
59308         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59309             //if(cm.isHidden(i)) continue;
59310             var w = cm.getColumnWidth(i);
59311             this.css.updateRule(this.colSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59312             this.css.updateRule(this.hdSelector+this.idToCssName(colIds[i]), "width", (w - this.borderWidth) + "px");
59313         }
59314         this.updateSplitters();
59315     },
59316
59317     generateRules : function(cm){
59318         var ruleBuf = [], rulesId = this.idToCssName(this.grid.id)+ '-cssrules';
59319         Roo.util.CSS.removeStyleSheet(rulesId);
59320         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59321             var cid = cm.getColumnId(i);
59322             var align = '';
59323             if(cm.config[i].align){
59324                 align = 'text-align:'+cm.config[i].align+';';
59325             }
59326             var hidden = '';
59327             if(cm.isHidden(i)){
59328                 hidden = 'display:none;';
59329             }
59330             var width = "width:" + (cm.getColumnWidth(i) - this.borderWidth) + "px;";
59331             ruleBuf.push(
59332                     this.colSelector, cid, " {\n", cm.config[i].css, align, width, "\n}\n",
59333                     this.hdSelector, cid, " {\n", align, width, "}\n",
59334                     this.tdSelector, cid, " {\n",hidden,"\n}\n",
59335                     this.splitSelector, cid, " {\n", hidden , "\n}\n");
59336         }
59337         return Roo.util.CSS.createStyleSheet(ruleBuf.join(""), rulesId);
59338     },
59339
59340     updateSplitters : function(){
59341         var cm = this.cm, s = this.getSplitters();
59342         if(s){ // splitters not created yet
59343             var pos = 0, locked = true;
59344             for(var i = 0, len = cm.getColumnCount(); i < len; i++){
59345                 if(cm.isHidden(i)) {
59346                     continue;
59347                 }
59348                 var w = cm.getColumnWidth(i); // make sure it's a number
59349                 if(!cm.isLocked(i) && locked){
59350                     pos = 0;
59351                     locked = false;
59352                 }
59353                 pos += w;
59354                 s[i].style.left = (pos-this.splitOffset) + "px";
59355             }
59356         }
59357     },
59358
59359     handleHiddenChange : function(colModel, colIndex, hidden){
59360         if(hidden){
59361             this.hideColumn(colIndex);
59362         }else{
59363             this.unhideColumn(colIndex);
59364         }
59365     },
59366
59367     hideColumn : function(colIndex){
59368         var cid = this.getColumnId(colIndex);
59369         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "none");
59370         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "none");
59371         if(Roo.isSafari){
59372             this.updateHeaders();
59373         }
59374         this.updateSplitters();
59375         this.layout();
59376     },
59377
59378     unhideColumn : function(colIndex){
59379         var cid = this.getColumnId(colIndex);
59380         this.css.updateRule(this.tdSelector+this.idToCssName(cid), "display", "");
59381         this.css.updateRule(this.splitSelector+this.idToCssName(cid), "display", "");
59382
59383         if(Roo.isSafari){
59384             this.updateHeaders();
59385         }
59386         this.updateSplitters();
59387         this.layout();
59388     },
59389
59390     insertRows : function(dm, firstRow, lastRow, isUpdate){
59391         if(firstRow == 0 && lastRow == dm.getCount()-1){
59392             this.refresh();
59393         }else{
59394             if(!isUpdate){
59395                 this.fireEvent("beforerowsinserted", this, firstRow, lastRow);
59396             }
59397             var s = this.getScrollState();
59398             var markup = this.renderRows(firstRow, lastRow);
59399             this.bufferRows(markup[0], this.getLockedTable(), firstRow);
59400             this.bufferRows(markup[1], this.getBodyTable(), firstRow);
59401             this.restoreScroll(s);
59402             if(!isUpdate){
59403                 this.fireEvent("rowsinserted", this, firstRow, lastRow);
59404                 this.syncRowHeights(firstRow, lastRow);
59405                 this.stripeRows(firstRow);
59406                 this.layout();
59407             }
59408         }
59409     },
59410
59411     bufferRows : function(markup, target, index){
59412         var before = null, trows = target.rows, tbody = target.tBodies[0];
59413         if(index < trows.length){
59414             before = trows[index];
59415         }
59416         var b = document.createElement("div");
59417         b.innerHTML = "<table><tbody>"+markup+"</tbody></table>";
59418         var rows = b.firstChild.rows;
59419         for(var i = 0, len = rows.length; i < len; i++){
59420             if(before){
59421                 tbody.insertBefore(rows[0], before);
59422             }else{
59423                 tbody.appendChild(rows[0]);
59424             }
59425         }
59426         b.innerHTML = "";
59427         b = null;
59428     },
59429
59430     deleteRows : function(dm, firstRow, lastRow){
59431         if(dm.getRowCount()<1){
59432             this.fireEvent("beforerefresh", this);
59433             this.mainBody.update("");
59434             this.lockedBody.update("");
59435             this.fireEvent("refresh", this);
59436         }else{
59437             this.fireEvent("beforerowsdeleted", this, firstRow, lastRow);
59438             var bt = this.getBodyTable();
59439             var tbody = bt.firstChild;
59440             var rows = bt.rows;
59441             for(var rowIndex = firstRow; rowIndex <= lastRow; rowIndex++){
59442                 tbody.removeChild(rows[firstRow]);
59443             }
59444             this.stripeRows(firstRow);
59445             this.fireEvent("rowsdeleted", this, firstRow, lastRow);
59446         }
59447     },
59448
59449     updateRows : function(dataSource, firstRow, lastRow){
59450         var s = this.getScrollState();
59451         this.refresh();
59452         this.restoreScroll(s);
59453     },
59454
59455     handleSort : function(dataSource, sortColumnIndex, sortDir, noRefresh){
59456         if(!noRefresh){
59457            this.refresh();
59458         }
59459         this.updateHeaderSortState();
59460     },
59461
59462     getScrollState : function(){
59463         
59464         var sb = this.scroller.dom;
59465         return {left: sb.scrollLeft, top: sb.scrollTop};
59466     },
59467
59468     stripeRows : function(startRow){
59469         if(!this.grid.stripeRows || this.ds.getCount() < 1){
59470             return;
59471         }
59472         startRow = startRow || 0;
59473         var rows = this.getBodyTable().rows;
59474         var lrows = this.getLockedTable().rows;
59475         var cls = ' x-grid-row-alt ';
59476         for(var i = startRow, len = rows.length; i < len; i++){
59477             var row = rows[i], lrow = lrows[i];
59478             var isAlt = ((i+1) % 2 == 0);
59479             var hasAlt = (' '+row.className + ' ').indexOf(cls) != -1;
59480             if(isAlt == hasAlt){
59481                 continue;
59482             }
59483             if(isAlt){
59484                 row.className += " x-grid-row-alt";
59485             }else{
59486                 row.className = row.className.replace("x-grid-row-alt", "");
59487             }
59488             if(lrow){
59489                 lrow.className = row.className;
59490             }
59491         }
59492     },
59493
59494     restoreScroll : function(state){
59495         //Roo.log('GridView.restoreScroll');
59496         var sb = this.scroller.dom;
59497         sb.scrollLeft = state.left;
59498         sb.scrollTop = state.top;
59499         this.syncScroll();
59500     },
59501
59502     syncScroll : function(){
59503         //Roo.log('GridView.syncScroll');
59504         var sb = this.scroller.dom;
59505         var sh = this.mainHd.dom;
59506         var bs = this.mainBody.dom;
59507         var lv = this.lockedBody.dom;
59508         sh.scrollLeft = bs.scrollLeft = sb.scrollLeft;
59509         lv.scrollTop = bs.scrollTop = sb.scrollTop;
59510     },
59511
59512     handleScroll : function(e){
59513         this.syncScroll();
59514         var sb = this.scroller.dom;
59515         this.grid.fireEvent("bodyscroll", sb.scrollLeft, sb.scrollTop);
59516         e.stopEvent();
59517     },
59518
59519     handleWheel : function(e){
59520         var d = e.getWheelDelta();
59521         this.scroller.dom.scrollTop -= d*22;
59522         // set this here to prevent jumpy scrolling on large tables
59523         this.lockedBody.dom.scrollTop = this.mainBody.dom.scrollTop = this.scroller.dom.scrollTop;
59524         e.stopEvent();
59525     },
59526
59527     renderRows : function(startRow, endRow){
59528         // pull in all the crap needed to render rows
59529         var g = this.grid, cm = g.colModel, ds = g.dataSource, stripe = g.stripeRows;
59530         var colCount = cm.getColumnCount();
59531
59532         if(ds.getCount() < 1){
59533             return ["", ""];
59534         }
59535
59536         // build a map for all the columns
59537         var cs = [];
59538         for(var i = 0; i < colCount; i++){
59539             var name = cm.getDataIndex(i);
59540             cs[i] = {
59541                 name : typeof name == 'undefined' ? ds.fields.get(i).name : name,
59542                 renderer : cm.getRenderer(i),
59543                 id : cm.getColumnId(i),
59544                 locked : cm.isLocked(i),
59545                 has_editor : cm.isCellEditable(i)
59546             };
59547         }
59548
59549         startRow = startRow || 0;
59550         endRow = typeof endRow == "undefined"? ds.getCount()-1 : endRow;
59551
59552         // records to render
59553         var rs = ds.getRange(startRow, endRow);
59554
59555         return this.doRender(cs, rs, ds, startRow, colCount, stripe);
59556     },
59557
59558     // As much as I hate to duplicate code, this was branched because FireFox really hates
59559     // [].join("") on strings. The performance difference was substantial enough to
59560     // branch this function
59561     doRender : Roo.isGecko ?
59562             function(cs, rs, ds, startRow, colCount, stripe){
59563                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59564                 // buffers
59565                 var buf = "", lbuf = "", cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59566                 
59567                 var hasListener = this.grid.hasListener('rowclass');
59568                 var rowcfg = {};
59569                 for(var j = 0, len = rs.length; j < len; j++){
59570                     r = rs[j]; cb = ""; lcb = ""; rowIndex = (j+startRow);
59571                     for(var i = 0; i < colCount; i++){
59572                         c = cs[i];
59573                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59574                         p.id = c.id;
59575                         p.css = p.attr = "";
59576                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59577                         if(p.value == undefined || p.value === "") {
59578                             p.value = "&#160;";
59579                         }
59580                         if(c.has_editor){
59581                             p.css += ' x-grid-editable-cell';
59582                         }
59583                         if(c.dirty && typeof r.modified[c.name] !== 'undefined'){
59584                             p.css +=  ' x-grid-dirty-cell';
59585                         }
59586                         var markup = ct.apply(p);
59587                         if(!c.locked){
59588                             cb+= markup;
59589                         }else{
59590                             lcb+= markup;
59591                         }
59592                     }
59593                     var alt = [];
59594                     if(stripe && ((rowIndex+1) % 2 == 0)){
59595                         alt.push("x-grid-row-alt")
59596                     }
59597                     if(r.dirty){
59598                         alt.push(  " x-grid-dirty-row");
59599                     }
59600                     rp.cells = lcb;
59601                     if(this.getRowClass){
59602                         alt.push(this.getRowClass(r, rowIndex));
59603                     }
59604                     if (hasListener) {
59605                         rowcfg = {
59606                              
59607                             record: r,
59608                             rowIndex : rowIndex,
59609                             rowClass : ''
59610                         };
59611                         this.grid.fireEvent('rowclass', this, rowcfg);
59612                         alt.push(rowcfg.rowClass);
59613                     }
59614                     rp.alt = alt.join(" ");
59615                     lbuf+= rt.apply(rp);
59616                     rp.cells = cb;
59617                     buf+=  rt.apply(rp);
59618                 }
59619                 return [lbuf, buf];
59620             } :
59621             function(cs, rs, ds, startRow, colCount, stripe){
59622                 var ts = this.templates, ct = ts.cell, rt = ts.row;
59623                 // buffers
59624                 var buf = [], lbuf = [], cb, lcb, c, p = {}, rp = {}, r, rowIndex;
59625                 var hasListener = this.grid.hasListener('rowclass');
59626  
59627                 var rowcfg = {};
59628                 for(var j = 0, len = rs.length; j < len; j++){
59629                     r = rs[j]; cb = []; lcb = []; rowIndex = (j+startRow);
59630                     for(var i = 0; i < colCount; i++){
59631                         c = cs[i];
59632                         p.cellId = "x-grid-cell-" + rowIndex + "-" + i;
59633                         p.id = c.id;
59634                         p.css = p.attr = "";
59635                         p.value = c.renderer(r.data[c.name], p, r, rowIndex, i, ds);
59636                         if(p.value == undefined || p.value === "") {
59637                             p.value = "&#160;";
59638                         }
59639                         //Roo.log(c);
59640                          if(c.has_editor){
59641                             p.css += ' x-grid-editable-cell';
59642                         }
59643                         if(r.dirty && typeof r.modified[c.name] !== 'undefined'){
59644                             p.css += ' x-grid-dirty-cell' 
59645                         }
59646                         
59647                         var markup = ct.apply(p);
59648                         if(!c.locked){
59649                             cb[cb.length] = markup;
59650                         }else{
59651                             lcb[lcb.length] = markup;
59652                         }
59653                     }
59654                     var alt = [];
59655                     if(stripe && ((rowIndex+1) % 2 == 0)){
59656                         alt.push( "x-grid-row-alt");
59657                     }
59658                     if(r.dirty){
59659                         alt.push(" x-grid-dirty-row");
59660                     }
59661                     rp.cells = lcb;
59662                     if(this.getRowClass){
59663                         alt.push( this.getRowClass(r, rowIndex));
59664                     }
59665                     if (hasListener) {
59666                         rowcfg = {
59667                              
59668                             record: r,
59669                             rowIndex : rowIndex,
59670                             rowClass : ''
59671                         };
59672                         this.grid.fireEvent('rowclass', this, rowcfg);
59673                         alt.push(rowcfg.rowClass);
59674                     }
59675                     
59676                     rp.alt = alt.join(" ");
59677                     rp.cells = lcb.join("");
59678                     lbuf[lbuf.length] = rt.apply(rp);
59679                     rp.cells = cb.join("");
59680                     buf[buf.length] =  rt.apply(rp);
59681                 }
59682                 return [lbuf.join(""), buf.join("")];
59683             },
59684
59685     renderBody : function(){
59686         var markup = this.renderRows();
59687         var bt = this.templates.body;
59688         return [bt.apply({rows: markup[0]}), bt.apply({rows: markup[1]})];
59689     },
59690
59691     /**
59692      * Refreshes the grid
59693      * @param {Boolean} headersToo
59694      */
59695     refresh : function(headersToo){
59696         this.fireEvent("beforerefresh", this);
59697         this.grid.stopEditing();
59698         var result = this.renderBody();
59699         this.lockedBody.update(result[0]);
59700         this.mainBody.update(result[1]);
59701         if(headersToo === true){
59702             this.updateHeaders();
59703             this.updateColumns();
59704             this.updateSplitters();
59705             this.updateHeaderSortState();
59706         }
59707         this.syncRowHeights();
59708         this.layout();
59709         this.fireEvent("refresh", this);
59710     },
59711
59712     handleColumnMove : function(cm, oldIndex, newIndex){
59713         this.indexMap = null;
59714         var s = this.getScrollState();
59715         this.refresh(true);
59716         this.restoreScroll(s);
59717         this.afterMove(newIndex);
59718     },
59719
59720     afterMove : function(colIndex){
59721         if(this.enableMoveAnim && Roo.enableFx){
59722             this.fly(this.getHeaderCell(colIndex).firstChild).highlight(this.hlColor);
59723         }
59724         // if multisort - fix sortOrder, and reload..
59725         if (this.grid.dataSource.multiSort) {
59726             // the we can call sort again..
59727             var dm = this.grid.dataSource;
59728             var cm = this.grid.colModel;
59729             var so = [];
59730             for(var i = 0; i < cm.config.length; i++ ) {
59731                 
59732                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined')) {
59733                     continue; // dont' bother, it's not in sort list or being set.
59734                 }
59735                 
59736                 so.push(cm.config[i].dataIndex);
59737             };
59738             dm.sortOrder = so;
59739             dm.load(dm.lastOptions);
59740             
59741             
59742         }
59743         
59744     },
59745
59746     updateCell : function(dm, rowIndex, dataIndex){
59747         var colIndex = this.getColumnIndexByDataIndex(dataIndex);
59748         if(typeof colIndex == "undefined"){ // not present in grid
59749             return;
59750         }
59751         var cm = this.grid.colModel;
59752         var cell = this.getCell(rowIndex, colIndex);
59753         var cellText = this.getCellText(rowIndex, colIndex);
59754
59755         var p = {
59756             cellId : "x-grid-cell-" + rowIndex + "-" + colIndex,
59757             id : cm.getColumnId(colIndex),
59758             css: colIndex == cm.getColumnCount()-1 ? "x-grid-col-last" : ""
59759         };
59760         var renderer = cm.getRenderer(colIndex);
59761         var val = renderer(dm.getValueAt(rowIndex, dataIndex), p, rowIndex, colIndex, dm);
59762         if(typeof val == "undefined" || val === "") {
59763             val = "&#160;";
59764         }
59765         cellText.innerHTML = val;
59766         cell.className = this.cellClass + " " + this.idToCssName(p.cellId) + " " + p.css;
59767         this.syncRowHeights(rowIndex, rowIndex);
59768     },
59769
59770     calcColumnWidth : function(colIndex, maxRowsToMeasure){
59771         var maxWidth = 0;
59772         if(this.grid.autoSizeHeaders){
59773             var h = this.getHeaderCellMeasure(colIndex);
59774             maxWidth = Math.max(maxWidth, h.scrollWidth);
59775         }
59776         var tb, index;
59777         if(this.cm.isLocked(colIndex)){
59778             tb = this.getLockedTable();
59779             index = colIndex;
59780         }else{
59781             tb = this.getBodyTable();
59782             index = colIndex - this.cm.getLockedCount();
59783         }
59784         if(tb && tb.rows){
59785             var rows = tb.rows;
59786             var stopIndex = Math.min(maxRowsToMeasure || rows.length, rows.length);
59787             for(var i = 0; i < stopIndex; i++){
59788                 var cell = rows[i].childNodes[index].firstChild;
59789                 maxWidth = Math.max(maxWidth, cell.scrollWidth);
59790             }
59791         }
59792         return maxWidth + /*margin for error in IE*/ 5;
59793     },
59794     /**
59795      * Autofit a column to its content.
59796      * @param {Number} colIndex
59797      * @param {Boolean} forceMinSize true to force the column to go smaller if possible
59798      */
59799      autoSizeColumn : function(colIndex, forceMinSize, suppressEvent){
59800          if(this.cm.isHidden(colIndex)){
59801              return; // can't calc a hidden column
59802          }
59803         if(forceMinSize){
59804             var cid = this.cm.getColumnId(colIndex);
59805             this.css.updateRule(this.colSelector +this.idToCssName( cid), "width", this.grid.minColumnWidth + "px");
59806            if(this.grid.autoSizeHeaders){
59807                this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", this.grid.minColumnWidth + "px");
59808            }
59809         }
59810         var newWidth = this.calcColumnWidth(colIndex);
59811         this.cm.setColumnWidth(colIndex,
59812             Math.max(this.grid.minColumnWidth, newWidth), suppressEvent);
59813         if(!suppressEvent){
59814             this.grid.fireEvent("columnresize", colIndex, newWidth);
59815         }
59816     },
59817
59818     /**
59819      * Autofits all columns to their content and then expands to fit any extra space in the grid
59820      */
59821      autoSizeColumns : function(){
59822         var cm = this.grid.colModel;
59823         var colCount = cm.getColumnCount();
59824         for(var i = 0; i < colCount; i++){
59825             this.autoSizeColumn(i, true, true);
59826         }
59827         if(cm.getTotalWidth() < this.scroller.dom.clientWidth){
59828             this.fitColumns();
59829         }else{
59830             this.updateColumns();
59831             this.layout();
59832         }
59833     },
59834
59835     /**
59836      * Autofits all columns to the grid's width proportionate with their current size
59837      * @param {Boolean} reserveScrollSpace Reserve space for a scrollbar
59838      */
59839     fitColumns : function(reserveScrollSpace){
59840         var cm = this.grid.colModel;
59841         var colCount = cm.getColumnCount();
59842         var cols = [];
59843         var width = 0;
59844         var i, w;
59845         for (i = 0; i < colCount; i++){
59846             if(!cm.isHidden(i) && !cm.isFixed(i)){
59847                 w = cm.getColumnWidth(i);
59848                 cols.push(i);
59849                 cols.push(w);
59850                 width += w;
59851             }
59852         }
59853         var avail = Math.min(this.scroller.dom.clientWidth, this.el.getWidth());
59854         if(reserveScrollSpace){
59855             avail -= 17;
59856         }
59857         var frac = (avail - cm.getTotalWidth())/width;
59858         while (cols.length){
59859             w = cols.pop();
59860             i = cols.pop();
59861             cm.setColumnWidth(i, Math.floor(w + w*frac), true);
59862         }
59863         this.updateColumns();
59864         this.layout();
59865     },
59866
59867     onRowSelect : function(rowIndex){
59868         var row = this.getRowComposite(rowIndex);
59869         row.addClass("x-grid-row-selected");
59870     },
59871
59872     onRowDeselect : function(rowIndex){
59873         var row = this.getRowComposite(rowIndex);
59874         row.removeClass("x-grid-row-selected");
59875     },
59876
59877     onCellSelect : function(row, col){
59878         var cell = this.getCell(row, col);
59879         if(cell){
59880             Roo.fly(cell).addClass("x-grid-cell-selected");
59881         }
59882     },
59883
59884     onCellDeselect : function(row, col){
59885         var cell = this.getCell(row, col);
59886         if(cell){
59887             Roo.fly(cell).removeClass("x-grid-cell-selected");
59888         }
59889     },
59890
59891     updateHeaderSortState : function(){
59892         
59893         // sort state can be single { field: xxx, direction : yyy}
59894         // or   { xxx=>ASC , yyy : DESC ..... }
59895         
59896         var mstate = {};
59897         if (!this.ds.multiSort) { 
59898             var state = this.ds.getSortState();
59899             if(!state){
59900                 return;
59901             }
59902             mstate[state.field] = state.direction;
59903             // FIXME... - this is not used here.. but might be elsewhere..
59904             this.sortState = state;
59905             
59906         } else {
59907             mstate = this.ds.sortToggle;
59908         }
59909         //remove existing sort classes..
59910         
59911         var sc = this.sortClasses;
59912         var hds = this.el.select(this.headerSelector).removeClass(sc);
59913         
59914         for(var f in mstate) {
59915         
59916             var sortColumn = this.cm.findColumnIndex(f);
59917             
59918             if(sortColumn != -1){
59919                 var sortDir = mstate[f];        
59920                 hds.item(sortColumn).addClass(sc[sortDir == "DESC" ? 1 : 0]);
59921             }
59922         }
59923         
59924          
59925         
59926     },
59927
59928
59929     handleHeaderClick : function(g, index,e){
59930         
59931         Roo.log("header click");
59932         
59933         if (Roo.isTouch) {
59934             // touch events on header are handled by context
59935             this.handleHdCtx(g,index,e);
59936             return;
59937         }
59938         
59939         
59940         if(this.headersDisabled){
59941             return;
59942         }
59943         var dm = g.dataSource, cm = g.colModel;
59944         if(!cm.isSortable(index)){
59945             return;
59946         }
59947         g.stopEditing();
59948         
59949         if (dm.multiSort) {
59950             // update the sortOrder
59951             var so = [];
59952             for(var i = 0; i < cm.config.length; i++ ) {
59953                 
59954                 if ((typeof(dm.sortToggle[cm.config[i].dataIndex]) == 'undefined') && (index != i)) {
59955                     continue; // dont' bother, it's not in sort list or being set.
59956                 }
59957                 
59958                 so.push(cm.config[i].dataIndex);
59959             };
59960             dm.sortOrder = so;
59961         }
59962         
59963         
59964         dm.sort(cm.getDataIndex(index));
59965     },
59966
59967
59968     destroy : function(){
59969         if(this.colMenu){
59970             this.colMenu.removeAll();
59971             Roo.menu.MenuMgr.unregister(this.colMenu);
59972             this.colMenu.getEl().remove();
59973             delete this.colMenu;
59974         }
59975         if(this.hmenu){
59976             this.hmenu.removeAll();
59977             Roo.menu.MenuMgr.unregister(this.hmenu);
59978             this.hmenu.getEl().remove();
59979             delete this.hmenu;
59980         }
59981         if(this.grid.enableColumnMove){
59982             var dds = Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
59983             if(dds){
59984                 for(var dd in dds){
59985                     if(!dds[dd].config.isTarget && dds[dd].dragElId){
59986                         var elid = dds[dd].dragElId;
59987                         dds[dd].unreg();
59988                         Roo.get(elid).remove();
59989                     } else if(dds[dd].config.isTarget){
59990                         dds[dd].proxyTop.remove();
59991                         dds[dd].proxyBottom.remove();
59992                         dds[dd].unreg();
59993                     }
59994                     if(Roo.dd.DDM.locationCache[dd]){
59995                         delete Roo.dd.DDM.locationCache[dd];
59996                     }
59997                 }
59998                 delete Roo.dd.DDM.ids['gridHeader' + this.grid.getGridEl().id];
59999             }
60000         }
60001         Roo.util.CSS.removeStyleSheet(this.idToCssName(this.grid.id) + '-cssrules');
60002         this.bind(null, null);
60003         Roo.EventManager.removeResizeListener(this.onWindowResize, this);
60004     },
60005
60006     handleLockChange : function(){
60007         this.refresh(true);
60008     },
60009
60010     onDenyColumnLock : function(){
60011
60012     },
60013
60014     onDenyColumnHide : function(){
60015
60016     },
60017
60018     handleHdMenuClick : function(item){
60019         var index = this.hdCtxIndex;
60020         var cm = this.cm, ds = this.ds;
60021         switch(item.id){
60022             case "asc":
60023                 ds.sort(cm.getDataIndex(index), "ASC");
60024                 break;
60025             case "desc":
60026                 ds.sort(cm.getDataIndex(index), "DESC");
60027                 break;
60028             case "lock":
60029                 var lc = cm.getLockedCount();
60030                 if(cm.getColumnCount(true) <= lc+1){
60031                     this.onDenyColumnLock();
60032                     return;
60033                 }
60034                 if(lc != index){
60035                     cm.setLocked(index, true, true);
60036                     cm.moveColumn(index, lc);
60037                     this.grid.fireEvent("columnmove", index, lc);
60038                 }else{
60039                     cm.setLocked(index, true);
60040                 }
60041             break;
60042             case "unlock":
60043                 var lc = cm.getLockedCount();
60044                 if((lc-1) != index){
60045                     cm.setLocked(index, false, true);
60046                     cm.moveColumn(index, lc-1);
60047                     this.grid.fireEvent("columnmove", index, lc-1);
60048                 }else{
60049                     cm.setLocked(index, false);
60050                 }
60051             break;
60052             case 'wider': // used to expand cols on touch..
60053             case 'narrow':
60054                 var cw = cm.getColumnWidth(index);
60055                 cw += (item.id == 'wider' ? 1 : -1) * 50;
60056                 cw = Math.max(0, cw);
60057                 cw = Math.min(cw,4000);
60058                 cm.setColumnWidth(index, cw);
60059                 break;
60060                 
60061             default:
60062                 index = cm.getIndexById(item.id.substr(4));
60063                 if(index != -1){
60064                     if(item.checked && cm.getColumnCount(true) <= 1){
60065                         this.onDenyColumnHide();
60066                         return false;
60067                     }
60068                     cm.setHidden(index, item.checked);
60069                 }
60070         }
60071         return true;
60072     },
60073
60074     beforeColMenuShow : function(){
60075         var cm = this.cm,  colCount = cm.getColumnCount();
60076         this.colMenu.removeAll();
60077         for(var i = 0; i < colCount; i++){
60078             this.colMenu.add(new Roo.menu.CheckItem({
60079                 id: "col-"+cm.getColumnId(i),
60080                 text: cm.getColumnHeader(i),
60081                 checked: !cm.isHidden(i),
60082                 hideOnClick:false
60083             }));
60084         }
60085     },
60086
60087     handleHdCtx : function(g, index, e){
60088         e.stopEvent();
60089         var hd = this.getHeaderCell(index);
60090         this.hdCtxIndex = index;
60091         var ms = this.hmenu.items, cm = this.cm;
60092         ms.get("asc").setDisabled(!cm.isSortable(index));
60093         ms.get("desc").setDisabled(!cm.isSortable(index));
60094         if(this.grid.enableColLock !== false){
60095             ms.get("lock").setDisabled(cm.isLocked(index));
60096             ms.get("unlock").setDisabled(!cm.isLocked(index));
60097         }
60098         this.hmenu.show(hd, "tl-bl");
60099     },
60100
60101     handleHdOver : function(e){
60102         var hd = this.findHeaderCell(e.getTarget());
60103         if(hd && !this.headersDisabled){
60104             if(this.grid.colModel.isSortable(this.getCellIndex(hd))){
60105                this.fly(hd).addClass("x-grid-hd-over");
60106             }
60107         }
60108     },
60109
60110     handleHdOut : function(e){
60111         var hd = this.findHeaderCell(e.getTarget());
60112         if(hd){
60113             this.fly(hd).removeClass("x-grid-hd-over");
60114         }
60115     },
60116
60117     handleSplitDblClick : function(e, t){
60118         var i = this.getCellIndex(t);
60119         if(this.grid.enableColumnResize !== false && this.cm.isResizable(i) && !this.cm.isFixed(i)){
60120             this.autoSizeColumn(i, true);
60121             this.layout();
60122         }
60123     },
60124
60125     render : function(){
60126
60127         var cm = this.cm;
60128         var colCount = cm.getColumnCount();
60129
60130         if(this.grid.monitorWindowResize === true){
60131             Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
60132         }
60133         var header = this.renderHeaders();
60134         var body = this.templates.body.apply({rows:""});
60135         var html = this.templates.master.apply({
60136             lockedBody: body,
60137             body: body,
60138             lockedHeader: header[0],
60139             header: header[1]
60140         });
60141
60142         //this.updateColumns();
60143
60144         this.grid.getGridEl().dom.innerHTML = html;
60145
60146         this.initElements();
60147         
60148         // a kludge to fix the random scolling effect in webkit
60149         this.el.on("scroll", function() {
60150             this.el.dom.scrollTop=0; // hopefully not recursive..
60151         },this);
60152
60153         this.scroller.on("scroll", this.handleScroll, this);
60154         this.lockedBody.on("mousewheel", this.handleWheel, this);
60155         this.mainBody.on("mousewheel", this.handleWheel, this);
60156
60157         this.mainHd.on("mouseover", this.handleHdOver, this);
60158         this.mainHd.on("mouseout", this.handleHdOut, this);
60159         this.mainHd.on("dblclick", this.handleSplitDblClick, this,
60160                 {delegate: "."+this.splitClass});
60161
60162         this.lockedHd.on("mouseover", this.handleHdOver, this);
60163         this.lockedHd.on("mouseout", this.handleHdOut, this);
60164         this.lockedHd.on("dblclick", this.handleSplitDblClick, this,
60165                 {delegate: "."+this.splitClass});
60166
60167         if(this.grid.enableColumnResize !== false && Roo.grid.SplitDragZone){
60168             new Roo.grid.SplitDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60169         }
60170
60171         this.updateSplitters();
60172
60173         if(this.grid.enableColumnMove && Roo.grid.HeaderDragZone){
60174             new Roo.grid.HeaderDragZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60175             new Roo.grid.HeaderDropZone(this.grid, this.lockedHd.dom, this.mainHd.dom);
60176         }
60177
60178         if(this.grid.enableCtxMenu !== false && Roo.menu.Menu){
60179             this.hmenu = new Roo.menu.Menu({id: this.grid.id + "-hctx"});
60180             this.hmenu.add(
60181                 {id:"asc", text: this.sortAscText, cls: "xg-hmenu-sort-asc"},
60182                 {id:"desc", text: this.sortDescText, cls: "xg-hmenu-sort-desc"}
60183             );
60184             if(this.grid.enableColLock !== false){
60185                 this.hmenu.add('-',
60186                     {id:"lock", text: this.lockText, cls: "xg-hmenu-lock"},
60187                     {id:"unlock", text: this.unlockText, cls: "xg-hmenu-unlock"}
60188                 );
60189             }
60190             if (Roo.isTouch) {
60191                  this.hmenu.add('-',
60192                     {id:"wider", text: this.columnsWiderText},
60193                     {id:"narrow", text: this.columnsNarrowText }
60194                 );
60195                 
60196                  
60197             }
60198             
60199             if(this.grid.enableColumnHide !== false){
60200
60201                 this.colMenu = new Roo.menu.Menu({id:this.grid.id + "-hcols-menu"});
60202                 this.colMenu.on("beforeshow", this.beforeColMenuShow, this);
60203                 this.colMenu.on("itemclick", this.handleHdMenuClick, this);
60204
60205                 this.hmenu.add('-',
60206                     {id:"columns", text: this.columnsText, menu: this.colMenu}
60207                 );
60208             }
60209             this.hmenu.on("itemclick", this.handleHdMenuClick, this);
60210
60211             this.grid.on("headercontextmenu", this.handleHdCtx, this);
60212         }
60213
60214         if((this.grid.enableDragDrop || this.grid.enableDrag) && Roo.grid.GridDragZone){
60215             this.dd = new Roo.grid.GridDragZone(this.grid, {
60216                 ddGroup : this.grid.ddGroup || 'GridDD'
60217             });
60218             
60219         }
60220
60221         /*
60222         for(var i = 0; i < colCount; i++){
60223             if(cm.isHidden(i)){
60224                 this.hideColumn(i);
60225             }
60226             if(cm.config[i].align){
60227                 this.css.updateRule(this.colSelector + i, "textAlign", cm.config[i].align);
60228                 this.css.updateRule(this.hdSelector + i, "textAlign", cm.config[i].align);
60229             }
60230         }*/
60231         
60232         this.updateHeaderSortState();
60233
60234         this.beforeInitialResize();
60235         this.layout(true);
60236
60237         // two part rendering gives faster view to the user
60238         this.renderPhase2.defer(1, this);
60239     },
60240
60241     renderPhase2 : function(){
60242         // render the rows now
60243         this.refresh();
60244         if(this.grid.autoSizeColumns){
60245             this.autoSizeColumns();
60246         }
60247     },
60248
60249     beforeInitialResize : function(){
60250
60251     },
60252
60253     onColumnSplitterMoved : function(i, w){
60254         this.userResized = true;
60255         var cm = this.grid.colModel;
60256         cm.setColumnWidth(i, w, true);
60257         var cid = cm.getColumnId(i);
60258         this.css.updateRule(this.colSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60259         this.css.updateRule(this.hdSelector + this.idToCssName(cid), "width", (w-this.borderWidth) + "px");
60260         this.updateSplitters();
60261         this.layout();
60262         this.grid.fireEvent("columnresize", i, w);
60263     },
60264
60265     syncRowHeights : function(startIndex, endIndex){
60266         if(this.grid.enableRowHeightSync === true && this.cm.getLockedCount() > 0){
60267             startIndex = startIndex || 0;
60268             var mrows = this.getBodyTable().rows;
60269             var lrows = this.getLockedTable().rows;
60270             var len = mrows.length-1;
60271             endIndex = Math.min(endIndex || len, len);
60272             for(var i = startIndex; i <= endIndex; i++){
60273                 var m = mrows[i], l = lrows[i];
60274                 var h = Math.max(m.offsetHeight, l.offsetHeight);
60275                 m.style.height = l.style.height = h + "px";
60276             }
60277         }
60278     },
60279
60280     layout : function(initialRender, is2ndPass)
60281     {
60282         var g = this.grid;
60283         var auto = g.autoHeight;
60284         var scrollOffset = 16;
60285         var c = g.getGridEl(), cm = this.cm,
60286                 expandCol = g.autoExpandColumn,
60287                 gv = this;
60288         //c.beginMeasure();
60289
60290         if(!c.dom.offsetWidth){ // display:none?
60291             if(initialRender){
60292                 this.lockedWrap.show();
60293                 this.mainWrap.show();
60294             }
60295             return;
60296         }
60297
60298         var hasLock = this.cm.isLocked(0);
60299
60300         var tbh = this.headerPanel.getHeight();
60301         var bbh = this.footerPanel.getHeight();
60302
60303         if(auto){
60304             var ch = this.getBodyTable().offsetHeight + tbh + bbh + this.mainHd.getHeight();
60305             var newHeight = ch + c.getBorderWidth("tb");
60306             if(g.maxHeight){
60307                 newHeight = Math.min(g.maxHeight, newHeight);
60308             }
60309             c.setHeight(newHeight);
60310         }
60311
60312         if(g.autoWidth){
60313             c.setWidth(cm.getTotalWidth()+c.getBorderWidth('lr'));
60314         }
60315
60316         var s = this.scroller;
60317
60318         var csize = c.getSize(true);
60319
60320         this.el.setSize(csize.width, csize.height);
60321
60322         this.headerPanel.setWidth(csize.width);
60323         this.footerPanel.setWidth(csize.width);
60324
60325         var hdHeight = this.mainHd.getHeight();
60326         var vw = csize.width;
60327         var vh = csize.height - (tbh + bbh);
60328
60329         s.setSize(vw, vh);
60330
60331         var bt = this.getBodyTable();
60332         
60333         if(cm.getLockedCount() == cm.config.length){
60334             bt = this.getLockedTable();
60335         }
60336         
60337         var ltWidth = hasLock ?
60338                       Math.max(this.getLockedTable().offsetWidth, this.lockedHd.dom.firstChild.offsetWidth) : 0;
60339
60340         var scrollHeight = bt.offsetHeight;
60341         var scrollWidth = ltWidth + bt.offsetWidth;
60342         var vscroll = false, hscroll = false;
60343
60344         this.scrollSizer.setSize(scrollWidth, scrollHeight+hdHeight);
60345
60346         var lw = this.lockedWrap, mw = this.mainWrap;
60347         var lb = this.lockedBody, mb = this.mainBody;
60348
60349         setTimeout(function(){
60350             var t = s.dom.offsetTop;
60351             var w = s.dom.clientWidth,
60352                 h = s.dom.clientHeight;
60353
60354             lw.setTop(t);
60355             lw.setSize(ltWidth, h);
60356
60357             mw.setLeftTop(ltWidth, t);
60358             mw.setSize(w-ltWidth, h);
60359
60360             lb.setHeight(h-hdHeight);
60361             mb.setHeight(h-hdHeight);
60362
60363             if(is2ndPass !== true && !gv.userResized && expandCol){
60364                 // high speed resize without full column calculation
60365                 
60366                 var ci = cm.getIndexById(expandCol);
60367                 if (ci < 0) {
60368                     ci = cm.findColumnIndex(expandCol);
60369                 }
60370                 ci = Math.max(0, ci); // make sure it's got at least the first col.
60371                 var expandId = cm.getColumnId(ci);
60372                 var  tw = cm.getTotalWidth(false);
60373                 var currentWidth = cm.getColumnWidth(ci);
60374                 var cw = Math.min(Math.max(((w-tw)+currentWidth-2)-/*scrollbar*/(w <= s.dom.offsetWidth ? 0 : 18), g.autoExpandMin), g.autoExpandMax);
60375                 if(currentWidth != cw){
60376                     cm.setColumnWidth(ci, cw, true);
60377                     gv.css.updateRule(gv.colSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60378                     gv.css.updateRule(gv.hdSelector+gv.idToCssName(expandId), "width", (cw - gv.borderWidth) + "px");
60379                     gv.updateSplitters();
60380                     gv.layout(false, true);
60381                 }
60382             }
60383
60384             if(initialRender){
60385                 lw.show();
60386                 mw.show();
60387             }
60388             //c.endMeasure();
60389         }, 10);
60390     },
60391
60392     onWindowResize : function(){
60393         if(!this.grid.monitorWindowResize || this.grid.autoHeight){
60394             return;
60395         }
60396         this.layout();
60397     },
60398
60399     appendFooter : function(parentEl){
60400         return null;
60401     },
60402
60403     sortAscText : "Sort Ascending",
60404     sortDescText : "Sort Descending",
60405     lockText : "Lock Column",
60406     unlockText : "Unlock Column",
60407     columnsText : "Columns",
60408  
60409     columnsWiderText : "Wider",
60410     columnsNarrowText : "Thinner"
60411 });
60412
60413
60414 Roo.grid.GridView.ColumnDragZone = function(grid, hd){
60415     Roo.grid.GridView.ColumnDragZone.superclass.constructor.call(this, grid, hd, null);
60416     this.proxy.el.addClass('x-grid3-col-dd');
60417 };
60418
60419 Roo.extend(Roo.grid.GridView.ColumnDragZone, Roo.grid.HeaderDragZone, {
60420     handleMouseDown : function(e){
60421
60422     },
60423
60424     callHandleMouseDown : function(e){
60425         Roo.grid.GridView.ColumnDragZone.superclass.handleMouseDown.call(this, e);
60426     }
60427 });
60428 /*
60429  * Based on:
60430  * Ext JS Library 1.1.1
60431  * Copyright(c) 2006-2007, Ext JS, LLC.
60432  *
60433  * Originally Released Under LGPL - original licence link has changed is not relivant.
60434  *
60435  * Fork - LGPL
60436  * <script type="text/javascript">
60437  */
60438  /**
60439  * @extends Roo.dd.DDProxy
60440  * @class Roo.grid.SplitDragZone
60441  * Support for Column Header resizing
60442  * @constructor
60443  * @param {Object} config
60444  */
60445 // private
60446 // This is a support class used internally by the Grid components
60447 Roo.grid.SplitDragZone = function(grid, hd, hd2){
60448     this.grid = grid;
60449     this.view = grid.getView();
60450     this.proxy = this.view.resizeProxy;
60451     Roo.grid.SplitDragZone.superclass.constructor.call(
60452         this,
60453         hd, // ID
60454         "gridSplitters" + this.grid.getGridEl().id, // SGROUP
60455         {  // CONFIG
60456             dragElId : Roo.id(this.proxy.dom),
60457             resizeFrame:false
60458         }
60459     );
60460     
60461     this.setHandleElId(Roo.id(hd));
60462     if (hd2 !== false) {
60463         this.setOuterHandleElId(Roo.id(hd2));
60464     }
60465     
60466     this.scroll = false;
60467 };
60468 Roo.extend(Roo.grid.SplitDragZone, Roo.dd.DDProxy, {
60469     fly: Roo.Element.fly,
60470
60471     b4StartDrag : function(x, y){
60472         this.view.headersDisabled = true;
60473         var h = this.view.mainWrap ? this.view.mainWrap.getHeight() : (
60474                     this.view.headEl.getHeight() + this.view.bodyEl.getHeight()
60475         );
60476         this.proxy.setHeight(h);
60477         
60478         // for old system colWidth really stored the actual width?
60479         // in bootstrap we tried using xs/ms/etc.. to do % sizing?
60480         // which in reality did not work.. - it worked only for fixed sizes
60481         // for resizable we need to use actual sizes.
60482         var w = this.cm.getColumnWidth(this.cellIndex);
60483         if (!this.view.mainWrap) {
60484             // bootstrap.
60485             w = this.view.getHeaderIndex(this.cellIndex).getWidth();
60486         }
60487         
60488         
60489         
60490         // this was w-this.grid.minColumnWidth;
60491         // doesnt really make sense? - w = thie curren width or the rendered one?
60492         var minw = Math.max(w-this.grid.minColumnWidth, 0);
60493         this.resetConstraints();
60494         this.setXConstraint(minw, 1000);
60495         this.setYConstraint(0, 0);
60496         this.minX = x - minw;
60497         this.maxX = x + 1000;
60498         this.startPos = x;
60499         if (!this.view.mainWrap) { // this is Bootstrap code..
60500             this.getDragEl().style.display='block';
60501         }
60502         
60503         Roo.dd.DDProxy.prototype.b4StartDrag.call(this, x, y);
60504     },
60505
60506
60507     handleMouseDown : function(e){
60508         ev = Roo.EventObject.setEvent(e);
60509         var t = this.fly(ev.getTarget());
60510         if(t.hasClass("x-grid-split")){
60511             this.cellIndex = this.view.getCellIndex(t.dom);
60512             this.split = t.dom;
60513             this.cm = this.grid.colModel;
60514             if(this.cm.isResizable(this.cellIndex) && !this.cm.isFixed(this.cellIndex)){
60515                 Roo.grid.SplitDragZone.superclass.handleMouseDown.apply(this, arguments);
60516             }
60517         }
60518     },
60519
60520     endDrag : function(e){
60521         this.view.headersDisabled = false;
60522         var endX = Math.max(this.minX, Roo.lib.Event.getPageX(e));
60523         var diff = endX - this.startPos;
60524         // 
60525         var w = this.cm.getColumnWidth(this.cellIndex);
60526         if (!this.view.mainWrap) {
60527             w = 0;
60528         }
60529         this.view.onColumnSplitterMoved(this.cellIndex, w+diff);
60530     },
60531
60532     autoOffset : function(){
60533         this.setDelta(0,0);
60534     }
60535 });/*
60536  * Based on:
60537  * Ext JS Library 1.1.1
60538  * Copyright(c) 2006-2007, Ext JS, LLC.
60539  *
60540  * Originally Released Under LGPL - original licence link has changed is not relivant.
60541  *
60542  * Fork - LGPL
60543  * <script type="text/javascript">
60544  */
60545  
60546 // private
60547 // This is a support class used internally by the Grid components
60548 Roo.grid.GridDragZone = function(grid, config){
60549     this.view = grid.getView();
60550     Roo.grid.GridDragZone.superclass.constructor.call(this, this.view.mainBody.dom, config);
60551     if(this.view.lockedBody){
60552         this.setHandleElId(Roo.id(this.view.mainBody.dom));
60553         this.setOuterHandleElId(Roo.id(this.view.lockedBody.dom));
60554     }
60555     this.scroll = false;
60556     this.grid = grid;
60557     this.ddel = document.createElement('div');
60558     this.ddel.className = 'x-grid-dd-wrap';
60559 };
60560
60561 Roo.extend(Roo.grid.GridDragZone, Roo.dd.DragZone, {
60562     ddGroup : "GridDD",
60563
60564     getDragData : function(e){
60565         var t = Roo.lib.Event.getTarget(e);
60566         var rowIndex = this.view.findRowIndex(t);
60567         var sm = this.grid.selModel;
60568             
60569         //Roo.log(rowIndex);
60570         
60571         if (sm.getSelectedCell) {
60572             // cell selection..
60573             if (!sm.getSelectedCell()) {
60574                 return false;
60575             }
60576             if (rowIndex != sm.getSelectedCell()[0]) {
60577                 return false;
60578             }
60579         
60580         }
60581         if (sm.getSelections && sm.getSelections().length < 1) {
60582             return false;
60583         }
60584         
60585         
60586         // before it used to all dragging of unseleted... - now we dont do that.
60587         if(rowIndex !== false){
60588             
60589             // if editorgrid.. 
60590             
60591             
60592             //Roo.log([ sm.getSelectedCell() ? sm.getSelectedCell()[0] : 'NO' , rowIndex ]);
60593                
60594             //if(!sm.isSelected(rowIndex) || e.hasModifier()){
60595               //  
60596             //}
60597             if (e.hasModifier()){
60598                 sm.handleMouseDown(e, t); // non modifier buttons are handled by row select.
60599             }
60600             
60601             Roo.log("getDragData");
60602             
60603             return {
60604                 grid: this.grid,
60605                 ddel: this.ddel,
60606                 rowIndex: rowIndex,
60607                 selections: sm.getSelections ? sm.getSelections() : (
60608                     sm.getSelectedCell() ? [ this.grid.ds.getAt(sm.getSelectedCell()[0]) ] : [])
60609             };
60610         }
60611         return false;
60612     },
60613     
60614     
60615     onInitDrag : function(e){
60616         var data = this.dragData;
60617         this.ddel.innerHTML = this.grid.getDragDropText();
60618         this.proxy.update(this.ddel);
60619         // fire start drag?
60620     },
60621
60622     afterRepair : function(){
60623         this.dragging = false;
60624     },
60625
60626     getRepairXY : function(e, data){
60627         return false;
60628     },
60629
60630     onEndDrag : function(data, e){
60631         // fire end drag?
60632     },
60633
60634     onValidDrop : function(dd, e, id){
60635         // fire drag drop?
60636         this.hideProxy();
60637     },
60638
60639     beforeInvalidDrop : function(e, id){
60640
60641     }
60642 });/*
60643  * Based on:
60644  * Ext JS Library 1.1.1
60645  * Copyright(c) 2006-2007, Ext JS, LLC.
60646  *
60647  * Originally Released Under LGPL - original licence link has changed is not relivant.
60648  *
60649  * Fork - LGPL
60650  * <script type="text/javascript">
60651  */
60652  
60653
60654 /**
60655  * @class Roo.grid.ColumnModel
60656  * @extends Roo.util.Observable
60657  * This is the default implementation of a ColumnModel used by the Grid. It defines
60658  * the columns in the grid.
60659  * <br>Usage:<br>
60660  <pre><code>
60661  var colModel = new Roo.grid.ColumnModel([
60662         {header: "Ticker", width: 60, sortable: true, locked: true},
60663         {header: "Company Name", width: 150, sortable: true},
60664         {header: "Market Cap.", width: 100, sortable: true},
60665         {header: "$ Sales", width: 100, sortable: true, renderer: money},
60666         {header: "Employees", width: 100, sortable: true, resizable: false}
60667  ]);
60668  </code></pre>
60669  * <p>
60670  
60671  * The config options listed for this class are options which may appear in each
60672  * individual column definition.
60673  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
60674  * @constructor
60675  * @param {Object} config An Array of column config objects. See this class's
60676  * config objects for details.
60677 */
60678 Roo.grid.ColumnModel = function(config){
60679         /**
60680      * The config passed into the constructor
60681      */
60682     this.config = []; //config;
60683     this.lookup = {};
60684
60685     // if no id, create one
60686     // if the column does not have a dataIndex mapping,
60687     // map it to the order it is in the config
60688     for(var i = 0, len = config.length; i < len; i++){
60689         this.addColumn(config[i]);
60690         
60691     }
60692
60693     /**
60694      * The width of columns which have no width specified (defaults to 100)
60695      * @type Number
60696      */
60697     this.defaultWidth = 100;
60698
60699     /**
60700      * Default sortable of columns which have no sortable specified (defaults to false)
60701      * @type Boolean
60702      */
60703     this.defaultSortable = false;
60704
60705     this.addEvents({
60706         /**
60707              * @event widthchange
60708              * Fires when the width of a column changes.
60709              * @param {ColumnModel} this
60710              * @param {Number} columnIndex The column index
60711              * @param {Number} newWidth The new width
60712              */
60713             "widthchange": true,
60714         /**
60715              * @event headerchange
60716              * Fires when the text of a header changes.
60717              * @param {ColumnModel} this
60718              * @param {Number} columnIndex The column index
60719              * @param {Number} newText The new header text
60720              */
60721             "headerchange": true,
60722         /**
60723              * @event hiddenchange
60724              * Fires when a column is hidden or "unhidden".
60725              * @param {ColumnModel} this
60726              * @param {Number} columnIndex The column index
60727              * @param {Boolean} hidden true if hidden, false otherwise
60728              */
60729             "hiddenchange": true,
60730             /**
60731          * @event columnmoved
60732          * Fires when a column is moved.
60733          * @param {ColumnModel} this
60734          * @param {Number} oldIndex
60735          * @param {Number} newIndex
60736          */
60737         "columnmoved" : true,
60738         /**
60739          * @event columlockchange
60740          * Fires when a column's locked state is changed
60741          * @param {ColumnModel} this
60742          * @param {Number} colIndex
60743          * @param {Boolean} locked true if locked
60744          */
60745         "columnlockchange" : true
60746     });
60747     Roo.grid.ColumnModel.superclass.constructor.call(this);
60748 };
60749 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
60750     /**
60751      * @cfg {String} header The header text to display in the Grid view.
60752      */
60753         /**
60754      * @cfg {String} xsHeader Header at Bootsrap Extra Small width (default for all)
60755      */
60756         /**
60757      * @cfg {String} smHeader Header at Bootsrap Small width
60758      */
60759         /**
60760      * @cfg {String} mdHeader Header at Bootsrap Medium width
60761      */
60762         /**
60763      * @cfg {String} lgHeader Header at Bootsrap Large width
60764      */
60765         /**
60766      * @cfg {String} xlHeader Header at Bootsrap extra Large width
60767      */
60768     /**
60769      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
60770      * {@link Roo.data.Record} definition from which to draw the column's value. If not
60771      * specified, the column's index is used as an index into the Record's data Array.
60772      */
60773     /**
60774      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
60775      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
60776      */
60777     /**
60778      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
60779      * Defaults to the value of the {@link #defaultSortable} property.
60780      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
60781      */
60782     /**
60783      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
60784      */
60785     /**
60786      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
60787      */
60788     /**
60789      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
60790      */
60791     /**
60792      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
60793      */
60794     /**
60795      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
60796      * given the cell's data value. See {@link #setRenderer}. If not specified, the
60797      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
60798      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
60799      */
60800        /**
60801      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
60802      */
60803     /**
60804      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
60805      */
60806     /**
60807      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
60808      */
60809     /**
60810      * @cfg {String} cursor (Optional)
60811      */
60812     /**
60813      * @cfg {String} tooltip (Optional)
60814      */
60815     /**
60816      * @cfg {Number} xs (Optional) can be '0' for hidden at this size (number less than 12)
60817      */
60818     /**
60819      * @cfg {Number} sm (Optional) can be '0' for hidden at this size (number less than 12)
60820      */
60821     /**
60822      * @cfg {Number} md (Optional) can be '0' for hidden at this size (number less than 12)
60823      */
60824     /**
60825      * @cfg {Number} lg (Optional) can be '0' for hidden at this size (number less than 12)
60826      */
60827         /**
60828      * @cfg {Number} xl (Optional) can be '0' for hidden at this size (number less than 12)
60829      */
60830     /**
60831      * Returns the id of the column at the specified index.
60832      * @param {Number} index The column index
60833      * @return {String} the id
60834      */
60835     getColumnId : function(index){
60836         return this.config[index].id;
60837     },
60838
60839     /**
60840      * Returns the column for a specified id.
60841      * @param {String} id The column id
60842      * @return {Object} the column
60843      */
60844     getColumnById : function(id){
60845         return this.lookup[id];
60846     },
60847
60848     
60849     /**
60850      * Returns the column Object for a specified dataIndex.
60851      * @param {String} dataIndex The column dataIndex
60852      * @return {Object|Boolean} the column or false if not found
60853      */
60854     getColumnByDataIndex: function(dataIndex){
60855         var index = this.findColumnIndex(dataIndex);
60856         return index > -1 ? this.config[index] : false;
60857     },
60858     
60859     /**
60860      * Returns the index for a specified column id.
60861      * @param {String} id The column id
60862      * @return {Number} the index, or -1 if not found
60863      */
60864     getIndexById : function(id){
60865         for(var i = 0, len = this.config.length; i < len; i++){
60866             if(this.config[i].id == id){
60867                 return i;
60868             }
60869         }
60870         return -1;
60871     },
60872     
60873     /**
60874      * Returns the index for a specified column dataIndex.
60875      * @param {String} dataIndex The column dataIndex
60876      * @return {Number} the index, or -1 if not found
60877      */
60878     
60879     findColumnIndex : function(dataIndex){
60880         for(var i = 0, len = this.config.length; i < len; i++){
60881             if(this.config[i].dataIndex == dataIndex){
60882                 return i;
60883             }
60884         }
60885         return -1;
60886     },
60887     
60888     
60889     moveColumn : function(oldIndex, newIndex){
60890         var c = this.config[oldIndex];
60891         this.config.splice(oldIndex, 1);
60892         this.config.splice(newIndex, 0, c);
60893         this.dataMap = null;
60894         this.fireEvent("columnmoved", this, oldIndex, newIndex);
60895     },
60896
60897     isLocked : function(colIndex){
60898         return this.config[colIndex].locked === true;
60899     },
60900
60901     setLocked : function(colIndex, value, suppressEvent){
60902         if(this.isLocked(colIndex) == value){
60903             return;
60904         }
60905         this.config[colIndex].locked = value;
60906         if(!suppressEvent){
60907             this.fireEvent("columnlockchange", this, colIndex, value);
60908         }
60909     },
60910
60911     getTotalLockedWidth : function(){
60912         var totalWidth = 0;
60913         for(var i = 0; i < this.config.length; i++){
60914             if(this.isLocked(i) && !this.isHidden(i)){
60915                 this.totalWidth += this.getColumnWidth(i);
60916             }
60917         }
60918         return totalWidth;
60919     },
60920
60921     getLockedCount : function(){
60922         for(var i = 0, len = this.config.length; i < len; i++){
60923             if(!this.isLocked(i)){
60924                 return i;
60925             }
60926         }
60927         
60928         return this.config.length;
60929     },
60930
60931     /**
60932      * Returns the number of columns.
60933      * @return {Number}
60934      */
60935     getColumnCount : function(visibleOnly){
60936         if(visibleOnly === true){
60937             var c = 0;
60938             for(var i = 0, len = this.config.length; i < len; i++){
60939                 if(!this.isHidden(i)){
60940                     c++;
60941                 }
60942             }
60943             return c;
60944         }
60945         return this.config.length;
60946     },
60947
60948     /**
60949      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
60950      * @param {Function} fn
60951      * @param {Object} scope (optional)
60952      * @return {Array} result
60953      */
60954     getColumnsBy : function(fn, scope){
60955         var r = [];
60956         for(var i = 0, len = this.config.length; i < len; i++){
60957             var c = this.config[i];
60958             if(fn.call(scope||this, c, i) === true){
60959                 r[r.length] = c;
60960             }
60961         }
60962         return r;
60963     },
60964
60965     /**
60966      * Returns true if the specified column is sortable.
60967      * @param {Number} col The column index
60968      * @return {Boolean}
60969      */
60970     isSortable : function(col){
60971         if(typeof this.config[col].sortable == "undefined"){
60972             return this.defaultSortable;
60973         }
60974         return this.config[col].sortable;
60975     },
60976
60977     /**
60978      * Returns the rendering (formatting) function defined for the column.
60979      * @param {Number} col The column index.
60980      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
60981      */
60982     getRenderer : function(col){
60983         if(!this.config[col].renderer){
60984             return Roo.grid.ColumnModel.defaultRenderer;
60985         }
60986         return this.config[col].renderer;
60987     },
60988
60989     /**
60990      * Sets the rendering (formatting) function for a column.
60991      * @param {Number} col The column index
60992      * @param {Function} fn The function to use to process the cell's raw data
60993      * to return HTML markup for the grid view. The render function is called with
60994      * the following parameters:<ul>
60995      * <li>Data value.</li>
60996      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
60997      * <li>css A CSS style string to apply to the table cell.</li>
60998      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
60999      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
61000      * <li>Row index</li>
61001      * <li>Column index</li>
61002      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
61003      */
61004     setRenderer : function(col, fn){
61005         this.config[col].renderer = fn;
61006     },
61007
61008     /**
61009      * Returns the width for the specified column.
61010      * @param {Number} col The column index
61011      * @param (optional) {String} gridSize bootstrap width size.
61012      * @return {Number}
61013      */
61014     getColumnWidth : function(col, gridSize)
61015         {
61016                 var cfg = this.config[col];
61017                 
61018                 if (typeof(gridSize) == 'undefined') {
61019                         return cfg.width * 1 || this.defaultWidth;
61020                 }
61021                 if (gridSize === false) { // if we set it..
61022                         return cfg.width || false;
61023                 }
61024                 var sizes = ['xl', 'lg', 'md', 'sm', 'xs'];
61025                 
61026                 for(var i = sizes.indexOf(gridSize); i < sizes.length; i++) {
61027                         if (typeof(cfg[ sizes[i] ] ) == 'undefined') {
61028                                 continue;
61029                         }
61030                         return cfg[ sizes[i] ];
61031                 }
61032                 return 1;
61033                 
61034     },
61035
61036     /**
61037      * Sets the width for a column.
61038      * @param {Number} col The column index
61039      * @param {Number} width The new width
61040      */
61041     setColumnWidth : function(col, width, suppressEvent){
61042         this.config[col].width = width;
61043         this.totalWidth = null;
61044         if(!suppressEvent){
61045              this.fireEvent("widthchange", this, col, width);
61046         }
61047     },
61048
61049     /**
61050      * Returns the total width of all columns.
61051      * @param {Boolean} includeHidden True to include hidden column widths
61052      * @return {Number}
61053      */
61054     getTotalWidth : function(includeHidden){
61055         if(!this.totalWidth){
61056             this.totalWidth = 0;
61057             for(var i = 0, len = this.config.length; i < len; i++){
61058                 if(includeHidden || !this.isHidden(i)){
61059                     this.totalWidth += this.getColumnWidth(i);
61060                 }
61061             }
61062         }
61063         return this.totalWidth;
61064     },
61065
61066     /**
61067      * Returns the header for the specified column.
61068      * @param {Number} col The column index
61069      * @return {String}
61070      */
61071     getColumnHeader : function(col){
61072         return this.config[col].header;
61073     },
61074
61075     /**
61076      * Sets the header for a column.
61077      * @param {Number} col The column index
61078      * @param {String} header The new header
61079      */
61080     setColumnHeader : function(col, header){
61081         this.config[col].header = header;
61082         this.fireEvent("headerchange", this, col, header);
61083     },
61084
61085     /**
61086      * Returns the tooltip for the specified column.
61087      * @param {Number} col The column index
61088      * @return {String}
61089      */
61090     getColumnTooltip : function(col){
61091             return this.config[col].tooltip;
61092     },
61093     /**
61094      * Sets the tooltip for a column.
61095      * @param {Number} col The column index
61096      * @param {String} tooltip The new tooltip
61097      */
61098     setColumnTooltip : function(col, tooltip){
61099             this.config[col].tooltip = tooltip;
61100     },
61101
61102     /**
61103      * Returns the dataIndex for the specified column.
61104      * @param {Number} col The column index
61105      * @return {Number}
61106      */
61107     getDataIndex : function(col){
61108         return this.config[col].dataIndex;
61109     },
61110
61111     /**
61112      * Sets the dataIndex for a column.
61113      * @param {Number} col The column index
61114      * @param {Number} dataIndex The new dataIndex
61115      */
61116     setDataIndex : function(col, dataIndex){
61117         this.config[col].dataIndex = dataIndex;
61118     },
61119
61120     
61121     
61122     /**
61123      * Returns true if the cell is editable.
61124      * @param {Number} colIndex The column index
61125      * @param {Number} rowIndex The row index - this is nto actually used..?
61126      * @return {Boolean}
61127      */
61128     isCellEditable : function(colIndex, rowIndex){
61129         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
61130     },
61131
61132     /**
61133      * Returns the editor defined for the cell/column.
61134      * return false or null to disable editing.
61135      * @param {Number} colIndex The column index
61136      * @param {Number} rowIndex The row index
61137      * @return {Object}
61138      */
61139     getCellEditor : function(colIndex, rowIndex){
61140         return this.config[colIndex].editor;
61141     },
61142
61143     /**
61144      * Sets if a column is editable.
61145      * @param {Number} col The column index
61146      * @param {Boolean} editable True if the column is editable
61147      */
61148     setEditable : function(col, editable){
61149         this.config[col].editable = editable;
61150     },
61151
61152
61153     /**
61154      * Returns true if the column is hidden.
61155      * @param {Number} colIndex The column index
61156      * @return {Boolean}
61157      */
61158     isHidden : function(colIndex){
61159         return this.config[colIndex].hidden;
61160     },
61161
61162
61163     /**
61164      * Returns true if the column width cannot be changed
61165      */
61166     isFixed : function(colIndex){
61167         return this.config[colIndex].fixed;
61168     },
61169
61170     /**
61171      * Returns true if the column can be resized
61172      * @return {Boolean}
61173      */
61174     isResizable : function(colIndex){
61175         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
61176     },
61177     /**
61178      * Sets if a column is hidden.
61179      * @param {Number} colIndex The column index
61180      * @param {Boolean} hidden True if the column is hidden
61181      */
61182     setHidden : function(colIndex, hidden){
61183         this.config[colIndex].hidden = hidden;
61184         this.totalWidth = null;
61185         this.fireEvent("hiddenchange", this, colIndex, hidden);
61186     },
61187
61188     /**
61189      * Sets the editor for a column.
61190      * @param {Number} col The column index
61191      * @param {Object} editor The editor object
61192      */
61193     setEditor : function(col, editor){
61194         this.config[col].editor = editor;
61195     },
61196     /**
61197      * Add a column (experimental...) - defaults to adding to the end..
61198      * @param {Object} config 
61199     */
61200     addColumn : function(c)
61201     {
61202     
61203         var i = this.config.length;
61204         this.config[i] = c;
61205         
61206         if(typeof c.dataIndex == "undefined"){
61207             c.dataIndex = i;
61208         }
61209         if(typeof c.renderer == "string"){
61210             c.renderer = Roo.util.Format[c.renderer];
61211         }
61212         if(typeof c.id == "undefined"){
61213             c.id = Roo.id();
61214         }
61215         if(c.editor && c.editor.xtype){
61216             c.editor  = Roo.factory(c.editor, Roo.grid);
61217         }
61218         if(c.editor && c.editor.isFormField){
61219             c.editor = new Roo.grid.GridEditor(c.editor);
61220         }
61221         this.lookup[c.id] = c;
61222     }
61223     
61224 });
61225
61226 Roo.grid.ColumnModel.defaultRenderer = function(value)
61227 {
61228     if(typeof value == "object") {
61229         return value;
61230     }
61231         if(typeof value == "string" && value.length < 1){
61232             return "&#160;";
61233         }
61234     
61235         return String.format("{0}", value);
61236 };
61237
61238 // Alias for backwards compatibility
61239 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
61240 /*
61241  * Based on:
61242  * Ext JS Library 1.1.1
61243  * Copyright(c) 2006-2007, Ext JS, LLC.
61244  *
61245  * Originally Released Under LGPL - original licence link has changed is not relivant.
61246  *
61247  * Fork - LGPL
61248  * <script type="text/javascript">
61249  */
61250
61251 /**
61252  * @class Roo.grid.AbstractSelectionModel
61253  * @extends Roo.util.Observable
61254  * @abstract
61255  * Abstract base class for grid SelectionModels.  It provides the interface that should be
61256  * implemented by descendant classes.  This class should not be directly instantiated.
61257  * @constructor
61258  */
61259 Roo.grid.AbstractSelectionModel = function(){
61260     this.locked = false;
61261     Roo.grid.AbstractSelectionModel.superclass.constructor.call(this);
61262 };
61263
61264 Roo.extend(Roo.grid.AbstractSelectionModel, Roo.util.Observable,  {
61265     /** @ignore Called by the grid automatically. Do not call directly. */
61266     init : function(grid){
61267         this.grid = grid;
61268         this.initEvents();
61269     },
61270
61271     /**
61272      * Locks the selections.
61273      */
61274     lock : function(){
61275         this.locked = true;
61276     },
61277
61278     /**
61279      * Unlocks the selections.
61280      */
61281     unlock : function(){
61282         this.locked = false;
61283     },
61284
61285     /**
61286      * Returns true if the selections are locked.
61287      * @return {Boolean}
61288      */
61289     isLocked : function(){
61290         return this.locked;
61291     }
61292 });/*
61293  * Based on:
61294  * Ext JS Library 1.1.1
61295  * Copyright(c) 2006-2007, Ext JS, LLC.
61296  *
61297  * Originally Released Under LGPL - original licence link has changed is not relivant.
61298  *
61299  * Fork - LGPL
61300  * <script type="text/javascript">
61301  */
61302 /**
61303  * @extends Roo.grid.AbstractSelectionModel
61304  * @class Roo.grid.RowSelectionModel
61305  * The default SelectionModel used by {@link Roo.grid.Grid}.
61306  * It supports multiple selections and keyboard selection/navigation. 
61307  * @constructor
61308  * @param {Object} config
61309  */
61310 Roo.grid.RowSelectionModel = function(config){
61311     Roo.apply(this, config);
61312     this.selections = new Roo.util.MixedCollection(false, function(o){
61313         return o.id;
61314     });
61315
61316     this.last = false;
61317     this.lastActive = false;
61318
61319     this.addEvents({
61320         /**
61321         * @event selectionchange
61322         * Fires when the selection changes
61323         * @param {SelectionModel} this
61324         */
61325        "selectionchange" : true,
61326        /**
61327         * @event afterselectionchange
61328         * Fires after the selection changes (eg. by key press or clicking)
61329         * @param {SelectionModel} this
61330         */
61331        "afterselectionchange" : true,
61332        /**
61333         * @event beforerowselect
61334         * Fires when a row is selected being selected, return false to cancel.
61335         * @param {SelectionModel} this
61336         * @param {Number} rowIndex The selected index
61337         * @param {Boolean} keepExisting False if other selections will be cleared
61338         */
61339        "beforerowselect" : true,
61340        /**
61341         * @event rowselect
61342         * Fires when a row is selected.
61343         * @param {SelectionModel} this
61344         * @param {Number} rowIndex The selected index
61345         * @param {Roo.data.Record} r The record
61346         */
61347        "rowselect" : true,
61348        /**
61349         * @event rowdeselect
61350         * Fires when a row is deselected.
61351         * @param {SelectionModel} this
61352         * @param {Number} rowIndex The selected index
61353         */
61354         "rowdeselect" : true
61355     });
61356     Roo.grid.RowSelectionModel.superclass.constructor.call(this);
61357     this.locked = false;
61358 };
61359
61360 Roo.extend(Roo.grid.RowSelectionModel, Roo.grid.AbstractSelectionModel,  {
61361     /**
61362      * @cfg {Boolean} singleSelect
61363      * True to allow selection of only one row at a time (defaults to false)
61364      */
61365     singleSelect : false,
61366
61367     // private
61368     initEvents : function(){
61369
61370         if(!this.grid.enableDragDrop && !this.grid.enableDrag){
61371             this.grid.on("mousedown", this.handleMouseDown, this);
61372         }else{ // allow click to work like normal
61373             this.grid.on("rowclick", this.handleDragableRowClick, this);
61374         }
61375         // bootstrap does not have a view..
61376         var view = this.grid.view ? this.grid.view : this.grid;
61377         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
61378             "up" : function(e){
61379                 if(!e.shiftKey){
61380                     this.selectPrevious(e.shiftKey);
61381                 }else if(this.last !== false && this.lastActive !== false){
61382                     var last = this.last;
61383                     this.selectRange(this.last,  this.lastActive-1);
61384                     view.focusRow(this.lastActive);
61385                     if(last !== false){
61386                         this.last = last;
61387                     }
61388                 }else{
61389                     this.selectFirstRow();
61390                 }
61391                 this.fireEvent("afterselectionchange", this);
61392             },
61393             "down" : function(e){
61394                 if(!e.shiftKey){
61395                     this.selectNext(e.shiftKey);
61396                 }else if(this.last !== false && this.lastActive !== false){
61397                     var last = this.last;
61398                     this.selectRange(this.last,  this.lastActive+1);
61399                     view.focusRow(this.lastActive);
61400                     if(last !== false){
61401                         this.last = last;
61402                     }
61403                 }else{
61404                     this.selectFirstRow();
61405                 }
61406                 this.fireEvent("afterselectionchange", this);
61407             },
61408             scope: this
61409         });
61410
61411          
61412         view.on("refresh", this.onRefresh, this);
61413         view.on("rowupdated", this.onRowUpdated, this);
61414         view.on("rowremoved", this.onRemove, this);
61415     },
61416
61417     // private
61418     onRefresh : function(){
61419         var ds = this.grid.ds, i, v = this.grid.view;
61420         var s = this.selections;
61421         s.each(function(r){
61422             if((i = ds.indexOfId(r.id)) != -1){
61423                 v.onRowSelect(i);
61424                 s.add(ds.getAt(i)); // updating the selection relate data
61425             }else{
61426                 s.remove(r);
61427             }
61428         });
61429     },
61430
61431     // private
61432     onRemove : function(v, index, r){
61433         this.selections.remove(r);
61434     },
61435
61436     // private
61437     onRowUpdated : function(v, index, r){
61438         if(this.isSelected(r)){
61439             v.onRowSelect(index);
61440         }
61441     },
61442
61443     /**
61444      * Select records.
61445      * @param {Array} records The records to select
61446      * @param {Boolean} keepExisting (optional) True to keep existing selections
61447      */
61448     selectRecords : function(records, keepExisting){
61449         if(!keepExisting){
61450             this.clearSelections();
61451         }
61452         var ds = this.grid.ds;
61453         for(var i = 0, len = records.length; i < len; i++){
61454             this.selectRow(ds.indexOf(records[i]), true);
61455         }
61456     },
61457
61458     /**
61459      * Gets the number of selected rows.
61460      * @return {Number}
61461      */
61462     getCount : function(){
61463         return this.selections.length;
61464     },
61465
61466     /**
61467      * Selects the first row in the grid.
61468      */
61469     selectFirstRow : function(){
61470         this.selectRow(0);
61471     },
61472
61473     /**
61474      * Select the last row.
61475      * @param {Boolean} keepExisting (optional) True to keep existing selections
61476      */
61477     selectLastRow : function(keepExisting){
61478         this.selectRow(this.grid.ds.getCount() - 1, keepExisting);
61479     },
61480
61481     /**
61482      * Selects the row immediately following the last selected row.
61483      * @param {Boolean} keepExisting (optional) True to keep existing selections
61484      */
61485     selectNext : function(keepExisting){
61486         if(this.last !== false && (this.last+1) < this.grid.ds.getCount()){
61487             this.selectRow(this.last+1, keepExisting);
61488             var view = this.grid.view ? this.grid.view : this.grid;
61489             view.focusRow(this.last);
61490         }
61491     },
61492
61493     /**
61494      * Selects the row that precedes the last selected row.
61495      * @param {Boolean} keepExisting (optional) True to keep existing selections
61496      */
61497     selectPrevious : function(keepExisting){
61498         if(this.last){
61499             this.selectRow(this.last-1, keepExisting);
61500             var view = this.grid.view ? this.grid.view : this.grid;
61501             view.focusRow(this.last);
61502         }
61503     },
61504
61505     /**
61506      * Returns the selected records
61507      * @return {Array} Array of selected records
61508      */
61509     getSelections : function(){
61510         return [].concat(this.selections.items);
61511     },
61512
61513     /**
61514      * Returns the first selected record.
61515      * @return {Record}
61516      */
61517     getSelected : function(){
61518         return this.selections.itemAt(0);
61519     },
61520
61521
61522     /**
61523      * Clears all selections.
61524      */
61525     clearSelections : function(fast){
61526         if(this.locked) {
61527             return;
61528         }
61529         if(fast !== true){
61530             var ds = this.grid.ds;
61531             var s = this.selections;
61532             s.each(function(r){
61533                 this.deselectRow(ds.indexOfId(r.id));
61534             }, this);
61535             s.clear();
61536         }else{
61537             this.selections.clear();
61538         }
61539         this.last = false;
61540     },
61541
61542
61543     /**
61544      * Selects all rows.
61545      */
61546     selectAll : function(){
61547         if(this.locked) {
61548             return;
61549         }
61550         this.selections.clear();
61551         for(var i = 0, len = this.grid.ds.getCount(); i < len; i++){
61552             this.selectRow(i, true);
61553         }
61554     },
61555
61556     /**
61557      * Returns True if there is a selection.
61558      * @return {Boolean}
61559      */
61560     hasSelection : function(){
61561         return this.selections.length > 0;
61562     },
61563
61564     /**
61565      * Returns True if the specified row is selected.
61566      * @param {Number/Record} record The record or index of the record to check
61567      * @return {Boolean}
61568      */
61569     isSelected : function(index){
61570         var r = typeof index == "number" ? this.grid.ds.getAt(index) : index;
61571         return (r && this.selections.key(r.id) ? true : false);
61572     },
61573
61574     /**
61575      * Returns True if the specified record id is selected.
61576      * @param {String} id The id of record to check
61577      * @return {Boolean}
61578      */
61579     isIdSelected : function(id){
61580         return (this.selections.key(id) ? true : false);
61581     },
61582
61583     // private
61584     handleMouseDown : function(e, t)
61585     {
61586         var view = this.grid.view ? this.grid.view : this.grid;
61587         var rowIndex;
61588         if(this.isLocked() || (rowIndex = view.findRowIndex(t)) === false){
61589             return;
61590         };
61591         if(e.shiftKey && this.last !== false){
61592             var last = this.last;
61593             this.selectRange(last, rowIndex, e.ctrlKey);
61594             this.last = last; // reset the last
61595             view.focusRow(rowIndex);
61596         }else{
61597             var isSelected = this.isSelected(rowIndex);
61598             if(e.button !== 0 && isSelected){
61599                 view.focusRow(rowIndex);
61600             }else if(e.ctrlKey && isSelected){
61601                 this.deselectRow(rowIndex);
61602             }else if(!isSelected){
61603                 this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
61604                 view.focusRow(rowIndex);
61605             }
61606         }
61607         this.fireEvent("afterselectionchange", this);
61608     },
61609     // private
61610     handleDragableRowClick :  function(grid, rowIndex, e) 
61611     {
61612         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
61613             this.selectRow(rowIndex, false);
61614             var view = this.grid.view ? this.grid.view : this.grid;
61615             view.focusRow(rowIndex);
61616              this.fireEvent("afterselectionchange", this);
61617         }
61618     },
61619     
61620     /**
61621      * Selects multiple rows.
61622      * @param {Array} rows Array of the indexes of the row to select
61623      * @param {Boolean} keepExisting (optional) True to keep existing selections
61624      */
61625     selectRows : function(rows, keepExisting){
61626         if(!keepExisting){
61627             this.clearSelections();
61628         }
61629         for(var i = 0, len = rows.length; i < len; i++){
61630             this.selectRow(rows[i], true);
61631         }
61632     },
61633
61634     /**
61635      * Selects a range of rows. All rows in between startRow and endRow are also selected.
61636      * @param {Number} startRow The index of the first row in the range
61637      * @param {Number} endRow The index of the last row in the range
61638      * @param {Boolean} keepExisting (optional) True to retain existing selections
61639      */
61640     selectRange : function(startRow, endRow, keepExisting){
61641         if(this.locked) {
61642             return;
61643         }
61644         if(!keepExisting){
61645             this.clearSelections();
61646         }
61647         if(startRow <= endRow){
61648             for(var i = startRow; i <= endRow; i++){
61649                 this.selectRow(i, true);
61650             }
61651         }else{
61652             for(var i = startRow; i >= endRow; i--){
61653                 this.selectRow(i, true);
61654             }
61655         }
61656     },
61657
61658     /**
61659      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
61660      * @param {Number} startRow The index of the first row in the range
61661      * @param {Number} endRow The index of the last row in the range
61662      */
61663     deselectRange : function(startRow, endRow, preventViewNotify){
61664         if(this.locked) {
61665             return;
61666         }
61667         for(var i = startRow; i <= endRow; i++){
61668             this.deselectRow(i, preventViewNotify);
61669         }
61670     },
61671
61672     /**
61673      * Selects a row.
61674      * @param {Number} row The index of the row to select
61675      * @param {Boolean} keepExisting (optional) True to keep existing selections
61676      */
61677     selectRow : function(index, keepExisting, preventViewNotify){
61678         if(this.locked || (index < 0 || index >= this.grid.ds.getCount())) {
61679             return;
61680         }
61681         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
61682             if(!keepExisting || this.singleSelect){
61683                 this.clearSelections();
61684             }
61685             var r = this.grid.ds.getAt(index);
61686             this.selections.add(r);
61687             this.last = this.lastActive = index;
61688             if(!preventViewNotify){
61689                 var view = this.grid.view ? this.grid.view : this.grid;
61690                 view.onRowSelect(index);
61691             }
61692             this.fireEvent("rowselect", this, index, r);
61693             this.fireEvent("selectionchange", this);
61694         }
61695     },
61696
61697     /**
61698      * Deselects a row.
61699      * @param {Number} row The index of the row to deselect
61700      */
61701     deselectRow : function(index, preventViewNotify){
61702         if(this.locked) {
61703             return;
61704         }
61705         if(this.last == index){
61706             this.last = false;
61707         }
61708         if(this.lastActive == index){
61709             this.lastActive = false;
61710         }
61711         var r = this.grid.ds.getAt(index);
61712         this.selections.remove(r);
61713         if(!preventViewNotify){
61714             var view = this.grid.view ? this.grid.view : this.grid;
61715             view.onRowDeselect(index);
61716         }
61717         this.fireEvent("rowdeselect", this, index);
61718         this.fireEvent("selectionchange", this);
61719     },
61720
61721     // private
61722     restoreLast : function(){
61723         if(this._last){
61724             this.last = this._last;
61725         }
61726     },
61727
61728     // private
61729     acceptsNav : function(row, col, cm){
61730         return !cm.isHidden(col) && cm.isCellEditable(col, row);
61731     },
61732
61733     // private
61734     onEditorKey : function(field, e){
61735         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
61736         if(k == e.TAB){
61737             e.stopEvent();
61738             ed.completeEdit();
61739             if(e.shiftKey){
61740                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
61741             }else{
61742                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
61743             }
61744         }else if(k == e.ENTER && !e.ctrlKey){
61745             e.stopEvent();
61746             ed.completeEdit();
61747             if(e.shiftKey){
61748                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
61749             }else{
61750                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
61751             }
61752         }else if(k == e.ESC){
61753             ed.cancelEdit();
61754         }
61755         if(newCell){
61756             g.startEditing(newCell[0], newCell[1]);
61757         }
61758     }
61759 });/*
61760  * Based on:
61761  * Ext JS Library 1.1.1
61762  * Copyright(c) 2006-2007, Ext JS, LLC.
61763  *
61764  * Originally Released Under LGPL - original licence link has changed is not relivant.
61765  *
61766  * Fork - LGPL
61767  * <script type="text/javascript">
61768  */
61769 /**
61770  * @class Roo.grid.CellSelectionModel
61771  * @extends Roo.grid.AbstractSelectionModel
61772  * This class provides the basic implementation for cell selection in a grid.
61773  * @constructor
61774  * @param {Object} config The object containing the configuration of this model.
61775  * @cfg {Boolean} enter_is_tab Enter behaves the same as tab. (eg. goes to next cell) default: false
61776  */
61777 Roo.grid.CellSelectionModel = function(config){
61778     Roo.apply(this, config);
61779
61780     this.selection = null;
61781
61782     this.addEvents({
61783         /**
61784              * @event beforerowselect
61785              * Fires before a cell is selected.
61786              * @param {SelectionModel} this
61787              * @param {Number} rowIndex The selected row index
61788              * @param {Number} colIndex The selected cell index
61789              */
61790             "beforecellselect" : true,
61791         /**
61792              * @event cellselect
61793              * Fires when a cell is selected.
61794              * @param {SelectionModel} this
61795              * @param {Number} rowIndex The selected row index
61796              * @param {Number} colIndex The selected cell index
61797              */
61798             "cellselect" : true,
61799         /**
61800              * @event selectionchange
61801              * Fires when the active selection changes.
61802              * @param {SelectionModel} this
61803              * @param {Object} selection null for no selection or an object (o) with two properties
61804                 <ul>
61805                 <li>o.record: the record object for the row the selection is in</li>
61806                 <li>o.cell: An array of [rowIndex, columnIndex]</li>
61807                 </ul>
61808              */
61809             "selectionchange" : true,
61810         /**
61811              * @event tabend
61812              * Fires when the tab (or enter) was pressed on the last editable cell
61813              * You can use this to trigger add new row.
61814              * @param {SelectionModel} this
61815              */
61816             "tabend" : true,
61817          /**
61818              * @event beforeeditnext
61819              * Fires before the next editable sell is made active
61820              * You can use this to skip to another cell or fire the tabend
61821              *    if you set cell to false
61822              * @param {Object} eventdata object : { cell : [ row, col ] } 
61823              */
61824             "beforeeditnext" : true
61825     });
61826     Roo.grid.CellSelectionModel.superclass.constructor.call(this);
61827 };
61828
61829 Roo.extend(Roo.grid.CellSelectionModel, Roo.grid.AbstractSelectionModel,  {
61830     
61831     enter_is_tab: false,
61832
61833     /** @ignore */
61834     initEvents : function(){
61835         this.grid.on("mousedown", this.handleMouseDown, this);
61836         this.grid.getGridEl().on(Roo.isIE ? "keydown" : "keypress", this.handleKeyDown, this);
61837         var view = this.grid.view;
61838         view.on("refresh", this.onViewChange, this);
61839         view.on("rowupdated", this.onRowUpdated, this);
61840         view.on("beforerowremoved", this.clearSelections, this);
61841         view.on("beforerowsinserted", this.clearSelections, this);
61842         if(this.grid.isEditor){
61843             this.grid.on("beforeedit", this.beforeEdit,  this);
61844         }
61845     },
61846
61847         //private
61848     beforeEdit : function(e){
61849         this.select(e.row, e.column, false, true, e.record);
61850     },
61851
61852         //private
61853     onRowUpdated : function(v, index, r){
61854         if(this.selection && this.selection.record == r){
61855             v.onCellSelect(index, this.selection.cell[1]);
61856         }
61857     },
61858
61859         //private
61860     onViewChange : function(){
61861         this.clearSelections(true);
61862     },
61863
61864         /**
61865          * Returns the currently selected cell,.
61866          * @return {Array} The selected cell (row, column) or null if none selected.
61867          */
61868     getSelectedCell : function(){
61869         return this.selection ? this.selection.cell : null;
61870     },
61871
61872     /**
61873      * Clears all selections.
61874      * @param {Boolean} true to prevent the gridview from being notified about the change.
61875      */
61876     clearSelections : function(preventNotify){
61877         var s = this.selection;
61878         if(s){
61879             if(preventNotify !== true){
61880                 this.grid.view.onCellDeselect(s.cell[0], s.cell[1]);
61881             }
61882             this.selection = null;
61883             this.fireEvent("selectionchange", this, null);
61884         }
61885     },
61886
61887     /**
61888      * Returns true if there is a selection.
61889      * @return {Boolean}
61890      */
61891     hasSelection : function(){
61892         return this.selection ? true : false;
61893     },
61894
61895     /** @ignore */
61896     handleMouseDown : function(e, t){
61897         var v = this.grid.getView();
61898         if(this.isLocked()){
61899             return;
61900         };
61901         var row = v.findRowIndex(t);
61902         var cell = v.findCellIndex(t);
61903         if(row !== false && cell !== false){
61904             this.select(row, cell);
61905         }
61906     },
61907
61908     /**
61909      * Selects a cell.
61910      * @param {Number} rowIndex
61911      * @param {Number} collIndex
61912      */
61913     select : function(rowIndex, colIndex, preventViewNotify, preventFocus, /*internal*/ r){
61914         if(this.fireEvent("beforecellselect", this, rowIndex, colIndex) !== false){
61915             this.clearSelections();
61916             r = r || this.grid.dataSource.getAt(rowIndex);
61917             this.selection = {
61918                 record : r,
61919                 cell : [rowIndex, colIndex]
61920             };
61921             if(!preventViewNotify){
61922                 var v = this.grid.getView();
61923                 v.onCellSelect(rowIndex, colIndex);
61924                 if(preventFocus !== true){
61925                     v.focusCell(rowIndex, colIndex);
61926                 }
61927             }
61928             this.fireEvent("cellselect", this, rowIndex, colIndex);
61929             this.fireEvent("selectionchange", this, this.selection);
61930         }
61931     },
61932
61933         //private
61934     isSelectable : function(rowIndex, colIndex, cm){
61935         return !cm.isHidden(colIndex);
61936     },
61937
61938     /** @ignore */
61939     handleKeyDown : function(e){
61940         //Roo.log('Cell Sel Model handleKeyDown');
61941         if(!e.isNavKeyPress()){
61942             return;
61943         }
61944         var g = this.grid, s = this.selection;
61945         if(!s){
61946             e.stopEvent();
61947             var cell = g.walkCells(0, 0, 1, this.isSelectable,  this);
61948             if(cell){
61949                 this.select(cell[0], cell[1]);
61950             }
61951             return;
61952         }
61953         var sm = this;
61954         var walk = function(row, col, step){
61955             return g.walkCells(row, col, step, sm.isSelectable,  sm);
61956         };
61957         var k = e.getKey(), r = s.cell[0], c = s.cell[1];
61958         var newCell;
61959
61960       
61961
61962         switch(k){
61963             case e.TAB:
61964                 // handled by onEditorKey
61965                 if (g.isEditor && g.editing) {
61966                     return;
61967                 }
61968                 if(e.shiftKey) {
61969                     newCell = walk(r, c-1, -1);
61970                 } else {
61971                     newCell = walk(r, c+1, 1);
61972                 }
61973                 break;
61974             
61975             case e.DOWN:
61976                newCell = walk(r+1, c, 1);
61977                 break;
61978             
61979             case e.UP:
61980                 newCell = walk(r-1, c, -1);
61981                 break;
61982             
61983             case e.RIGHT:
61984                 newCell = walk(r, c+1, 1);
61985                 break;
61986             
61987             case e.LEFT:
61988                 newCell = walk(r, c-1, -1);
61989                 break;
61990             
61991             case e.ENTER:
61992                 
61993                 if(g.isEditor && !g.editing){
61994                    g.startEditing(r, c);
61995                    e.stopEvent();
61996                    return;
61997                 }
61998                 
61999                 
62000              break;
62001         };
62002         if(newCell){
62003             this.select(newCell[0], newCell[1]);
62004             e.stopEvent();
62005             
62006         }
62007     },
62008
62009     acceptsNav : function(row, col, cm){
62010         return !cm.isHidden(col) && cm.isCellEditable(col, row);
62011     },
62012     /**
62013      * Selects a cell.
62014      * @param {Number} field (not used) - as it's normally used as a listener
62015      * @param {Number} e - event - fake it by using
62016      *
62017      * var e = Roo.EventObjectImpl.prototype;
62018      * e.keyCode = e.TAB
62019      *
62020      * 
62021      */
62022     onEditorKey : function(field, e){
62023         
62024         var k = e.getKey(),
62025             newCell,
62026             g = this.grid,
62027             ed = g.activeEditor,
62028             forward = false;
62029         ///Roo.log('onEditorKey' + k);
62030         
62031         
62032         if (this.enter_is_tab && k == e.ENTER) {
62033             k = e.TAB;
62034         }
62035         
62036         if(k == e.TAB){
62037             if(e.shiftKey){
62038                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
62039             }else{
62040                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62041                 forward = true;
62042             }
62043             
62044             e.stopEvent();
62045             
62046         } else if(k == e.ENTER &&  !e.ctrlKey){
62047             ed.completeEdit();
62048             e.stopEvent();
62049             newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
62050         
62051                 } else if(k == e.ESC){
62052             ed.cancelEdit();
62053         }
62054                 
62055         if (newCell) {
62056             var ecall = { cell : newCell, forward : forward };
62057             this.fireEvent('beforeeditnext', ecall );
62058             newCell = ecall.cell;
62059                         forward = ecall.forward;
62060         }
62061                 
62062         if(newCell){
62063             //Roo.log('next cell after edit');
62064             g.startEditing.defer(100, g, [newCell[0], newCell[1]]);
62065         } else if (forward) {
62066             // tabbed past last
62067             this.fireEvent.defer(100, this, ['tabend',this]);
62068         }
62069     }
62070 });/*
62071  * Based on:
62072  * Ext JS Library 1.1.1
62073  * Copyright(c) 2006-2007, Ext JS, LLC.
62074  *
62075  * Originally Released Under LGPL - original licence link has changed is not relivant.
62076  *
62077  * Fork - LGPL
62078  * <script type="text/javascript">
62079  */
62080  
62081 /**
62082  * @class Roo.grid.EditorGrid
62083  * @extends Roo.grid.Grid
62084  * Class for creating and editable grid.
62085  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered - 
62086  * The container MUST have some type of size defined for the grid to fill. The container will be 
62087  * automatically set to position relative if it isn't already.
62088  * @param {Object} dataSource The data model to bind to
62089  * @param {Object} colModel The column model with info about this grid's columns
62090  */
62091 Roo.grid.EditorGrid = function(container, config){
62092     Roo.grid.EditorGrid.superclass.constructor.call(this, container, config);
62093     this.getGridEl().addClass("xedit-grid");
62094
62095     if(!this.selModel){
62096         this.selModel = new Roo.grid.CellSelectionModel();
62097     }
62098
62099     this.activeEditor = null;
62100
62101         this.addEvents({
62102             /**
62103              * @event beforeedit
62104              * Fires before cell editing is triggered. The edit event object has the following properties <br />
62105              * <ul style="padding:5px;padding-left:16px;">
62106              * <li>grid - This grid</li>
62107              * <li>record - The record being edited</li>
62108              * <li>field - The field name being edited</li>
62109              * <li>value - The value for the field being edited.</li>
62110              * <li>row - The grid row index</li>
62111              * <li>column - The grid column index</li>
62112              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62113              * </ul>
62114              * @param {Object} e An edit event (see above for description)
62115              */
62116             "beforeedit" : true,
62117             /**
62118              * @event afteredit
62119              * Fires after a cell is edited. <br />
62120              * <ul style="padding:5px;padding-left:16px;">
62121              * <li>grid - This grid</li>
62122              * <li>record - The record being edited</li>
62123              * <li>field - The field name being edited</li>
62124              * <li>value - The value being set</li>
62125              * <li>originalValue - The original value for the field, before the edit.</li>
62126              * <li>row - The grid row index</li>
62127              * <li>column - The grid column index</li>
62128              * </ul>
62129              * @param {Object} e An edit event (see above for description)
62130              */
62131             "afteredit" : true,
62132             /**
62133              * @event validateedit
62134              * Fires after a cell is edited, but before the value is set in the record. 
62135          * You can use this to modify the value being set in the field, Return false
62136              * to cancel the change. The edit event object has the following properties <br />
62137              * <ul style="padding:5px;padding-left:16px;">
62138          * <li>editor - This editor</li>
62139              * <li>grid - This grid</li>
62140              * <li>record - The record being edited</li>
62141              * <li>field - The field name being edited</li>
62142              * <li>value - The value being set</li>
62143              * <li>originalValue - The original value for the field, before the edit.</li>
62144              * <li>row - The grid row index</li>
62145              * <li>column - The grid column index</li>
62146              * <li>cancel - Set this to true to cancel the edit or return false from your handler.</li>
62147              * </ul>
62148              * @param {Object} e An edit event (see above for description)
62149              */
62150             "validateedit" : true
62151         });
62152     this.on("bodyscroll", this.stopEditing,  this);
62153     this.on(this.clicksToEdit == 1 ? "cellclick" : "celldblclick", this.onCellDblClick,  this);
62154 };
62155
62156 Roo.extend(Roo.grid.EditorGrid, Roo.grid.Grid, {
62157     /**
62158      * @cfg {Number} clicksToEdit
62159      * The number of clicks on a cell required to display the cell's editor (defaults to 2)
62160      */
62161     clicksToEdit: 2,
62162
62163     // private
62164     isEditor : true,
62165     // private
62166     trackMouseOver: false, // causes very odd FF errors
62167
62168     onCellDblClick : function(g, row, col){
62169         this.startEditing(row, col);
62170     },
62171
62172     onEditComplete : function(ed, value, startValue){
62173         this.editing = false;
62174         this.activeEditor = null;
62175         ed.un("specialkey", this.selModel.onEditorKey, this.selModel);
62176         var r = ed.record;
62177         var field = this.colModel.getDataIndex(ed.col);
62178         var e = {
62179             grid: this,
62180             record: r,
62181             field: field,
62182             originalValue: startValue,
62183             value: value,
62184             row: ed.row,
62185             column: ed.col,
62186             cancel:false,
62187             editor: ed
62188         };
62189         var cell = Roo.get(this.view.getCell(ed.row,ed.col));
62190         cell.show();
62191           
62192         if(String(value) !== String(startValue)){
62193             
62194             if(this.fireEvent("validateedit", e) !== false && !e.cancel){
62195                 r.set(field, e.value);
62196                 // if we are dealing with a combo box..
62197                 // then we also set the 'name' colum to be the displayField
62198                 if (ed.field.displayField && ed.field.name) {
62199                     r.set(ed.field.name, ed.field.el.dom.value);
62200                 }
62201                 
62202                 delete e.cancel; //?? why!!!
62203                 this.fireEvent("afteredit", e);
62204             }
62205         } else {
62206             this.fireEvent("afteredit", e); // always fire it!
62207         }
62208         this.view.focusCell(ed.row, ed.col);
62209     },
62210
62211     /**
62212      * Starts editing the specified for the specified row/column
62213      * @param {Number} rowIndex
62214      * @param {Number} colIndex
62215      */
62216     startEditing : function(row, col){
62217         this.stopEditing();
62218         if(this.colModel.isCellEditable(col, row)){
62219             this.view.ensureVisible(row, col, true);
62220           
62221             var r = this.dataSource.getAt(row);
62222             var field = this.colModel.getDataIndex(col);
62223             var cell = Roo.get(this.view.getCell(row,col));
62224             var e = {
62225                 grid: this,
62226                 record: r,
62227                 field: field,
62228                 value: r.data[field],
62229                 row: row,
62230                 column: col,
62231                 cancel:false 
62232             };
62233             if(this.fireEvent("beforeedit", e) !== false && !e.cancel){
62234                 this.editing = true;
62235                 var ed = this.colModel.getCellEditor(col, row);
62236                 
62237                 if (!ed) {
62238                     return;
62239                 }
62240                 if(!ed.rendered){
62241                     ed.render(ed.parentEl || document.body);
62242                 }
62243                 ed.field.reset();
62244                
62245                 cell.hide();
62246                 
62247                 (function(){ // complex but required for focus issues in safari, ie and opera
62248                     ed.row = row;
62249                     ed.col = col;
62250                     ed.record = r;
62251                     ed.on("complete",   this.onEditComplete,        this,       {single: true});
62252                     ed.on("specialkey", this.selModel.onEditorKey,  this.selModel);
62253                     this.activeEditor = ed;
62254                     var v = r.data[field];
62255                     ed.startEdit(this.view.getCell(row, col), v);
62256                     // combo's with 'displayField and name set
62257                     if (ed.field.displayField && ed.field.name) {
62258                         ed.field.el.dom.value = r.data[ed.field.name];
62259                     }
62260                     
62261                     
62262                 }).defer(50, this);
62263             }
62264         }
62265     },
62266         
62267     /**
62268      * Stops any active editing
62269      */
62270     stopEditing : function(){
62271         if(this.activeEditor){
62272             this.activeEditor.completeEdit();
62273         }
62274         this.activeEditor = null;
62275     },
62276         
62277          /**
62278      * Called to get grid's drag proxy text, by default returns this.ddText.
62279      * @return {String}
62280      */
62281     getDragDropText : function(){
62282         var count = this.selModel.getSelectedCell() ? 1 : 0;
62283         return String.format(this.ddText, count, count == 1 ? '' : 's');
62284     }
62285         
62286 });/*
62287  * Based on:
62288  * Ext JS Library 1.1.1
62289  * Copyright(c) 2006-2007, Ext JS, LLC.
62290  *
62291  * Originally Released Under LGPL - original licence link has changed is not relivant.
62292  *
62293  * Fork - LGPL
62294  * <script type="text/javascript">
62295  */
62296
62297 // private - not really -- you end up using it !
62298 // This is a support class used internally by the Grid components
62299
62300 /**
62301  * @class Roo.grid.GridEditor
62302  * @extends Roo.Editor
62303  * Class for creating and editable grid elements.
62304  * @param {Object} config any settings (must include field)
62305  */
62306 Roo.grid.GridEditor = function(field, config){
62307     if (!config && field.field) {
62308         config = field;
62309         field = Roo.factory(config.field, Roo.form);
62310     }
62311     Roo.grid.GridEditor.superclass.constructor.call(this, field, config);
62312     field.monitorTab = false;
62313 };
62314
62315 Roo.extend(Roo.grid.GridEditor, Roo.Editor, {
62316     
62317     /**
62318      * @cfg {Roo.form.Field} field Field to wrap (or xtyped)
62319      */
62320     
62321     alignment: "tl-tl",
62322     autoSize: "width",
62323     hideEl : false,
62324     cls: "x-small-editor x-grid-editor",
62325     shim:false,
62326     shadow:"frame"
62327 });/*
62328  * Based on:
62329  * Ext JS Library 1.1.1
62330  * Copyright(c) 2006-2007, Ext JS, LLC.
62331  *
62332  * Originally Released Under LGPL - original licence link has changed is not relivant.
62333  *
62334  * Fork - LGPL
62335  * <script type="text/javascript">
62336  */
62337   
62338
62339   
62340 Roo.grid.PropertyRecord = Roo.data.Record.create([
62341     {name:'name',type:'string'},  'value'
62342 ]);
62343
62344
62345 Roo.grid.PropertyStore = function(grid, source){
62346     this.grid = grid;
62347     this.store = new Roo.data.Store({
62348         recordType : Roo.grid.PropertyRecord
62349     });
62350     this.store.on('update', this.onUpdate,  this);
62351     if(source){
62352         this.setSource(source);
62353     }
62354     Roo.grid.PropertyStore.superclass.constructor.call(this);
62355 };
62356
62357
62358
62359 Roo.extend(Roo.grid.PropertyStore, Roo.util.Observable, {
62360     setSource : function(o){
62361         this.source = o;
62362         this.store.removeAll();
62363         var data = [];
62364         for(var k in o){
62365             if(this.isEditableValue(o[k])){
62366                 data.push(new Roo.grid.PropertyRecord({name: k, value: o[k]}, k));
62367             }
62368         }
62369         this.store.loadRecords({records: data}, {}, true);
62370     },
62371
62372     onUpdate : function(ds, record, type){
62373         if(type == Roo.data.Record.EDIT){
62374             var v = record.data['value'];
62375             var oldValue = record.modified['value'];
62376             if(this.grid.fireEvent('beforepropertychange', this.source, record.id, v, oldValue) !== false){
62377                 this.source[record.id] = v;
62378                 record.commit();
62379                 this.grid.fireEvent('propertychange', this.source, record.id, v, oldValue);
62380             }else{
62381                 record.reject();
62382             }
62383         }
62384     },
62385
62386     getProperty : function(row){
62387        return this.store.getAt(row);
62388     },
62389
62390     isEditableValue: function(val){
62391         if(val && val instanceof Date){
62392             return true;
62393         }else if(typeof val == 'object' || typeof val == 'function'){
62394             return false;
62395         }
62396         return true;
62397     },
62398
62399     setValue : function(prop, value){
62400         this.source[prop] = value;
62401         this.store.getById(prop).set('value', value);
62402     },
62403
62404     getSource : function(){
62405         return this.source;
62406     }
62407 });
62408
62409 Roo.grid.PropertyColumnModel = function(grid, store){
62410     this.grid = grid;
62411     var g = Roo.grid;
62412     g.PropertyColumnModel.superclass.constructor.call(this, [
62413         {header: this.nameText, sortable: true, dataIndex:'name', id: 'name'},
62414         {header: this.valueText, resizable:false, dataIndex: 'value', id: 'value'}
62415     ]);
62416     this.store = store;
62417     this.bselect = Roo.DomHelper.append(document.body, {
62418         tag: 'select', style:'display:none', cls: 'x-grid-editor', children: [
62419             {tag: 'option', value: 'true', html: 'true'},
62420             {tag: 'option', value: 'false', html: 'false'}
62421         ]
62422     });
62423     Roo.id(this.bselect);
62424     var f = Roo.form;
62425     this.editors = {
62426         'date' : new g.GridEditor(new f.DateField({selectOnFocus:true})),
62427         'string' : new g.GridEditor(new f.TextField({selectOnFocus:true})),
62428         'number' : new g.GridEditor(new f.NumberField({selectOnFocus:true, style:'text-align:left;'})),
62429         'int' : new g.GridEditor(new f.NumberField({selectOnFocus:true, allowDecimals:false, style:'text-align:left;'})),
62430         'boolean' : new g.GridEditor(new f.Field({el:this.bselect,selectOnFocus:true}))
62431     };
62432     this.renderCellDelegate = this.renderCell.createDelegate(this);
62433     this.renderPropDelegate = this.renderProp.createDelegate(this);
62434 };
62435
62436 Roo.extend(Roo.grid.PropertyColumnModel, Roo.grid.ColumnModel, {
62437     
62438     
62439     nameText : 'Name',
62440     valueText : 'Value',
62441     
62442     dateFormat : 'm/j/Y',
62443     
62444     
62445     renderDate : function(dateVal){
62446         return dateVal.dateFormat(this.dateFormat);
62447     },
62448
62449     renderBool : function(bVal){
62450         return bVal ? 'true' : 'false';
62451     },
62452
62453     isCellEditable : function(colIndex, rowIndex){
62454         return colIndex == 1;
62455     },
62456
62457     getRenderer : function(col){
62458         return col == 1 ?
62459             this.renderCellDelegate : this.renderPropDelegate;
62460     },
62461
62462     renderProp : function(v){
62463         return this.getPropertyName(v);
62464     },
62465
62466     renderCell : function(val){
62467         var rv = val;
62468         if(val instanceof Date){
62469             rv = this.renderDate(val);
62470         }else if(typeof val == 'boolean'){
62471             rv = this.renderBool(val);
62472         }
62473         return Roo.util.Format.htmlEncode(rv);
62474     },
62475
62476     getPropertyName : function(name){
62477         var pn = this.grid.propertyNames;
62478         return pn && pn[name] ? pn[name] : name;
62479     },
62480
62481     getCellEditor : function(colIndex, rowIndex){
62482         var p = this.store.getProperty(rowIndex);
62483         var n = p.data['name'], val = p.data['value'];
62484         
62485         if(typeof(this.grid.customEditors[n]) == 'string'){
62486             return this.editors[this.grid.customEditors[n]];
62487         }
62488         if(typeof(this.grid.customEditors[n]) != 'undefined'){
62489             return this.grid.customEditors[n];
62490         }
62491         if(val instanceof Date){
62492             return this.editors['date'];
62493         }else if(typeof val == 'number'){
62494             return this.editors['number'];
62495         }else if(typeof val == 'boolean'){
62496             return this.editors['boolean'];
62497         }else{
62498             return this.editors['string'];
62499         }
62500     }
62501 });
62502
62503 /**
62504  * @class Roo.grid.PropertyGrid
62505  * @extends Roo.grid.EditorGrid
62506  * This class represents the  interface of a component based property grid control.
62507  * <br><br>Usage:<pre><code>
62508  var grid = new Roo.grid.PropertyGrid("my-container-id", {
62509       
62510  });
62511  // set any options
62512  grid.render();
62513  * </code></pre>
62514   
62515  * @constructor
62516  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62517  * The container MUST have some type of size defined for the grid to fill. The container will be
62518  * automatically set to position relative if it isn't already.
62519  * @param {Object} config A config object that sets properties on this grid.
62520  */
62521 Roo.grid.PropertyGrid = function(container, config){
62522     config = config || {};
62523     var store = new Roo.grid.PropertyStore(this);
62524     this.store = store;
62525     var cm = new Roo.grid.PropertyColumnModel(this, store);
62526     store.store.sort('name', 'ASC');
62527     Roo.grid.PropertyGrid.superclass.constructor.call(this, container, Roo.apply({
62528         ds: store.store,
62529         cm: cm,
62530         enableColLock:false,
62531         enableColumnMove:false,
62532         stripeRows:false,
62533         trackMouseOver: false,
62534         clicksToEdit:1
62535     }, config));
62536     this.getGridEl().addClass('x-props-grid');
62537     this.lastEditRow = null;
62538     this.on('columnresize', this.onColumnResize, this);
62539     this.addEvents({
62540          /**
62541              * @event beforepropertychange
62542              * Fires before a property changes (return false to stop?)
62543              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62544              * @param {String} id Record Id
62545              * @param {String} newval New Value
62546          * @param {String} oldval Old Value
62547              */
62548         "beforepropertychange": true,
62549         /**
62550              * @event propertychange
62551              * Fires after a property changes
62552              * @param {Roo.grid.PropertyGrid} grid property grid? (check could be store)
62553              * @param {String} id Record Id
62554              * @param {String} newval New Value
62555          * @param {String} oldval Old Value
62556              */
62557         "propertychange": true
62558     });
62559     this.customEditors = this.customEditors || {};
62560 };
62561 Roo.extend(Roo.grid.PropertyGrid, Roo.grid.EditorGrid, {
62562     
62563      /**
62564      * @cfg {Object} customEditors map of colnames=> custom editors.
62565      * the custom editor can be one of the standard ones (date|string|number|int|boolean), or a
62566      * grid editor eg. Roo.grid.GridEditor(new Roo.form.TextArea({selectOnFocus:true})),
62567      * false disables editing of the field.
62568          */
62569     
62570       /**
62571      * @cfg {Object} propertyNames map of property Names to their displayed value
62572          */
62573     
62574     render : function(){
62575         Roo.grid.PropertyGrid.superclass.render.call(this);
62576         this.autoSize.defer(100, this);
62577     },
62578
62579     autoSize : function(){
62580         Roo.grid.PropertyGrid.superclass.autoSize.call(this);
62581         if(this.view){
62582             this.view.fitColumns();
62583         }
62584     },
62585
62586     onColumnResize : function(){
62587         this.colModel.setColumnWidth(1, this.container.getWidth(true)-this.colModel.getColumnWidth(0));
62588         this.autoSize();
62589     },
62590     /**
62591      * Sets the data for the Grid
62592      * accepts a Key => Value object of all the elements avaiable.
62593      * @param {Object} data  to appear in grid.
62594      */
62595     setSource : function(source){
62596         this.store.setSource(source);
62597         //this.autoSize();
62598     },
62599     /**
62600      * Gets all the data from the grid.
62601      * @return {Object} data  data stored in grid
62602      */
62603     getSource : function(){
62604         return this.store.getSource();
62605     }
62606 });/*
62607   
62608  * Licence LGPL
62609  
62610  */
62611  
62612 /**
62613  * @class Roo.grid.Calendar
62614  * @extends Roo.grid.Grid
62615  * This class extends the Grid to provide a calendar widget
62616  * <br><br>Usage:<pre><code>
62617  var grid = new Roo.grid.Calendar("my-container-id", {
62618      ds: myDataStore,
62619      cm: myColModel,
62620      selModel: mySelectionModel,
62621      autoSizeColumns: true,
62622      monitorWindowResize: false,
62623      trackMouseOver: true
62624      eventstore : real data store..
62625  });
62626  // set any options
62627  grid.render();
62628   
62629   * @constructor
62630  * @param {String/HTMLElement/Roo.Element} container The element into which this grid will be rendered -
62631  * The container MUST have some type of size defined for the grid to fill. The container will be
62632  * automatically set to position relative if it isn't already.
62633  * @param {Object} config A config object that sets properties on this grid.
62634  */
62635 Roo.grid.Calendar = function(container, config){
62636         // initialize the container
62637         this.container = Roo.get(container);
62638         this.container.update("");
62639         this.container.setStyle("overflow", "hidden");
62640     this.container.addClass('x-grid-container');
62641
62642     this.id = this.container.id;
62643
62644     Roo.apply(this, config);
62645     // check and correct shorthanded configs
62646     
62647     var rows = [];
62648     var d =1;
62649     for (var r = 0;r < 6;r++) {
62650         
62651         rows[r]=[];
62652         for (var c =0;c < 7;c++) {
62653             rows[r][c]= '';
62654         }
62655     }
62656     if (this.eventStore) {
62657         this.eventStore= Roo.factory(this.eventStore, Roo.data);
62658         this.eventStore.on('load',this.onLoad, this);
62659         this.eventStore.on('beforeload',this.clearEvents, this);
62660          
62661     }
62662     
62663     this.dataSource = new Roo.data.Store({
62664             proxy: new Roo.data.MemoryProxy(rows),
62665             reader: new Roo.data.ArrayReader({}, [
62666                    'weekday0', 'weekday1', 'weekday2', 'weekday3', 'weekday4', 'weekday5', 'weekday6' ])
62667     });
62668
62669     this.dataSource.load();
62670     this.ds = this.dataSource;
62671     this.ds.xmodule = this.xmodule || false;
62672     
62673     
62674     var cellRender = function(v,x,r)
62675     {
62676         return String.format(
62677             '<div class="fc-day  fc-widget-content"><div>' +
62678                 '<div class="fc-event-container"></div>' +
62679                 '<div class="fc-day-number">{0}</div>'+
62680                 
62681                 '<div class="fc-day-content"><div style="position:relative"></div></div>' +
62682             '</div></div>', v);
62683     
62684     }
62685     
62686     
62687     this.colModel = new Roo.grid.ColumnModel( [
62688         {
62689             xtype: 'ColumnModel',
62690             xns: Roo.grid,
62691             dataIndex : 'weekday0',
62692             header : 'Sunday',
62693             renderer : cellRender
62694         },
62695         {
62696             xtype: 'ColumnModel',
62697             xns: Roo.grid,
62698             dataIndex : 'weekday1',
62699             header : 'Monday',
62700             renderer : cellRender
62701         },
62702         {
62703             xtype: 'ColumnModel',
62704             xns: Roo.grid,
62705             dataIndex : 'weekday2',
62706             header : 'Tuesday',
62707             renderer : cellRender
62708         },
62709         {
62710             xtype: 'ColumnModel',
62711             xns: Roo.grid,
62712             dataIndex : 'weekday3',
62713             header : 'Wednesday',
62714             renderer : cellRender
62715         },
62716         {
62717             xtype: 'ColumnModel',
62718             xns: Roo.grid,
62719             dataIndex : 'weekday4',
62720             header : 'Thursday',
62721             renderer : cellRender
62722         },
62723         {
62724             xtype: 'ColumnModel',
62725             xns: Roo.grid,
62726             dataIndex : 'weekday5',
62727             header : 'Friday',
62728             renderer : cellRender
62729         },
62730         {
62731             xtype: 'ColumnModel',
62732             xns: Roo.grid,
62733             dataIndex : 'weekday6',
62734             header : 'Saturday',
62735             renderer : cellRender
62736         }
62737     ]);
62738     this.cm = this.colModel;
62739     this.cm.xmodule = this.xmodule || false;
62740  
62741         
62742           
62743     //this.selModel = new Roo.grid.CellSelectionModel();
62744     //this.sm = this.selModel;
62745     //this.selModel.init(this);
62746     
62747     
62748     if(this.width){
62749         this.container.setWidth(this.width);
62750     }
62751
62752     if(this.height){
62753         this.container.setHeight(this.height);
62754     }
62755     /** @private */
62756         this.addEvents({
62757         // raw events
62758         /**
62759          * @event click
62760          * The raw click event for the entire grid.
62761          * @param {Roo.EventObject} e
62762          */
62763         "click" : true,
62764         /**
62765          * @event dblclick
62766          * The raw dblclick event for the entire grid.
62767          * @param {Roo.EventObject} e
62768          */
62769         "dblclick" : true,
62770         /**
62771          * @event contextmenu
62772          * The raw contextmenu event for the entire grid.
62773          * @param {Roo.EventObject} e
62774          */
62775         "contextmenu" : true,
62776         /**
62777          * @event mousedown
62778          * The raw mousedown event for the entire grid.
62779          * @param {Roo.EventObject} e
62780          */
62781         "mousedown" : true,
62782         /**
62783          * @event mouseup
62784          * The raw mouseup event for the entire grid.
62785          * @param {Roo.EventObject} e
62786          */
62787         "mouseup" : true,
62788         /**
62789          * @event mouseover
62790          * The raw mouseover event for the entire grid.
62791          * @param {Roo.EventObject} e
62792          */
62793         "mouseover" : true,
62794         /**
62795          * @event mouseout
62796          * The raw mouseout event for the entire grid.
62797          * @param {Roo.EventObject} e
62798          */
62799         "mouseout" : true,
62800         /**
62801          * @event keypress
62802          * The raw keypress event for the entire grid.
62803          * @param {Roo.EventObject} e
62804          */
62805         "keypress" : true,
62806         /**
62807          * @event keydown
62808          * The raw keydown event for the entire grid.
62809          * @param {Roo.EventObject} e
62810          */
62811         "keydown" : true,
62812
62813         // custom events
62814
62815         /**
62816          * @event cellclick
62817          * Fires when a cell is clicked
62818          * @param {Grid} this
62819          * @param {Number} rowIndex
62820          * @param {Number} columnIndex
62821          * @param {Roo.EventObject} e
62822          */
62823         "cellclick" : true,
62824         /**
62825          * @event celldblclick
62826          * Fires when a cell is double clicked
62827          * @param {Grid} this
62828          * @param {Number} rowIndex
62829          * @param {Number} columnIndex
62830          * @param {Roo.EventObject} e
62831          */
62832         "celldblclick" : true,
62833         /**
62834          * @event rowclick
62835          * Fires when a row is clicked
62836          * @param {Grid} this
62837          * @param {Number} rowIndex
62838          * @param {Roo.EventObject} e
62839          */
62840         "rowclick" : true,
62841         /**
62842          * @event rowdblclick
62843          * Fires when a row is double clicked
62844          * @param {Grid} this
62845          * @param {Number} rowIndex
62846          * @param {Roo.EventObject} e
62847          */
62848         "rowdblclick" : true,
62849         /**
62850          * @event headerclick
62851          * Fires when a header is clicked
62852          * @param {Grid} this
62853          * @param {Number} columnIndex
62854          * @param {Roo.EventObject} e
62855          */
62856         "headerclick" : true,
62857         /**
62858          * @event headerdblclick
62859          * Fires when a header cell is double clicked
62860          * @param {Grid} this
62861          * @param {Number} columnIndex
62862          * @param {Roo.EventObject} e
62863          */
62864         "headerdblclick" : true,
62865         /**
62866          * @event rowcontextmenu
62867          * Fires when a row is right clicked
62868          * @param {Grid} this
62869          * @param {Number} rowIndex
62870          * @param {Roo.EventObject} e
62871          */
62872         "rowcontextmenu" : true,
62873         /**
62874          * @event cellcontextmenu
62875          * Fires when a cell is right clicked
62876          * @param {Grid} this
62877          * @param {Number} rowIndex
62878          * @param {Number} cellIndex
62879          * @param {Roo.EventObject} e
62880          */
62881          "cellcontextmenu" : true,
62882         /**
62883          * @event headercontextmenu
62884          * Fires when a header is right clicked
62885          * @param {Grid} this
62886          * @param {Number} columnIndex
62887          * @param {Roo.EventObject} e
62888          */
62889         "headercontextmenu" : true,
62890         /**
62891          * @event bodyscroll
62892          * Fires when the body element is scrolled
62893          * @param {Number} scrollLeft
62894          * @param {Number} scrollTop
62895          */
62896         "bodyscroll" : true,
62897         /**
62898          * @event columnresize
62899          * Fires when the user resizes a column
62900          * @param {Number} columnIndex
62901          * @param {Number} newSize
62902          */
62903         "columnresize" : true,
62904         /**
62905          * @event columnmove
62906          * Fires when the user moves a column
62907          * @param {Number} oldIndex
62908          * @param {Number} newIndex
62909          */
62910         "columnmove" : true,
62911         /**
62912          * @event startdrag
62913          * Fires when row(s) start being dragged
62914          * @param {Grid} this
62915          * @param {Roo.GridDD} dd The drag drop object
62916          * @param {event} e The raw browser event
62917          */
62918         "startdrag" : true,
62919         /**
62920          * @event enddrag
62921          * Fires when a drag operation is complete
62922          * @param {Grid} this
62923          * @param {Roo.GridDD} dd The drag drop object
62924          * @param {event} e The raw browser event
62925          */
62926         "enddrag" : true,
62927         /**
62928          * @event dragdrop
62929          * Fires when dragged row(s) are dropped on a valid DD target
62930          * @param {Grid} this
62931          * @param {Roo.GridDD} dd The drag drop object
62932          * @param {String} targetId The target drag drop object
62933          * @param {event} e The raw browser event
62934          */
62935         "dragdrop" : true,
62936         /**
62937          * @event dragover
62938          * Fires while row(s) are being dragged. "targetId" is the id of the Yahoo.util.DD object the selected rows are being dragged over.
62939          * @param {Grid} this
62940          * @param {Roo.GridDD} dd The drag drop object
62941          * @param {String} targetId The target drag drop object
62942          * @param {event} e The raw browser event
62943          */
62944         "dragover" : true,
62945         /**
62946          * @event dragenter
62947          *  Fires when the dragged row(s) first cross another DD target while being dragged
62948          * @param {Grid} this
62949          * @param {Roo.GridDD} dd The drag drop object
62950          * @param {String} targetId The target drag drop object
62951          * @param {event} e The raw browser event
62952          */
62953         "dragenter" : true,
62954         /**
62955          * @event dragout
62956          * Fires when the dragged row(s) leave another DD target while being dragged
62957          * @param {Grid} this
62958          * @param {Roo.GridDD} dd The drag drop object
62959          * @param {String} targetId The target drag drop object
62960          * @param {event} e The raw browser event
62961          */
62962         "dragout" : true,
62963         /**
62964          * @event rowclass
62965          * Fires when a row is rendered, so you can change add a style to it.
62966          * @param {GridView} gridview   The grid view
62967          * @param {Object} rowcfg   contains record  rowIndex and rowClass - set rowClass to add a style.
62968          */
62969         'rowclass' : true,
62970
62971         /**
62972          * @event render
62973          * Fires when the grid is rendered
62974          * @param {Grid} grid
62975          */
62976         'render' : true,
62977             /**
62978              * @event select
62979              * Fires when a date is selected
62980              * @param {DatePicker} this
62981              * @param {Date} date The selected date
62982              */
62983         'select': true,
62984         /**
62985              * @event monthchange
62986              * Fires when the displayed month changes 
62987              * @param {DatePicker} this
62988              * @param {Date} date The selected month
62989              */
62990         'monthchange': true,
62991         /**
62992              * @event evententer
62993              * Fires when mouse over an event
62994              * @param {Calendar} this
62995              * @param {event} Event
62996              */
62997         'evententer': true,
62998         /**
62999              * @event eventleave
63000              * Fires when the mouse leaves an
63001              * @param {Calendar} this
63002              * @param {event}
63003              */
63004         'eventleave': true,
63005         /**
63006              * @event eventclick
63007              * Fires when the mouse click an
63008              * @param {Calendar} this
63009              * @param {event}
63010              */
63011         'eventclick': true,
63012         /**
63013              * @event eventrender
63014              * Fires before each cell is rendered, so you can modify the contents, like cls / title / qtip
63015              * @param {Calendar} this
63016              * @param {data} data to be modified
63017              */
63018         'eventrender': true
63019         
63020     });
63021
63022     Roo.grid.Grid.superclass.constructor.call(this);
63023     this.on('render', function() {
63024         this.view.el.addClass('x-grid-cal'); 
63025         
63026         (function() { this.setDate(new Date()); }).defer(100,this); //default today..
63027
63028     },this);
63029     
63030     if (!Roo.grid.Calendar.style) {
63031         Roo.grid.Calendar.style = Roo.util.CSS.createStyleSheet({
63032             
63033             
63034             '.x-grid-cal .x-grid-col' :  {
63035                 height: 'auto !important',
63036                 'vertical-align': 'top'
63037             },
63038             '.x-grid-cal  .fc-event-hori' : {
63039                 height: '14px'
63040             }
63041              
63042             
63043         }, Roo.id());
63044     }
63045
63046     
63047     
63048 };
63049 Roo.extend(Roo.grid.Calendar, Roo.grid.Grid, {
63050     /**
63051      * @cfg {Store} eventStore The store that loads events.
63052      */
63053     eventStore : 25,
63054
63055      
63056     activeDate : false,
63057     startDay : 0,
63058     autoWidth : true,
63059     monitorWindowResize : false,
63060
63061     
63062     resizeColumns : function() {
63063         var col = (this.view.el.getWidth() / 7) - 3;
63064         // loop through cols, and setWidth
63065         for(var i =0 ; i < 7 ; i++){
63066             this.cm.setColumnWidth(i, col);
63067         }
63068     },
63069      setDate :function(date) {
63070         
63071         Roo.log('setDate?');
63072         
63073         this.resizeColumns();
63074         var vd = this.activeDate;
63075         this.activeDate = date;
63076 //        if(vd && this.el){
63077 //            var t = date.getTime();
63078 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
63079 //                Roo.log('using add remove');
63080 //                
63081 //                this.fireEvent('monthchange', this, date);
63082 //                
63083 //                this.cells.removeClass("fc-state-highlight");
63084 //                this.cells.each(function(c){
63085 //                   if(c.dateValue == t){
63086 //                       c.addClass("fc-state-highlight");
63087 //                       setTimeout(function(){
63088 //                            try{c.dom.firstChild.focus();}catch(e){}
63089 //                       }, 50);
63090 //                       return false;
63091 //                   }
63092 //                   return true;
63093 //                });
63094 //                return;
63095 //            }
63096 //        }
63097         
63098         var days = date.getDaysInMonth();
63099         
63100         var firstOfMonth = date.getFirstDateOfMonth();
63101         var startingPos = firstOfMonth.getDay()-this.startDay;
63102         
63103         if(startingPos < this.startDay){
63104             startingPos += 7;
63105         }
63106         
63107         var pm = date.add(Date.MONTH, -1);
63108         var prevStart = pm.getDaysInMonth()-startingPos;
63109 //        
63110         
63111         
63112         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63113         
63114         this.textNodes = this.view.el.query('.x-grid-row .x-grid-col .x-grid-cell-text');
63115         //this.cells.addClassOnOver('fc-state-hover');
63116         
63117         var cells = this.cells.elements;
63118         var textEls = this.textNodes;
63119         
63120         //Roo.each(cells, function(cell){
63121         //    cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
63122         //});
63123         
63124         days += startingPos;
63125
63126         // convert everything to numbers so it's fast
63127         var day = 86400000;
63128         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
63129         //Roo.log(d);
63130         //Roo.log(pm);
63131         //Roo.log(prevStart);
63132         
63133         var today = new Date().clearTime().getTime();
63134         var sel = date.clearTime().getTime();
63135         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
63136         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
63137         var ddMatch = this.disabledDatesRE;
63138         var ddText = this.disabledDatesText;
63139         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
63140         var ddaysText = this.disabledDaysText;
63141         var format = this.format;
63142         
63143         var setCellClass = function(cal, cell){
63144             
63145             //Roo.log('set Cell Class');
63146             cell.title = "";
63147             var t = d.getTime();
63148             
63149             //Roo.log(d);
63150             
63151             
63152             cell.dateValue = t;
63153             if(t == today){
63154                 cell.className += " fc-today";
63155                 cell.className += " fc-state-highlight";
63156                 cell.title = cal.todayText;
63157             }
63158             if(t == sel){
63159                 // disable highlight in other month..
63160                 cell.className += " fc-state-highlight";
63161                 
63162             }
63163             // disabling
63164             if(t < min) {
63165                 //cell.className = " fc-state-disabled";
63166                 cell.title = cal.minText;
63167                 return;
63168             }
63169             if(t > max) {
63170                 //cell.className = " fc-state-disabled";
63171                 cell.title = cal.maxText;
63172                 return;
63173             }
63174             if(ddays){
63175                 if(ddays.indexOf(d.getDay()) != -1){
63176                     // cell.title = ddaysText;
63177                    // cell.className = " fc-state-disabled";
63178                 }
63179             }
63180             if(ddMatch && format){
63181                 var fvalue = d.dateFormat(format);
63182                 if(ddMatch.test(fvalue)){
63183                     cell.title = ddText.replace("%0", fvalue);
63184                    cell.className = " fc-state-disabled";
63185                 }
63186             }
63187             
63188             if (!cell.initialClassName) {
63189                 cell.initialClassName = cell.dom.className;
63190             }
63191             
63192             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
63193         };
63194
63195         var i = 0;
63196         
63197         for(; i < startingPos; i++) {
63198             cells[i].dayName =  (++prevStart);
63199             Roo.log(textEls[i]);
63200             d.setDate(d.getDate()+1);
63201             
63202             //cells[i].className = "fc-past fc-other-month";
63203             setCellClass(this, cells[i]);
63204         }
63205         
63206         var intDay = 0;
63207         
63208         for(; i < days; i++){
63209             intDay = i - startingPos + 1;
63210             cells[i].dayName =  (intDay);
63211             d.setDate(d.getDate()+1);
63212             
63213             cells[i].className = ''; // "x-date-active";
63214             setCellClass(this, cells[i]);
63215         }
63216         var extraDays = 0;
63217         
63218         for(; i < 42; i++) {
63219             //textEls[i].innerHTML = (++extraDays);
63220             
63221             d.setDate(d.getDate()+1);
63222             cells[i].dayName = (++extraDays);
63223             cells[i].className = "fc-future fc-other-month";
63224             setCellClass(this, cells[i]);
63225         }
63226         
63227         //this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
63228         
63229         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
63230         
63231         // this will cause all the cells to mis
63232         var rows= [];
63233         var i =0;
63234         for (var r = 0;r < 6;r++) {
63235             for (var c =0;c < 7;c++) {
63236                 this.ds.getAt(r).set('weekday' + c ,cells[i++].dayName );
63237             }    
63238         }
63239         
63240         this.cells = this.view.el.select('.x-grid-row .x-grid-col',true);
63241         for(i=0;i<cells.length;i++) {
63242             
63243             this.cells.elements[i].dayName = cells[i].dayName ;
63244             this.cells.elements[i].className = cells[i].className;
63245             this.cells.elements[i].initialClassName = cells[i].initialClassName ;
63246             this.cells.elements[i].title = cells[i].title ;
63247             this.cells.elements[i].dateValue = cells[i].dateValue ;
63248         }
63249         
63250         
63251         
63252         
63253         //this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
63254         //this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
63255         
63256         ////if(totalRows != 6){
63257             //this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
63258            // this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
63259        // }
63260         
63261         this.fireEvent('monthchange', this, date);
63262         
63263         
63264     },
63265  /**
63266      * Returns the grid's SelectionModel.
63267      * @return {SelectionModel}
63268      */
63269     getSelectionModel : function(){
63270         if(!this.selModel){
63271             this.selModel = new Roo.grid.CellSelectionModel();
63272         }
63273         return this.selModel;
63274     },
63275
63276     load: function() {
63277         this.eventStore.load()
63278         
63279         
63280         
63281     },
63282     
63283     findCell : function(dt) {
63284         dt = dt.clearTime().getTime();
63285         var ret = false;
63286         this.cells.each(function(c){
63287             //Roo.log("check " +c.dateValue + '?=' + dt);
63288             if(c.dateValue == dt){
63289                 ret = c;
63290                 return false;
63291             }
63292             return true;
63293         });
63294         
63295         return ret;
63296     },
63297     
63298     findCells : function(rec) {
63299         var s = rec.data.start_dt.clone().clearTime().getTime();
63300        // Roo.log(s);
63301         var e= rec.data.end_dt.clone().clearTime().getTime();
63302        // Roo.log(e);
63303         var ret = [];
63304         this.cells.each(function(c){
63305              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
63306             
63307             if(c.dateValue > e){
63308                 return ;
63309             }
63310             if(c.dateValue < s){
63311                 return ;
63312             }
63313             ret.push(c);
63314         });
63315         
63316         return ret;    
63317     },
63318     
63319     findBestRow: function(cells)
63320     {
63321         var ret = 0;
63322         
63323         for (var i =0 ; i < cells.length;i++) {
63324             ret  = Math.max(cells[i].rows || 0,ret);
63325         }
63326         return ret;
63327         
63328     },
63329     
63330     
63331     addItem : function(rec)
63332     {
63333         // look for vertical location slot in
63334         var cells = this.findCells(rec);
63335         
63336         rec.row = this.findBestRow(cells);
63337         
63338         // work out the location.
63339         
63340         var crow = false;
63341         var rows = [];
63342         for(var i =0; i < cells.length; i++) {
63343             if (!crow) {
63344                 crow = {
63345                     start : cells[i],
63346                     end :  cells[i]
63347                 };
63348                 continue;
63349             }
63350             if (crow.start.getY() == cells[i].getY()) {
63351                 // on same row.
63352                 crow.end = cells[i];
63353                 continue;
63354             }
63355             // different row.
63356             rows.push(crow);
63357             crow = {
63358                 start: cells[i],
63359                 end : cells[i]
63360             };
63361             
63362         }
63363         
63364         rows.push(crow);
63365         rec.els = [];
63366         rec.rows = rows;
63367         rec.cells = cells;
63368         for (var i = 0; i < cells.length;i++) {
63369             cells[i].rows = Math.max(cells[i].rows || 0 , rec.row + 1 );
63370             
63371         }
63372         
63373         
63374     },
63375     
63376     clearEvents: function() {
63377         
63378         if (!this.eventStore.getCount()) {
63379             return;
63380         }
63381         // reset number of rows in cells.
63382         Roo.each(this.cells.elements, function(c){
63383             c.rows = 0;
63384         });
63385         
63386         this.eventStore.each(function(e) {
63387             this.clearEvent(e);
63388         },this);
63389         
63390     },
63391     
63392     clearEvent : function(ev)
63393     {
63394         if (ev.els) {
63395             Roo.each(ev.els, function(el) {
63396                 el.un('mouseenter' ,this.onEventEnter, this);
63397                 el.un('mouseleave' ,this.onEventLeave, this);
63398                 el.remove();
63399             },this);
63400             ev.els = [];
63401         }
63402     },
63403     
63404     
63405     renderEvent : function(ev,ctr) {
63406         if (!ctr) {
63407              ctr = this.view.el.select('.fc-event-container',true).first();
63408         }
63409         
63410          
63411         this.clearEvent(ev);
63412             //code
63413        
63414         
63415         
63416         ev.els = [];
63417         var cells = ev.cells;
63418         var rows = ev.rows;
63419         this.fireEvent('eventrender', this, ev);
63420         
63421         for(var i =0; i < rows.length; i++) {
63422             
63423             cls = '';
63424             if (i == 0) {
63425                 cls += ' fc-event-start';
63426             }
63427             if ((i+1) == rows.length) {
63428                 cls += ' fc-event-end';
63429             }
63430             
63431             //Roo.log(ev.data);
63432             // how many rows should it span..
63433             var cg = this.eventTmpl.append(ctr,Roo.apply({
63434                 fccls : cls
63435                 
63436             }, ev.data) , true);
63437             
63438             
63439             cg.on('mouseenter' ,this.onEventEnter, this, ev);
63440             cg.on('mouseleave' ,this.onEventLeave, this, ev);
63441             cg.on('click', this.onEventClick, this, ev);
63442             
63443             ev.els.push(cg);
63444             
63445             var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
63446             var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
63447             //Roo.log(cg);
63448              
63449             cg.setXY([sbox.x +2, sbox.y +(ev.row * 20)]);    
63450             cg.setWidth(ebox.right - sbox.x -2);
63451         }
63452     },
63453     
63454     renderEvents: function()
63455     {   
63456         // first make sure there is enough space..
63457         
63458         if (!this.eventTmpl) {
63459             this.eventTmpl = new Roo.Template(
63460                 '<div class="roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable {fccls} {cls}"  style="position: absolute" unselectable="on">' +
63461                     '<div class="fc-event-inner">' +
63462                         '<span class="fc-event-time">{time}</span>' +
63463                         '<span class="fc-event-title" qtip="{qtip}">{title}</span>' +
63464                     '</div>' +
63465                     '<div class="ui-resizable-heandle ui-resizable-e">&nbsp;&nbsp;&nbsp;</div>' +
63466                 '</div>'
63467             );
63468                 
63469         }
63470                
63471         
63472         
63473         this.cells.each(function(c) {
63474             //Roo.log(c.select('.fc-day-content div',true).first());
63475             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, (c.rows || 1) * 20));
63476         });
63477         
63478         var ctr = this.view.el.select('.fc-event-container',true).first();
63479         
63480         var cls;
63481         this.eventStore.each(function(ev){
63482             
63483             this.renderEvent(ev);
63484              
63485              
63486         }, this);
63487         this.view.layout();
63488         
63489     },
63490     
63491     onEventEnter: function (e, el,event,d) {
63492         this.fireEvent('evententer', this, el, event);
63493     },
63494     
63495     onEventLeave: function (e, el,event,d) {
63496         this.fireEvent('eventleave', this, el, event);
63497     },
63498     
63499     onEventClick: function (e, el,event,d) {
63500         this.fireEvent('eventclick', this, el, event);
63501     },
63502     
63503     onMonthChange: function () {
63504         this.store.load();
63505     },
63506     
63507     onLoad: function () {
63508         
63509         //Roo.log('calendar onload');
63510 //         
63511         if(this.eventStore.getCount() > 0){
63512             
63513            
63514             
63515             this.eventStore.each(function(d){
63516                 
63517                 
63518                 // FIXME..
63519                 var add =   d.data;
63520                 if (typeof(add.end_dt) == 'undefined')  {
63521                     Roo.log("Missing End time in calendar data: ");
63522                     Roo.log(d);
63523                     return;
63524                 }
63525                 if (typeof(add.start_dt) == 'undefined')  {
63526                     Roo.log("Missing Start time in calendar data: ");
63527                     Roo.log(d);
63528                     return;
63529                 }
63530                 add.start_dt = typeof(add.start_dt) == 'string' ? Date.parseDate(add.start_dt,'Y-m-d H:i:s') : add.start_dt,
63531                 add.end_dt = typeof(add.end_dt) == 'string' ? Date.parseDate(add.end_dt,'Y-m-d H:i:s') : add.end_dt,
63532                 add.id = add.id || d.id;
63533                 add.title = add.title || '??';
63534                 
63535                 this.addItem(d);
63536                 
63537              
63538             },this);
63539         }
63540         
63541         this.renderEvents();
63542     }
63543     
63544
63545 });
63546 /*
63547  grid : {
63548                 xtype: 'Grid',
63549                 xns: Roo.grid,
63550                 listeners : {
63551                     render : function ()
63552                     {
63553                         _this.grid = this;
63554                         
63555                         if (!this.view.el.hasClass('course-timesheet')) {
63556                             this.view.el.addClass('course-timesheet');
63557                         }
63558                         if (this.tsStyle) {
63559                             this.ds.load({});
63560                             return; 
63561                         }
63562                         Roo.log('width');
63563                         Roo.log(_this.grid.view.el.getWidth());
63564                         
63565                         
63566                         this.tsStyle =  Roo.util.CSS.createStyleSheet({
63567                             '.course-timesheet .x-grid-row' : {
63568                                 height: '80px'
63569                             },
63570                             '.x-grid-row td' : {
63571                                 'vertical-align' : 0
63572                             },
63573                             '.course-edit-link' : {
63574                                 'color' : 'blue',
63575                                 'text-overflow' : 'ellipsis',
63576                                 'overflow' : 'hidden',
63577                                 'white-space' : 'nowrap',
63578                                 'cursor' : 'pointer'
63579                             },
63580                             '.sub-link' : {
63581                                 'color' : 'green'
63582                             },
63583                             '.de-act-sup-link' : {
63584                                 'color' : 'purple',
63585                                 'text-decoration' : 'line-through'
63586                             },
63587                             '.de-act-link' : {
63588                                 'color' : 'red',
63589                                 'text-decoration' : 'line-through'
63590                             },
63591                             '.course-timesheet .course-highlight' : {
63592                                 'border-top-style': 'dashed !important',
63593                                 'border-bottom-bottom': 'dashed !important'
63594                             },
63595                             '.course-timesheet .course-item' : {
63596                                 'font-family'   : 'tahoma, arial, helvetica',
63597                                 'font-size'     : '11px',
63598                                 'overflow'      : 'hidden',
63599                                 'padding-left'  : '10px',
63600                                 'padding-right' : '10px',
63601                                 'padding-top' : '10px' 
63602                             }
63603                             
63604                         }, Roo.id());
63605                                 this.ds.load({});
63606                     }
63607                 },
63608                 autoWidth : true,
63609                 monitorWindowResize : false,
63610                 cellrenderer : function(v,x,r)
63611                 {
63612                     return v;
63613                 },
63614                 sm : {
63615                     xtype: 'CellSelectionModel',
63616                     xns: Roo.grid
63617                 },
63618                 dataSource : {
63619                     xtype: 'Store',
63620                     xns: Roo.data,
63621                     listeners : {
63622                         beforeload : function (_self, options)
63623                         {
63624                             options.params = options.params || {};
63625                             options.params._month = _this.monthField.getValue();
63626                             options.params.limit = 9999;
63627                             options.params['sort'] = 'when_dt';    
63628                             options.params['dir'] = 'ASC';    
63629                             this.proxy.loadResponse = this.loadResponse;
63630                             Roo.log("load?");
63631                             //this.addColumns();
63632                         },
63633                         load : function (_self, records, options)
63634                         {
63635                             _this.grid.view.el.select('.course-edit-link', true).on('click', function() {
63636                                 // if you click on the translation.. you can edit it...
63637                                 var el = Roo.get(this);
63638                                 var id = el.dom.getAttribute('data-id');
63639                                 var d = el.dom.getAttribute('data-date');
63640                                 var t = el.dom.getAttribute('data-time');
63641                                 //var id = this.child('span').dom.textContent;
63642                                 
63643                                 //Roo.log(this);
63644                                 Pman.Dialog.CourseCalendar.show({
63645                                     id : id,
63646                                     when_d : d,
63647                                     when_t : t,
63648                                     productitem_active : id ? 1 : 0
63649                                 }, function() {
63650                                     _this.grid.ds.load({});
63651                                 });
63652                            
63653                            });
63654                            
63655                            _this.panel.fireEvent('resize', [ '', '' ]);
63656                         }
63657                     },
63658                     loadResponse : function(o, success, response){
63659                             // this is overridden on before load..
63660                             
63661                             Roo.log("our code?");       
63662                             //Roo.log(success);
63663                             //Roo.log(response)
63664                             delete this.activeRequest;
63665                             if(!success){
63666                                 this.fireEvent("loadexception", this, o, response);
63667                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63668                                 return;
63669                             }
63670                             var result;
63671                             try {
63672                                 result = o.reader.read(response);
63673                             }catch(e){
63674                                 Roo.log("load exception?");
63675                                 this.fireEvent("loadexception", this, o, response, e);
63676                                 o.request.callback.call(o.request.scope, null, o.request.arg, false);
63677                                 return;
63678                             }
63679                             Roo.log("ready...");        
63680                             // loop through result.records;
63681                             // and set this.tdate[date] = [] << array of records..
63682                             _this.tdata  = {};
63683                             Roo.each(result.records, function(r){
63684                                 //Roo.log(r.data);
63685                                 if(typeof(_this.tdata[r.data.when_dt.format('j')]) == 'undefined'){
63686                                     _this.tdata[r.data.when_dt.format('j')] = [];
63687                                 }
63688                                 _this.tdata[r.data.when_dt.format('j')].push(r.data);
63689                             });
63690                             
63691                             //Roo.log(_this.tdata);
63692                             
63693                             result.records = [];
63694                             result.totalRecords = 6;
63695                     
63696                             // let's generate some duumy records for the rows.
63697                             //var st = _this.dateField.getValue();
63698                             
63699                             // work out monday..
63700                             //st = st.add(Date.DAY, -1 * st.format('w'));
63701                             
63702                             var date = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63703                             
63704                             var firstOfMonth = date.getFirstDayOfMonth();
63705                             var days = date.getDaysInMonth();
63706                             var d = 1;
63707                             var firstAdded = false;
63708                             for (var i = 0; i < result.totalRecords ; i++) {
63709                                 //var d= st.add(Date.DAY, i);
63710                                 var row = {};
63711                                 var added = 0;
63712                                 for(var w = 0 ; w < 7 ; w++){
63713                                     if(!firstAdded && firstOfMonth != w){
63714                                         continue;
63715                                     }
63716                                     if(d > days){
63717                                         continue;
63718                                     }
63719                                     firstAdded = true;
63720                                     var dd = (d > 0 && d < 10) ? "0"+d : d;
63721                                     row['weekday'+w] = String.format(
63722                                                     '<span style="font-size: 16px;"><b>{0}</b></span>'+
63723                                                     '<span class="course-edit-link" style="color:blue;" data-id="0" data-date="{1}"> Add New</span>',
63724                                                     d,
63725                                                     date.format('Y-m-')+dd
63726                                                 );
63727                                     added++;
63728                                     if(typeof(_this.tdata[d]) != 'undefined'){
63729                                         Roo.each(_this.tdata[d], function(r){
63730                                             var is_sub = '';
63731                                             var deactive = '';
63732                                             var id = r.id;
63733                                             var desc = (r.productitem_id_descrip) ? r.productitem_id_descrip : '';
63734                                             if(r.parent_id*1>0){
63735                                                 is_sub = (r.productitem_id_visible*1 < 1) ? 'de-act-sup-link' :'sub-link';
63736                                                 id = r.parent_id;
63737                                             }
63738                                             if(r.productitem_id_visible*1 < 1 && r.parent_id*1 < 1){
63739                                                 deactive = 'de-act-link';
63740                                             }
63741                                             
63742                                             row['weekday'+w] += String.format(
63743                                                     '<br /><span class="course-edit-link {3} {4}" qtip="{5}" data-id="{0}">{2} - {1}</span>',
63744                                                     id, //0
63745                                                     r.product_id_name, //1
63746                                                     r.when_dt.format('h:ia'), //2
63747                                                     is_sub, //3
63748                                                     deactive, //4
63749                                                     desc // 5
63750                                             );
63751                                         });
63752                                     }
63753                                     d++;
63754                                 }
63755                                 
63756                                 // only do this if something added..
63757                                 if(added > 0){ 
63758                                     result.records.push(_this.grid.dataSource.reader.newRow(row));
63759                                 }
63760                                 
63761                                 
63762                                 // push it twice. (second one with an hour..
63763                                 
63764                             }
63765                             //Roo.log(result);
63766                             this.fireEvent("load", this, o, o.request.arg);
63767                             o.request.callback.call(o.request.scope, result, o.request.arg, true);
63768                         },
63769                     sortInfo : {field: 'when_dt', direction : 'ASC' },
63770                     proxy : {
63771                         xtype: 'HttpProxy',
63772                         xns: Roo.data,
63773                         method : 'GET',
63774                         url : baseURL + '/Roo/Shop_course.php'
63775                     },
63776                     reader : {
63777                         xtype: 'JsonReader',
63778                         xns: Roo.data,
63779                         id : 'id',
63780                         fields : [
63781                             {
63782                                 'name': 'id',
63783                                 'type': 'int'
63784                             },
63785                             {
63786                                 'name': 'when_dt',
63787                                 'type': 'string'
63788                             },
63789                             {
63790                                 'name': 'end_dt',
63791                                 'type': 'string'
63792                             },
63793                             {
63794                                 'name': 'parent_id',
63795                                 'type': 'int'
63796                             },
63797                             {
63798                                 'name': 'product_id',
63799                                 'type': 'int'
63800                             },
63801                             {
63802                                 'name': 'productitem_id',
63803                                 'type': 'int'
63804                             },
63805                             {
63806                                 'name': 'guid',
63807                                 'type': 'int'
63808                             }
63809                         ]
63810                     }
63811                 },
63812                 toolbar : {
63813                     xtype: 'Toolbar',
63814                     xns: Roo,
63815                     items : [
63816                         {
63817                             xtype: 'Button',
63818                             xns: Roo.Toolbar,
63819                             listeners : {
63820                                 click : function (_self, e)
63821                                 {
63822                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63823                                     sd.setMonth(sd.getMonth()-1);
63824                                     _this.monthField.setValue(sd.format('Y-m-d'));
63825                                     _this.grid.ds.load({});
63826                                 }
63827                             },
63828                             text : "Back"
63829                         },
63830                         {
63831                             xtype: 'Separator',
63832                             xns: Roo.Toolbar
63833                         },
63834                         {
63835                             xtype: 'MonthField',
63836                             xns: Roo.form,
63837                             listeners : {
63838                                 render : function (_self)
63839                                 {
63840                                     _this.monthField = _self;
63841                                    // _this.monthField.set  today
63842                                 },
63843                                 select : function (combo, date)
63844                                 {
63845                                     _this.grid.ds.load({});
63846                                 }
63847                             },
63848                             value : (function() { return new Date(); })()
63849                         },
63850                         {
63851                             xtype: 'Separator',
63852                             xns: Roo.Toolbar
63853                         },
63854                         {
63855                             xtype: 'TextItem',
63856                             xns: Roo.Toolbar,
63857                             text : "Blue: in-active, green: in-active sup-event, red: de-active, purple: de-active sup-event"
63858                         },
63859                         {
63860                             xtype: 'Fill',
63861                             xns: Roo.Toolbar
63862                         },
63863                         {
63864                             xtype: 'Button',
63865                             xns: Roo.Toolbar,
63866                             listeners : {
63867                                 click : function (_self, e)
63868                                 {
63869                                     var sd = Date.parseDate(_this.monthField.getValue(), "Y-m-d");
63870                                     sd.setMonth(sd.getMonth()+1);
63871                                     _this.monthField.setValue(sd.format('Y-m-d'));
63872                                     _this.grid.ds.load({});
63873                                 }
63874                             },
63875                             text : "Next"
63876                         }
63877                     ]
63878                 },
63879                  
63880             }
63881         };
63882         
63883         *//*
63884  * Based on:
63885  * Ext JS Library 1.1.1
63886  * Copyright(c) 2006-2007, Ext JS, LLC.
63887  *
63888  * Originally Released Under LGPL - original licence link has changed is not relivant.
63889  *
63890  * Fork - LGPL
63891  * <script type="text/javascript">
63892  */
63893  
63894 /**
63895  * @class Roo.LoadMask
63896  * A simple utility class for generically masking elements while loading data.  If the element being masked has
63897  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
63898  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
63899  * element's UpdateManager load indicator and will be destroyed after the initial load.
63900  * @constructor
63901  * Create a new LoadMask
63902  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
63903  * @param {Object} config The config object
63904  */
63905 Roo.LoadMask = function(el, config){
63906     this.el = Roo.get(el);
63907     Roo.apply(this, config);
63908     if(this.store){
63909         this.store.on('beforeload', this.onBeforeLoad, this);
63910         this.store.on('load', this.onLoad, this);
63911         this.store.on('loadexception', this.onLoadException, this);
63912         this.removeMask = false;
63913     }else{
63914         var um = this.el.getUpdateManager();
63915         um.showLoadIndicator = false; // disable the default indicator
63916         um.on('beforeupdate', this.onBeforeLoad, this);
63917         um.on('update', this.onLoad, this);
63918         um.on('failure', this.onLoad, this);
63919         this.removeMask = true;
63920     }
63921 };
63922
63923 Roo.LoadMask.prototype = {
63924     /**
63925      * @cfg {Boolean} removeMask
63926      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
63927      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
63928      */
63929     removeMask : false,
63930     /**
63931      * @cfg {String} msg
63932      * The text to display in a centered loading message box (defaults to 'Loading...')
63933      */
63934     msg : 'Loading...',
63935     /**
63936      * @cfg {String} msgCls
63937      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
63938      */
63939     msgCls : 'x-mask-loading',
63940
63941     /**
63942      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
63943      * @type Boolean
63944      */
63945     disabled: false,
63946
63947     /**
63948      * Disables the mask to prevent it from being displayed
63949      */
63950     disable : function(){
63951        this.disabled = true;
63952     },
63953
63954     /**
63955      * Enables the mask so that it can be displayed
63956      */
63957     enable : function(){
63958         this.disabled = false;
63959     },
63960     
63961     onLoadException : function()
63962     {
63963         Roo.log(arguments);
63964         
63965         if (typeof(arguments[3]) != 'undefined') {
63966             Roo.MessageBox.alert("Error loading",arguments[3]);
63967         } 
63968         /*
63969         try {
63970             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
63971                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
63972             }   
63973         } catch(e) {
63974             
63975         }
63976         */
63977     
63978         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
63979     },
63980     // private
63981     onLoad : function()
63982     {
63983         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
63984     },
63985
63986     // private
63987     onBeforeLoad : function(){
63988         if(!this.disabled){
63989             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
63990         }
63991     },
63992
63993     // private
63994     destroy : function(){
63995         if(this.store){
63996             this.store.un('beforeload', this.onBeforeLoad, this);
63997             this.store.un('load', this.onLoad, this);
63998             this.store.un('loadexception', this.onLoadException, this);
63999         }else{
64000             var um = this.el.getUpdateManager();
64001             um.un('beforeupdate', this.onBeforeLoad, this);
64002             um.un('update', this.onLoad, this);
64003             um.un('failure', this.onLoad, this);
64004         }
64005     }
64006 };/*
64007  * Based on:
64008  * Ext JS Library 1.1.1
64009  * Copyright(c) 2006-2007, Ext JS, LLC.
64010  *
64011  * Originally Released Under LGPL - original licence link has changed is not relivant.
64012  *
64013  * Fork - LGPL
64014  * <script type="text/javascript">
64015  */
64016
64017
64018 /**
64019  * @class Roo.XTemplate
64020  * @extends Roo.Template
64021  * Provides a template that can have nested templates for loops or conditionals. The syntax is:
64022 <pre><code>
64023 var t = new Roo.XTemplate(
64024         '&lt;select name="{name}"&gt;',
64025                 '&lt;tpl for="options"&gt;&lt;option value="{value:trim}"&gt;{text:ellipsis(10)}&lt;/option&gt;&lt;/tpl&gt;',
64026         '&lt;/select&gt;'
64027 );
64028  
64029 // then append, applying the master template values
64030  </code></pre>
64031  *
64032  * Supported features:
64033  *
64034  *  Tags:
64035
64036 <pre><code>
64037       {a_variable} - output encoded.
64038       {a_variable.format:("Y-m-d")} - call a method on the variable
64039       {a_variable:raw} - unencoded output
64040       {a_variable:toFixed(1,2)} - Roo.util.Format."toFixed"
64041       {a_variable:this.method_on_template(...)} - call a method on the template object.
64042  
64043 </code></pre>
64044  *  The tpl tag:
64045 <pre><code>
64046         &lt;tpl for="a_variable or condition.."&gt;&lt;/tpl&gt;
64047         &lt;tpl if="a_variable or condition"&gt;&lt;/tpl&gt;
64048         &lt;tpl exec="some javascript"&gt;&lt;/tpl&gt;
64049         &lt;tpl name="named_template"&gt;&lt;/tpl&gt; (experimental)
64050   
64051         &lt;tpl for="."&gt;&lt;/tpl&gt; - just iterate the property..
64052         &lt;tpl for=".."&gt;&lt;/tpl&gt; - iterates with the parent (probably the template) 
64053 </code></pre>
64054  *      
64055  */
64056 Roo.XTemplate = function()
64057 {
64058     Roo.XTemplate.superclass.constructor.apply(this, arguments);
64059     if (this.html) {
64060         this.compile();
64061     }
64062 };
64063
64064
64065 Roo.extend(Roo.XTemplate, Roo.Template, {
64066
64067     /**
64068      * The various sub templates
64069      */
64070     tpls : false,
64071     /**
64072      *
64073      * basic tag replacing syntax
64074      * WORD:WORD()
64075      *
64076      * // you can fake an object call by doing this
64077      *  x.t:(test,tesT) 
64078      * 
64079      */
64080     re : /\{([\w-\.]+)(?:\:([\w\.]*)(?:\((.*?)?\))?)?\}/g,
64081
64082     /**
64083      * compile the template
64084      *
64085      * This is not recursive, so I'm not sure how nested templates are really going to be handled..
64086      *
64087      */
64088     compile: function()
64089     {
64090         var s = this.html;
64091      
64092         s = ['<tpl>', s, '</tpl>'].join('');
64093     
64094         var re     = /<tpl\b[^>]*>((?:(?=([^<]+))\2|<(?!tpl\b[^>]*>))*?)<\/tpl>/,
64095             nameRe = /^<tpl\b[^>]*?for="(.*?)"/,
64096             ifRe   = /^<tpl\b[^>]*?if="(.*?)"/,
64097             execRe = /^<tpl\b[^>]*?exec="(.*?)"/,
64098             namedRe = /^<tpl\b[^>]*?name="(\w+)"/,  // named templates..
64099             m,
64100             id     = 0,
64101             tpls   = [];
64102     
64103         while(true == !!(m = s.match(re))){
64104             var forMatch   = m[0].match(nameRe),
64105                 ifMatch   = m[0].match(ifRe),
64106                 execMatch   = m[0].match(execRe),
64107                 namedMatch   = m[0].match(namedRe),
64108                 
64109                 exp  = null, 
64110                 fn   = null,
64111                 exec = null,
64112                 name = forMatch && forMatch[1] ? forMatch[1] : '';
64113                 
64114             if (ifMatch) {
64115                 // if - puts fn into test..
64116                 exp = ifMatch && ifMatch[1] ? ifMatch[1] : null;
64117                 if(exp){
64118                    fn = new Function('values', 'parent', 'with(values){ return '+(Roo.util.Format.htmlDecode(exp))+'; }');
64119                 }
64120             }
64121             
64122             if (execMatch) {
64123                 // exec - calls a function... returns empty if true is  returned.
64124                 exp = execMatch && execMatch[1] ? execMatch[1] : null;
64125                 if(exp){
64126                    exec = new Function('values', 'parent', 'with(values){ '+(Roo.util.Format.htmlDecode(exp))+'; }');
64127                 }
64128             }
64129             
64130             
64131             if (name) {
64132                 // for = 
64133                 switch(name){
64134                     case '.':  name = new Function('values', 'parent', 'with(values){ return values; }'); break;
64135                     case '..': name = new Function('values', 'parent', 'with(values){ return parent; }'); break;
64136                     default:   name = new Function('values', 'parent', 'with(values){ return '+name+'; }');
64137                 }
64138             }
64139             var uid = namedMatch ? namedMatch[1] : id;
64140             
64141             
64142             tpls.push({
64143                 id:     namedMatch ? namedMatch[1] : id,
64144                 target: name,
64145                 exec:   exec,
64146                 test:   fn,
64147                 body:   m[1] || ''
64148             });
64149             if (namedMatch) {
64150                 s = s.replace(m[0], '');
64151             } else { 
64152                 s = s.replace(m[0], '{xtpl'+ id + '}');
64153             }
64154             ++id;
64155         }
64156         this.tpls = [];
64157         for(var i = tpls.length-1; i >= 0; --i){
64158             this.compileTpl(tpls[i]);
64159             this.tpls[tpls[i].id] = tpls[i];
64160         }
64161         this.master = tpls[tpls.length-1];
64162         return this;
64163     },
64164     /**
64165      * same as applyTemplate, except it's done to one of the subTemplates
64166      * when using named templates, you can do:
64167      *
64168      * var str = pl.applySubTemplate('your-name', values);
64169      *
64170      * 
64171      * @param {Number} id of the template
64172      * @param {Object} values to apply to template
64173      * @param {Object} parent (normaly the instance of this object)
64174      */
64175     applySubTemplate : function(id, values, parent)
64176     {
64177         
64178         
64179         var t = this.tpls[id];
64180         
64181         
64182         try { 
64183             if(t.test && !t.test.call(this, values, parent)){
64184                 return '';
64185             }
64186         } catch(e) {
64187             Roo.log("Xtemplate.applySubTemplate 'test': Exception thrown");
64188             Roo.log(e.toString());
64189             Roo.log(t.test);
64190             return ''
64191         }
64192         try { 
64193             
64194             if(t.exec && t.exec.call(this, values, parent)){
64195                 return '';
64196             }
64197         } catch(e) {
64198             Roo.log("Xtemplate.applySubTemplate 'exec': Exception thrown");
64199             Roo.log(e.toString());
64200             Roo.log(t.exec);
64201             return ''
64202         }
64203         try {
64204             var vs = t.target ? t.target.call(this, values, parent) : values;
64205             parent = t.target ? values : parent;
64206             if(t.target && vs instanceof Array){
64207                 var buf = [];
64208                 for(var i = 0, len = vs.length; i < len; i++){
64209                     buf[buf.length] = t.compiled.call(this, vs[i], parent);
64210                 }
64211                 return buf.join('');
64212             }
64213             return t.compiled.call(this, vs, parent);
64214         } catch (e) {
64215             Roo.log("Xtemplate.applySubTemplate : Exception thrown");
64216             Roo.log(e.toString());
64217             Roo.log(t.compiled);
64218             return '';
64219         }
64220     },
64221
64222     compileTpl : function(tpl)
64223     {
64224         var fm = Roo.util.Format;
64225         var useF = this.disableFormats !== true;
64226         var sep = Roo.isGecko ? "+" : ",";
64227         var undef = function(str) {
64228             Roo.log("Property not found :"  + str);
64229             return '';
64230         };
64231         
64232         var fn = function(m, name, format, args)
64233         {
64234             //Roo.log(arguments);
64235             args = args ? args.replace(/\\'/g,"'") : args;
64236             //["{TEST:(a,b,c)}", "TEST", "", "a,b,c", 0, "{TEST:(a,b,c)}"]
64237             if (typeof(format) == 'undefined') {
64238                 format= 'htmlEncode';
64239             }
64240             if (format == 'raw' ) {
64241                 format = false;
64242             }
64243             
64244             if(name.substr(0, 4) == 'xtpl'){
64245                 return "'"+ sep +'this.applySubTemplate('+name.substr(4)+', values, parent)'+sep+"'";
64246             }
64247             
64248             // build an array of options to determine if value is undefined..
64249             
64250             // basically get 'xxxx.yyyy' then do
64251             // (typeof(xxxx) == 'undefined' || typeof(xxx.yyyy) == 'undefined') ?
64252             //    (function () { Roo.log("Property not found"); return ''; })() :
64253             //    ......
64254             
64255             var udef_ar = [];
64256             var lookfor = '';
64257             Roo.each(name.split('.'), function(st) {
64258                 lookfor += (lookfor.length ? '.': '') + st;
64259                 udef_ar.push(  "(typeof(" + lookfor + ") == 'undefined')"  );
64260             });
64261             
64262             var udef_st = '((' + udef_ar.join(" || ") +") ? undef('" + name + "') : "; // .. needs )
64263             
64264             
64265             if(format && useF){
64266                 
64267                 args = args ? ',' + args : "";
64268                  
64269                 if(format.substr(0, 5) != "this."){
64270                     format = "fm." + format + '(';
64271                 }else{
64272                     format = 'this.call("'+ format.substr(5) + '", ';
64273                     args = ", values";
64274                 }
64275                 
64276                 return "'"+ sep +   udef_st   +    format + name + args + "))"+sep+"'";
64277             }
64278              
64279             if (args.length) {
64280                 // called with xxyx.yuu:(test,test)
64281                 // change to ()
64282                 return "'"+ sep + udef_st  + name + '(' +  args + "))"+sep+"'";
64283             }
64284             // raw.. - :raw modifier..
64285             return "'"+ sep + udef_st  + name + ")"+sep+"'";
64286             
64287         };
64288         var body;
64289         // branched to use + in gecko and [].join() in others
64290         if(Roo.isGecko){
64291             body = "tpl.compiled = function(values, parent){  with(values) { return '" +
64292                    tpl.body.replace(/(\r\n|\n)/g, '\\n').replace(/'/g, "\\'").replace(this.re, fn) +
64293                     "';};};";
64294         }else{
64295             body = ["tpl.compiled = function(values, parent){  with (values) { return ['"];
64296             body.push(tpl.body.replace(/(\r\n|\n)/g,
64297                             '\\n').replace(/'/g, "\\'").replace(this.re, fn));
64298             body.push("'].join('');};};");
64299             body = body.join('');
64300         }
64301         
64302         Roo.debug && Roo.log(body.replace(/\\n/,'\n'));
64303        
64304         /** eval:var:tpl eval:var:fm eval:var:useF eval:var:undef  */
64305         eval(body);
64306         
64307         return this;
64308     },
64309
64310     applyTemplate : function(values){
64311         return this.master.compiled.call(this, values, {});
64312         //var s = this.subs;
64313     },
64314
64315     apply : function(){
64316         return this.applyTemplate.apply(this, arguments);
64317     }
64318
64319  });
64320
64321 Roo.XTemplate.from = function(el){
64322     el = Roo.getDom(el);
64323     return new Roo.XTemplate(el.value || el.innerHTML);
64324 };